Merge "Add tests for rendering to virtual displays."
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 9b12a02..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
@@ -140,6 +144,12 @@
     chmod 0666 /sys/kernel/tracing/events/block/block_rq_complete/enable
     chmod 0666 /sys/kernel/debug/tracing/events/block/block_rq_complete/enable
 
+    # filemap events for iorapd
+    chmod 0666 /sys/kernel/tracing/events/filemap/mm_filemap_add_to_page_cache/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/filemap/mm_filemap_add_to_page_cache/enable
+    chmod 0666 /sys/kernel/tracing/events/filemap/mm_filemap_delete_from_page_cache/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/filemap/mm_filemap_delete_from_page_cache/enable
+
 # Tracing disabled by default
     write /sys/kernel/debug/tracing/tracing_on 0
     write /sys/kernel/tracing/tracing_on 0
@@ -165,7 +175,7 @@
     chmod 0666 /sys/kernel/tracing/per_cpu/cpu5/trace
     chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu6/trace
     chmod 0666 /sys/kernel/tracing/per_cpu/cpu6/trace
-    chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu6/trace
+    chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu7/trace
     chmod 0666 /sys/kernel/tracing/per_cpu/cpu7/trace
     chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu8/trace
     chmod 0666 /sys/kernel/tracing/per_cpu/cpu8/trace
diff --git a/cmds/atrace/atrace_userdebug.rc b/cmds/atrace/atrace_userdebug.rc
index 5c28c9d..6c86c21 100644
--- a/cmds/atrace/atrace_userdebug.rc
+++ b/cmds/atrace/atrace_userdebug.rc
@@ -5,6 +5,12 @@
 # Access control to these files is now entirely in selinux policy.
 
 on post-fs
+    # On userdebug allow to enable any event via the generic
+    # set_event interface:
+    # echo sched/foo > set_event == echo 1 > events/sched/foo/enable.
+    chmod 0666 /sys/kernel/tracing/set_event
+    chmod 0666 /sys/kernel/debug/tracing/set_event
+
     chmod 0666 /sys/kernel/tracing/events/workqueue/enable
     chmod 0666 /sys/kernel/debug/tracing/events/workqueue/enable
     chmod 0666 /sys/kernel/tracing/events/regulator/enable
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index 9aa1075..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",
@@ -99,8 +97,9 @@
         "utils.cpp",
     ],
     static_libs: [
+        "libincidentcompanion",
         "libdumpsys",
-        "libserviceutils"
+        "libserviceutils",
     ],
 }
 
@@ -158,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 909bdd7..b13478c 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -31,6 +31,13 @@
 
 namespace {
 
+struct DumpstateInfo {
+  public:
+    Dumpstate* ds = nullptr;
+    int32_t calling_uid = -1;
+    std::string calling_package;
+};
+
 static binder::Status exception(uint32_t code, const std::string& msg) {
     MYLOGE("%s (%d) ", msg.c_str(), code);
     return binder::Status::fromExceptionCode(code, String8(msg.c_str()));
@@ -42,15 +49,15 @@
 }
 
 static void* callAndNotify(void* data) {
-    Dumpstate& ds = *static_cast<Dumpstate*>(data);
-    // TODO(111441001): Return status on listener.
-    ds.Run();
-    MYLOGE("Finished Run()\n");
+    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;
 }
 
 class DumpstateToken : public BnDumpstateToken {};
-}
+
+}  // namespace
 
 DumpstateService::DumpstateService() : ds_(Dumpstate::GetInstance()) {
 }
@@ -98,11 +105,13 @@
     return binder::Status::ok();
 }
 
-binder::Status DumpstateService::startBugreport(const android::base::unique_fd& /* bugreportFd */,
-                                                const android::base::unique_fd& /* screenshotFd */,
+// 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,
+                                                const android::base::unique_fd& screenshot_fd,
                                                 int bugreport_mode,
-                                                const sp<IDumpstateListener>& /* listener */) {
-    // TODO(b/111441001): Pass in fds & other arguments to DumpOptions.
+                                                const sp<IDumpstateListener>& listener) {
     MYLOGI("startBugreport() with mode: %d\n", bugreport_mode);
 
     if (bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_FULL &&
@@ -116,9 +125,25 @@
                          StringPrintf("Invalid bugreport mode: %d", bugreport_mode));
     }
 
+    if (bugreport_fd.get() == -1 || screenshot_fd.get() == -1) {
+        return exception(binder::Status::EX_ILLEGAL_ARGUMENT, "Invalid file descriptor");
+    }
+
     std::unique_ptr<Dumpstate::DumpOptions> options = std::make_unique<Dumpstate::DumpOptions>();
-    options->Initialize(static_cast<Dumpstate::BugreportMode>(bugreport_mode));
+    options->Initialize(static_cast<Dumpstate::BugreportMode>(bugreport_mode), bugreport_fd,
+                        screenshot_fd);
+
+    std::lock_guard<std::mutex> lock(lock_);
+    // TODO(b/111441001): Disallow multiple simultaneous bugreports.
     ds_.SetOptions(std::move(options));
+    if (listener != nullptr) {
+        ds_.listener_ = listener;
+    }
+
+    DumpstateInfo ds_info;
+    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_);
@@ -128,7 +153,18 @@
     return binder::Status::ok();
 }
 
+binder::Status DumpstateService::cancelBugreport() {
+    // This is a no-op since the cancellation is done from java side via setting sys properties.
+    // See BugreportManagerServiceImpl.
+    // TODO(b/111441001): maybe make native and java sides use different binder interface
+    // to avoid these annoyances.
+    return binder::Status::ok();
+}
+
 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");
@@ -139,8 +175,7 @@
     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", ds_.bugreport_dir_.c_str());
-    dprintf(fd, "bugreport_internal_dir_: %s\n", ds_.bugreport_internal_dir_.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());
diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h
index 1736ae8..faeea53 100644
--- a/cmds/dumpstate/DumpstateService.h
+++ b/cmds/dumpstate/DumpstateService.h
@@ -42,10 +42,14 @@
                                bool getSectionDetails,
                                sp<IDumpstateToken>* returned_token) override;
 
-    binder::Status startBugreport(const android::base::unique_fd& bugreportFd,
-                                  const android::base::unique_fd& screenshotFd, int bugreport_mode,
+    binder::Status startBugreport(int32_t calling_uid, const std::string& calling_package,
+                                  const android::base::unique_fd& bugreport_fd,
+                                  const android::base::unique_fd& screenshot_fd, int bugreport_mode,
                                   const sp<IDumpstateListener>& listener) override;
 
+    // No-op
+    binder::Status cancelBugreport();
+
   private:
     Dumpstate& ds_;
     std::mutex lock_;
diff --git a/cmds/dumpstate/README.md b/cmds/dumpstate/README.md
index 6ac17d8..273a5a6 100644
--- a/cmds/dumpstate/README.md
+++ b/cmds/dumpstate/README.md
@@ -49,7 +49,7 @@
 Then run:
 
 ```
-mmm -j frameworks/native/cmds/dumpstate/ && adb push ${OUT}/data/nativetest64/dumpstate_test* /data/nativetest64 && adb shell /data/nativetest64/dumpstate_test/dumpstate_test
+mmm -j frameworks/native/cmds/dumpstate/ && adb push ${OUT}/data/nativetest64/dumpstate_* /data/nativetest64 && adb shell /data/nativetest64/dumpstate_test/dumpstate_test
 ```
 
 And to run just one test (for example, `DumpstateTest.RunCommandNoArgs`):
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 ba3e290..b1005d3 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
@@ -18,15 +18,12 @@
 
 import android.os.IDumpstateListener;
 import android.os.IDumpstateToken;
-import android.os.DumpstateOptions;
 
 /**
   * Binder interface for the currently running dumpstate process.
   * {@hide}
   */
 interface IDumpstate {
-
-
     // TODO: remove method once startBugReport is used by Shell.
     /*
      * Sets the listener for this dumpstate progress.
@@ -61,14 +58,29 @@
     // Default mode.
     const int BUGREPORT_MODE_DEFAULT = 6;
 
+    // TODO(b/111441001): Should the args be for the consuming application rather than triggering?
     /*
      * Starts a bugreport in the background.
      *
+     *<p>Shows the user a dialog to get consent for sharing the bugreport with the calling
+     * application. If they deny {@link IDumpstateListener#onError} will be called. If they
+     * consent and bugreport generation is successful artifacts will be copied to the given fds and
+     * {@link IDumpstateListener#onFinished} will be called. If there
+     * are errors in bugreport generation {@link IDumpstateListener#onError} will be called.
+     *
+     * @param callingUid UID of the original application that requested the report.
+     * @param callingPackage package of the original application that requested the report.
      * @param bugreportFd the file to which the zipped bugreport should be written
      * @param screenshotFd the file to which screenshot should be written; optional
      * @param bugreportMode the mode that specifies other run time options; must be one of above
      * @param listener callback for updates; optional
      */
-     void startBugreport(FileDescriptor bugreportFd, FileDescriptor screenshotFd, int bugreportMode,
-                         IDumpstateListener listener);
+    void startBugreport(int callingUid, @utf8InCpp String callingPackage,
+                        FileDescriptor bugreportFd, FileDescriptor screenshotFd,
+                        int bugreportMode, IDumpstateListener listener);
+
+    /*
+     * Cancels the bugreport currently in progress.
+     */
+    void cancelBugreport();
 }
diff --git a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
index 030d69d..907a67c 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
@@ -22,18 +22,49 @@
   * {@hide}
   */
 interface IDumpstateListener {
+    /**
+     * Called when there is a progress update.
+     *
+     * @param progress the progress in [0, 100]
+     */
+    oneway void onProgress(int progress);
+
+    /* Options specified are invalid or incompatible */
+    const int BUGREPORT_ERROR_INVALID_INPUT = 1;
+
+    /* Bugreport encountered a runtime error */
+    const int BUGREPORT_ERROR_RUNTIME_ERROR = 2;
+
+    /* User denied consent to share the bugreport with the specified app */
+    const int BUGREPORT_ERROR_USER_DENIED_CONSENT = 3;
+
+    /* The request to get user consent timed out */
+    const int BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT = 4;
+
+    /**
+     * Called on an error condition with one of the error codes listed above.
+     */
+    oneway void onError(int errorCode);
+
+    /**
+     * Called when taking bugreport finishes successfully.
+     */
+     oneway void onFinished();
+
+    // TODO(b/111441001): Remove old methods when not used anymore.
     void onProgressUpdated(int progress);
     void onMaxProgressUpdated(int maxProgress);
 
     /**
-    * Called after every section is complete.
-    * @param  name          section name
-    * @param  status        values from status_t
-    *                       {@code OK} section completed successfully
-    *                       {@code TIMEOUT} dump timed out
-    *                       {@code != OK} error
-    * @param  size          size in bytes, may be invalid if status != OK
-    * @param  durationMs    duration in ms
-    */
+     * Called after every section is complete.
+     *
+     * @param  name          section name
+     * @param  status        values from status_t
+     *                       {@code OK} section completed successfully
+     *                       {@code TIMEOUT} dump timed out
+     *                       {@code != OK} error
+     * @param  size          size in bytes, may be invalid if status != OK
+     * @param  durationMs    duration in ms
+     */
     void onSectionComplete(@utf8InCpp String name, int status, int size, int durationMs);
 }
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 47c4f62..49d8fe9 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -51,6 +51,7 @@
 #include <android-base/unique_fd.h>
 #include <android/hardware/dumpstate/1.0/IDumpstateDevice.h>
 #include <android/hidl/manager/1.0/IServiceManager.h>
+#include <android/os/IIncidentCompanion.h>
 #include <cutils/native_handle.h>
 #include <cutils/properties.h>
 #include <dumpsys.h>
@@ -83,15 +84,19 @@
 using android::UNKNOWN_ERROR;
 using android::Vector;
 using android::base::StringPrintf;
+using android::os::IDumpstateListener;
 using android::os::dumpstate::CommandOptions;
 using android::os::dumpstate::DumpFileToFd;
 using android::os::dumpstate::DumpstateSectionReporter;
 using android::os::dumpstate::GetPidByName;
 using android::os::dumpstate::PropertiesHelper;
 
+typedef Dumpstate::ConsentCallback::ConsentResult UserConsentResult;
+
 /* read before root is shed */
 static char cmdline_buf[16384] = "(unknown)";
 static const char *dump_traces_path = nullptr;
+static const uint64_t USER_CONSENT_TIMEOUT_MS = 30 * 1000;
 
 // TODO: variables and functions below should be part of dumpstate object
 
@@ -135,10 +140,6 @@
     return fd;
 }
 
-static int OpenForWrite(std::string path) {
-    return Open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
-                S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
-}
 
 static int OpenForRead(std::string path) {
     return Open(path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW);
@@ -169,16 +170,12 @@
     return false;
 }
 
-static bool CopyFileToFile(const std::string& input_file, const std::string& output_file) {
-    if (input_file == output_file) {
-        MYLOGD("Skipping copying bugreport file since the destination is the same (%s)\n",
-               output_file.c_str());
+static bool UnlinkAndLogOnError(const std::string& file) {
+    if (unlink(file.c_str()) != -1) {
+        MYLOGE("Failed to remove file (%s): %s\n", file.c_str(), strerror(errno));
         return false;
     }
-
-    MYLOGD("Going to copy bugreport file (%s) to %s\n", input_file.c_str(), output_file.c_str());
-    android::base::unique_fd out_fd(OpenForWrite(output_file));
-    return CopyFileToFd(input_file, out_fd.get());
+    return true;
 }
 
 }  // namespace
@@ -475,47 +472,6 @@
     return false;
 }
 
-static void dump_systrace() {
-    if (!ds.IsZipping()) {
-        MYLOGD("Not dumping systrace because it's not a zipped bugreport\n");
-        return;
-    }
-    std::string systrace_path = ds.GetPath("-systrace.txt");
-    if (systrace_path.empty()) {
-        MYLOGE("Not dumping systrace because path is empty\n");
-        return;
-    }
-    const char* path = "/sys/kernel/debug/tracing/tracing_on";
-    long int is_tracing;
-    if (read_file_as_long(path, &is_tracing)) {
-        return; // error already logged
-    }
-    if (is_tracing <= 0) {
-        MYLOGD("Skipping systrace because '%s' content is '%ld'\n", path, is_tracing);
-        return;
-    }
-
-    MYLOGD("Running '/system/bin/atrace --async_dump -o %s', which can take several minutes",
-            systrace_path.c_str());
-    if (RunCommand("SYSTRACE", {"/system/bin/atrace", "--async_dump", "-o", systrace_path},
-                   CommandOptions::WithTimeout(120).Build())) {
-        MYLOGE("systrace timed out, its zip entry will be incomplete\n");
-        // TODO: RunCommand tries to kill the process, but atrace doesn't die
-        // peacefully; ideally, we should call strace to stop itself, but there is no such option
-        // yet (just a --async_stop, which stops and dump
-        // if (RunCommand("SYSTRACE", {"/system/bin/atrace", "--kill"})) {
-        //   MYLOGE("could not stop systrace ");
-        // }
-    }
-    if (!ds.AddZipEntry("systrace.txt", systrace_path)) {
-        MYLOGE("Unable to add systrace file %s to zip file\n", systrace_path.c_str());
-    } else {
-        if (remove(systrace_path.c_str())) {
-            MYLOGE("Error removing systrace file %s: %s", systrace_path.c_str(), strerror(errno));
-        }
-    }
-}
-
 static bool skip_not_stat(const char *path) {
     static const char stat[] = "/stat";
     size_t len = strlen(path);
@@ -713,6 +669,32 @@
     return timeout_ms > MINIMUM_LOGCAT_TIMEOUT_MS ? timeout_ms : MINIMUM_LOGCAT_TIMEOUT_MS;
 }
 
+Dumpstate::ConsentCallback::ConsentCallback() : result_(UNAVAILABLE), start_time_(Nanotime()) {
+}
+
+android::binder::Status Dumpstate::ConsentCallback::onReportApproved() {
+    std::lock_guard<std::mutex> lock(lock_);
+    result_ = APPROVED;
+    MYLOGD("User approved consent to share bugreport\n");
+    return android::binder::Status::ok();
+}
+
+android::binder::Status Dumpstate::ConsentCallback::onReportDenied() {
+    std::lock_guard<std::mutex> lock(lock_);
+    result_ = DENIED;
+    MYLOGW("User denied consent to share bugreport\n");
+    return android::binder::Status::ok();
+}
+
+UserConsentResult Dumpstate::ConsentCallback::getResult() {
+    std::lock_guard<std::mutex> lock(lock_);
+    return result_;
+}
+
+uint64_t Dumpstate::ConsentCallback::getElapsedTimeMs() const {
+    return Nanotime() - start_time_;
+}
+
 void Dumpstate::PrintHeader() const {
     std::string build, fingerprint, radio, bootloader, network;
     char date[80];
@@ -1446,12 +1428,8 @@
 
 /* Dumps state for the default case. Returns true if everything went fine. */
 static bool DumpstateDefault() {
-    // Dumps systrace right away, otherwise it will be filled with unnecessary events.
-    // First try to dump anrd trace if the daemon is running. Otherwise, dump
-    // the raw trace.
-    if (!dump_anrd_trace()) {
-        dump_systrace();
-    }
+    // Try to dump anrd trace if the daemon is running.
+    dump_anrd_trace();
 
     // Invoking the following dumpsys calls before dump_traces() to try and
     // keep the system stats as close to its initial state as possible.
@@ -1720,6 +1698,7 @@
             "progress (requires -o and -B)\n"
             "  -R: take bugreport in remote mode (requires -o, -z, -d and -B, "
             "shouldn't be used with -P)\n"
+            "  -w: start binder service and make it wait for a call to startBugreport\n"
             "  -v: prints the dumpstate header and exit\n");
 }
 
@@ -1851,16 +1830,9 @@
 static void PrepareToWriteToFile() {
     MaybeResolveSymlink(&ds.bugreport_internal_dir_);
 
-    std::string base_name_part1 = "bugreport";
-    if (!ds.options_->use_outfile.empty()) {
-        ds.bugreport_dir_ = dirname(ds.options_->use_outfile.c_str());
-        base_name_part1 = basename(ds.options_->use_outfile.c_str());
-    }
-
     std::string build_id = android::base::GetProperty("ro.build.id", "UNKNOWN_BUILD");
     std::string device_name = android::base::GetProperty("ro.product.name", "UNKNOWN_DEVICE");
-    ds.base_name_ =
-        StringPrintf("%s-%s-%s", base_name_part1.c_str(), device_name.c_str(), build_id.c_str());
+    ds.base_name_ = StringPrintf("bugreport-%s-%s", device_name.c_str(), build_id.c_str());
     if (ds.options_->do_add_date) {
         char date[80];
         strftime(date, sizeof(date), "%Y-%m-%d-%H-%M-%S", localtime(&ds.now_));
@@ -1881,18 +1853,18 @@
     ds.tmp_path_ = ds.GetPath(".tmp");
     ds.log_path_ = ds.GetPath("-dumpstate_log-" + std::to_string(ds.pid_) + ".txt");
 
-    std::string destination = ds.options_->fd != -1 ? StringPrintf("[fd:%d]", ds.options_->fd)
-                                                    : ds.bugreport_dir_.c_str();
+    std::string destination = ds.options_->bugreport_fd.get() != -1
+                                  ? StringPrintf("[fd:%d]", ds.options_->bugreport_fd.get())
+                                  : ds.bugreport_internal_dir_.c_str();
     MYLOGD(
         "Bugreport dir: %s\n"
-        "Internal Bugreport dir: %s\n"
         "Base name: %s\n"
         "Suffix: %s\n"
         "Log path: %s\n"
         "Temporary path: %s\n"
         "Screenshot path: %s\n",
-        destination.c_str(), ds.bugreport_internal_dir_.c_str(), ds.base_name_.c_str(),
-        ds.name_.c_str(), ds.log_path_.c_str(), ds.tmp_path_.c_str(), ds.screenshot_path_.c_str());
+        destination.c_str(), ds.base_name_.c_str(), ds.name_.c_str(), ds.log_path_.c_str(),
+        ds.tmp_path_.c_str(), ds.screenshot_path_.c_str());
 
     if (ds.options_->do_zip_file) {
         ds.path_ = ds.GetPath(".zip");
@@ -1958,19 +1930,6 @@
                     ds.path_ = new_path;
                 }
             }
-            // The zip file lives in an internal directory. Copy it over to output.
-            bool copy_succeeded = false;
-            if (ds.options_->fd != -1) {
-                copy_succeeded = android::os::CopyFileToFd(ds.path_, ds.options_->fd);
-            } else {
-                ds.final_path_ = ds.GetPath(ds.bugreport_dir_, ".zip");
-                copy_succeeded = android::os::CopyFileToFile(ds.path_, ds.final_path_);
-            }
-            if (copy_succeeded) {
-                if (remove(ds.path_.c_str())) {
-                    MYLOGE("remove(%s): %s", ds.path_.c_str(), strerror(errno));
-                }
-            }
         }
     }
     if (do_text_file) {
@@ -1996,8 +1955,8 @@
 /* Broadcasts that we are done with the bugreport */
 static void SendBugreportFinishedBroadcast() {
     // TODO(b/111441001): use callback instead of broadcast.
-    if (!ds.final_path_.empty()) {
-        MYLOGI("Final bugreport path: %s\n", ds.final_path_.c_str());
+    if (!ds.path_.empty()) {
+        MYLOGI("Final bugreport path: %s\n", ds.path_.c_str());
         // clang-format off
 
         std::vector<std::string> am_args = {
@@ -2005,7 +1964,7 @@
              "--ei", "android.intent.extra.ID", std::to_string(ds.id_),
              "--ei", "android.intent.extra.PID", std::to_string(ds.pid_),
              "--ei", "android.intent.extra.MAX", std::to_string(ds.progress_->GetMax()),
-             "--es", "android.intent.extra.BUGREPORT", ds.final_path_,
+             "--es", "android.intent.extra.BUGREPORT", ds.path_,
              "--es", "android.intent.extra.DUMPSTATE_LOG", ds.log_path_
         };
         // clang-format on
@@ -2027,7 +1986,7 @@
         if (ds.options_->is_remote_mode) {
             am_args.push_back("--es");
             am_args.push_back("android.intent.extra.REMOTE_BUGREPORT_HASH");
-            am_args.push_back(SHA256_file_hash(ds.final_path_));
+            am_args.push_back(SHA256_file_hash(ds.path_));
             SendBroadcast("com.android.internal.intent.action.REMOTE_BUGREPORT_FINISHED", am_args);
         } else {
             SendBroadcast("com.android.internal.intent.action.BUGREPORT_FINISHED", am_args);
@@ -2164,22 +2123,24 @@
     MYLOGI("telephony_only: %d\n", options.telephony_only);
     MYLOGI("wifi_only: %d\n", options.wifi_only);
     MYLOGI("do_progress_updates: %d\n", options.do_progress_updates);
-    MYLOGI("fd: %d\n", options.fd);
-    MYLOGI("use_outfile: %s\n", options.use_outfile.c_str());
+    MYLOGI("fd: %d\n", options.bugreport_fd.get());
     MYLOGI("extra_options: %s\n", options.extra_options.c_str());
     MYLOGI("args: %s\n", options.args.c_str());
     MYLOGI("notification_title: %s\n", options.notification_title.c_str());
     MYLOGI("notification_description: %s\n", options.notification_description.c_str());
 }
 
-void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode) {
+void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode,
+                                        const android::base::unique_fd& bugreport_fd_in,
+                                        const android::base::unique_fd& screenshot_fd_in) {
     // In the new API world, date is always added; output is always a zip file.
     // TODO(111441001): remove these options once they are obsolete.
     do_add_date = true;
     do_zip_file = true;
 
-    // STOPSHIP b/111441001: Remove hardcoded output file path; accept fd.
-    use_outfile = "/data/user_de/0/com.android.shell/files/bugreports/bugreport";
+    // Duplicate the fds because the passed in fds don't outlive the binder transaction.
+    bugreport_fd.reset(dup(bugreport_fd_in.get()));
+    screenshot_fd.reset(dup(screenshot_fd_in.get()));
 
     extra_options = ModeToString(bugreport_mode);
     SetOptionsFromMode(bugreport_mode, this);
@@ -2188,12 +2149,14 @@
 Dumpstate::RunStatus Dumpstate::DumpOptions::Initialize(int argc, char* argv[]) {
     RunStatus status = RunStatus::OK;
     int c;
-    while ((c = getopt(argc, argv, "dho:svqzpPBRSV:")) != -1) {
+    while ((c = getopt(argc, argv, "dho:svqzpPBRSV:w")) != -1) {
         switch (c) {
             // clang-format off
             case 'd': do_add_date = true;            break;
             case 'z': do_zip_file = true;            break;
-            case 'o': use_outfile = optarg;          break;
+            // o=use_outfile not supported anymore.
+            // TODO(b/111441001): Remove when all callers have migrated.
+            case 'o': break;
             case 's': use_socket = true;             break;
             case 'S': use_control_socket = true;     break;
             case 'v': show_header_only = true;       break;
@@ -2203,6 +2166,9 @@
             case 'R': is_remote_mode = true;         break;
             case 'B': do_broadcast = true;           break;
             case 'V':                                break;  // compatibility no-op
+            case 'w':
+                // This was already processed
+                break;
             case 'h':
                 status = RunStatus::HELP;
                 break;
@@ -2230,13 +2196,11 @@
 }
 
 bool Dumpstate::DumpOptions::ValidateOptions() const {
-    if (fd != -1 && !do_zip_file) {
+    if (bugreport_fd.get() != -1 && !do_zip_file) {
         return false;
     }
 
-    bool has_out_file_options = !use_outfile.empty() || fd != -1;
-    if ((do_zip_file || do_add_date || do_progress_updates || do_broadcast) &&
-        !has_out_file_options) {
+    if ((do_zip_file || do_add_date || do_progress_updates || do_broadcast) && !OutputToFile()) {
         return false;
     }
 
@@ -2258,6 +2222,32 @@
     options_ = std::move(options);
 }
 
+Dumpstate::RunStatus Dumpstate::Run(int32_t calling_uid, const std::string& calling_package) {
+    Dumpstate::RunStatus status = RunInternal(calling_uid, calling_package);
+    if (listener_ != nullptr) {
+        switch (status) {
+            case Dumpstate::RunStatus::OK:
+                listener_->onFinished();
+                break;
+            case Dumpstate::RunStatus::HELP:
+                break;
+            case Dumpstate::RunStatus::INVALID_INPUT:
+                listener_->onError(IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
+                break;
+            case Dumpstate::RunStatus::ERROR:
+                listener_->onError(IDumpstateListener::BUGREPORT_ERROR_RUNTIME_ERROR);
+                break;
+            case Dumpstate::RunStatus::USER_CONSENT_DENIED:
+                listener_->onError(IDumpstateListener::BUGREPORT_ERROR_USER_DENIED_CONSENT);
+                break;
+            case Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT:
+                listener_->onError(IDumpstateListener::BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
+                break;
+        }
+    }
+    return status;
+}
+
 /*
  * Dumps relevant information to a bugreport based on the given options.
  *
@@ -2276,10 +2266,11 @@
  * If zipping, a bunch of other files and dumps also get added to the zip archive. The log file also
  * gets added to the archive.
  *
- * Bugreports are first generated in a local directory and later copied to the caller's fd or
- * directory.
+ * Bugreports are first generated in a local directory and later copied to the caller's fd if
+ * supplied.
  */
-Dumpstate::RunStatus Dumpstate::Run() {
+Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid,
+                                            const std::string& calling_package) {
     LogDumpOptions(*options_);
     if (!options_->ValidateOptions()) {
         MYLOGE("Invalid options specified\n");
@@ -2317,8 +2308,14 @@
         return RunStatus::OK;
     }
 
+    if (options_->bugreport_fd.get() != -1) {
+        // If the output needs to be copied over to the caller's fd, get user consent.
+        android::String16 package(calling_package.c_str());
+        CheckUserConsent(calling_uid, package);
+    }
+
     // Redirect output if needed
-    bool is_redirecting = !options_->use_socket && !options_->use_outfile.empty();
+    bool is_redirecting = options_->OutputToFile();
 
     // TODO: temporarily set progress until it's part of the Dumpstate constructor
     std::string stats_path =
@@ -2468,11 +2465,31 @@
         TEMP_FAILURE_RETRY(dup2(dup_stdout_fd, fileno(stdout)));
     }
 
-    /* rename or zip the (now complete) .tmp file to its final location */
-    if (!options_->use_outfile.empty()) {
+    // Rename, and/or zip the (now complete) .tmp file within the internal directory.
+    if (options_->OutputToFile()) {
         FinalizeFile();
     }
 
+    // Share the final file with the caller if the user has consented.
+    Dumpstate::RunStatus status = Dumpstate::RunStatus::OK;
+    if (options_->bugreport_fd.get() != -1) {
+        status = CopyBugreportIfUserConsented();
+        if (status != Dumpstate::RunStatus::OK &&
+            status != Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT) {
+            // Do an early return if there were errors. We make an exception for consent
+            // timing out because it's possible the user got distracted. In this case the
+            // 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 */
     if (options_->do_vibrate) {
         for (int i = 0; i < 3; i++) {
@@ -2484,6 +2501,7 @@
     /* tell activity manager we're done */
     if (options_->do_broadcast) {
         SendBugreportFinishedBroadcast();
+        // Note that listener_ is notified in Run();
     }
 
     MYLOGD("Final progress: %d/%d (estimated %d)\n", progress_->Get(), progress_->GetMax(),
@@ -2503,7 +2521,73 @@
     tombstone_data_.clear();
     anr_data_.clear();
 
-    return RunStatus::OK;
+    return (consent_callback_ != nullptr &&
+            consent_callback_->getResult() == UserConsentResult::UNAVAILABLE)
+               ? USER_CONSENT_TIMED_OUT
+               : RunStatus::OK;
+}
+
+void Dumpstate::CheckUserConsent(int32_t calling_uid, const android::String16& calling_package) {
+    consent_callback_ = new ConsentCallback();
+    const String16 incidentcompanion("incidentcompanion");
+    sp<android::IBinder> ics(defaultServiceManager()->getService(incidentcompanion));
+    if (ics != nullptr) {
+        MYLOGD("Checking user consent via incidentcompanion service\n");
+        android::interface_cast<android::os::IIncidentCompanion>(ics)->authorizeReport(
+            calling_uid, calling_package, 0x1 /* FLAG_CONFIRMATION_DIALOG */,
+            consent_callback_.get());
+    } else {
+        MYLOGD("Unable to check user consent; incidentcompanion service unavailable\n");
+    }
+}
+
+void Dumpstate::CleanupFiles() {
+    android::os::UnlinkAndLogOnError(tmp_path_);
+    android::os::UnlinkAndLogOnError(screenshot_path_);
+    android::os::UnlinkAndLogOnError(path_);
+}
+
+Dumpstate::RunStatus Dumpstate::HandleUserConsentDenied() {
+    MYLOGD("User denied consent; deleting files and returning\n");
+    CleanupFiles();
+    return USER_CONSENT_DENIED;
+}
+
+Dumpstate::RunStatus Dumpstate::CopyBugreportIfUserConsented() {
+    // If the caller has asked to copy the bugreport over to their directory, we need explicit
+    // user consent.
+    UserConsentResult consent_result = consent_callback_->getResult();
+    if (consent_result == UserConsentResult::UNAVAILABLE) {
+        // User has not responded yet.
+        uint64_t elapsed_ms = consent_callback_->getElapsedTimeMs();
+        if (elapsed_ms < USER_CONSENT_TIMEOUT_MS) {
+            uint delay_seconds = (USER_CONSENT_TIMEOUT_MS - elapsed_ms) / 1000;
+            MYLOGD("Did not receive user consent yet; going to wait for %d seconds", delay_seconds);
+            sleep(delay_seconds);
+        }
+        consent_result = consent_callback_->getResult();
+    }
+    if (consent_result == UserConsentResult::DENIED) {
+        // User has explicitly denied sharing with the app. To be safe delete the
+        // internal bugreport & tmp files.
+        return HandleUserConsentDenied();
+    }
+    if (consent_result == UserConsentResult::APPROVED) {
+        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) {
+        // consent_result is still UNAVAILABLE. The user has likely not responded yet.
+        // Since we do not have user consent to share the bugreport it does not get
+        // copied over to the calling app but remains in the internal directory from
+        // where the user can manually pull it.
+        return Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT;
+    }
+    // Unknown result; must be a programming error.
+    MYLOGE("Unknown user consent result:%d\n", consent_result);
+    return Dumpstate::RunStatus::ERROR;
 }
 
 /* Main entry point for dumpstate binary. */
@@ -2512,7 +2596,14 @@
     Dumpstate::RunStatus status = options->Initialize(argc, argv);
     if (status == Dumpstate::RunStatus::OK) {
         ds.SetOptions(std::move(options));
-        status = ds.Run();
+        // When directly running dumpstate binary, the output is not expected to be written
+        // to any external file descriptor.
+        assert(ds.options_->bugreport_fd.get() == -1);
+
+        // calling_uid and calling_package are for user consent to share the bugreport with
+        // an app; they are irrelvant here because bugreport is only written to a local
+        // directory, and not shared.
+        status = ds.Run(-1 /* calling_uid */, "" /* calling_package */);
     }
 
     switch (status) {
@@ -2526,9 +2617,10 @@
             ShowUsage();
             exit(1);
         case Dumpstate::RunStatus::ERROR:
-            exit(2);
-        default:
-            fprintf(stderr, "Unknown status: %d\n", status);
+            FALLTHROUGH_INTENDED;
+        case Dumpstate::RunStatus::USER_CONSENT_DENIED:
+            FALLTHROUGH_INTENDED;
+        case Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT:
             exit(2);
     }
 }
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 7ac25e4..7fb2f3b 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -27,6 +27,7 @@
 
 #include <android-base/macros.h>
 #include <android-base/unique_fd.h>
+#include <android/os/BnIncidentAuthListener.h>
 #include <android/os/IDumpstate.h>
 #include <android/os/IDumpstateListener.h>
 #include <utils/StrongPointer.h>
@@ -192,7 +193,7 @@
     friend class DumpstateTest;
 
   public:
-    enum RunStatus { OK, HELP, INVALID_INPUT, ERROR };
+    enum RunStatus { OK, HELP, INVALID_INPUT, ERROR, USER_CONSENT_DENIED, USER_CONSENT_TIMED_OUT };
 
     // The mode under which the bugreport should be run. Each mode encapsulates a few options.
     enum BugreportMode {
@@ -319,7 +320,7 @@
     struct DumpOptions;
 
     /* Main entry point for running a complete bugreport. */
-    RunStatus Run();
+    RunStatus Run(int32_t calling_uid, const std::string& calling_package);
 
     /* Sets runtime options. */
     void SetOptions(std::unique_ptr<DumpOptions> options);
@@ -331,6 +332,7 @@
         bool do_add_date = false;
         bool do_zip_file = false;
         bool do_vibrate = true;
+        // Writes bugreport content to a socket; only flatfile format is supported.
         bool use_socket = false;
         bool use_control_socket = false;
         bool do_fb = false;
@@ -342,11 +344,10 @@
         bool wifi_only = false;
         // Whether progress updates should be published.
         bool do_progress_updates = false;
-        // File descriptor to output zip file. -1 indicates not set. Takes precedence over
-        // use_outfile.
-        int fd = -1;
-        // Partial path to output file.
-        std::string use_outfile;
+        // File descriptor to output zip file.
+        android::base::unique_fd bugreport_fd;
+        // File descriptor to screenshot file.
+        android::base::unique_fd screenshot_fd;
         // TODO: rename to MODE.
         // Extra options passed as system property.
         std::string extra_options;
@@ -360,10 +361,18 @@
         RunStatus Initialize(int argc, char* argv[]);
 
         /* Initializes options from the requested mode. */
-        void Initialize(BugreportMode bugreport_mode);
+        void Initialize(BugreportMode bugreport_mode, const android::base::unique_fd& bugreport_fd,
+                        const android::base::unique_fd& screenshot_fd);
 
         /* Returns true if the options set so far are consistent. */
         bool ValidateOptions() const;
+
+        /* Returns if options specified require writing bugreport to a file */
+        bool OutputToFile() const {
+            // If we are not writing to socket, we will write to a file. If bugreport_fd is
+            // specified, it is preferred. If not bugreport is written to /bugreports.
+            return !use_socket;
+        }
     };
 
     // TODO: initialize fields on constructor
@@ -421,13 +430,6 @@
     // Full path of the temporary file containing the screenshot (when requested).
     std::string screenshot_path_;
 
-    // TODO(b/111441001): remove when obsolete.
-    // Full path of the final zip file inside the caller-specified directory, if available.
-    std::string final_path_;
-
-    // The caller-specified directory, if available.
-    std::string bugreport_dir_;
-
     // Pointer to the zipped file.
     std::unique_ptr<FILE, int (*)(FILE*)> zip_file{nullptr, fclose};
 
@@ -445,10 +447,47 @@
     // List of open ANR dump files.
     std::vector<DumpData> anr_data_;
 
+    // A callback to IncidentCompanion service, which checks user consent for sharing the
+    // bugreport with the calling app. If the user has not responded yet to the dialog it will
+    // be neither confirmed nor denied.
+    class ConsentCallback : public android::os::BnIncidentAuthListener {
+      public:
+        ConsentCallback();
+        android::binder::Status onReportApproved() override;
+        android::binder::Status onReportDenied() override;
+
+        enum ConsentResult { APPROVED, DENIED, UNAVAILABLE };
+
+        ConsentResult getResult();
+
+        // Returns the time since creating this listener
+        uint64_t getElapsedTimeMs() const;
+
+      private:
+        ConsentResult result_;
+        uint64_t start_time_;
+        std::mutex lock_;
+    };
+
   private:
+    RunStatus RunInternal(int32_t calling_uid, const std::string& calling_package);
+
+    void CheckUserConsent(int32_t calling_uid, const android::String16& calling_package);
+
+    // Removes the in progress files output files (tmp file, zip/txt file, screenshot),
+    // but leaves the log file alone.
+    void CleanupFiles();
+
+    RunStatus HandleUserConsentDenied();
+
+    // Copies bugreport artifacts over to the caller's directories provided there is user consent.
+    RunStatus CopyBugreportIfUserConsented();
+
     // Used by GetInstance() only.
     explicit Dumpstate(const std::string& version = VERSION_CURRENT);
 
+    android::sp<ConsentCallback> consent_callback_;
+
     DISALLOW_COPY_AND_ASSIGN(Dumpstate);
 };
 
diff --git a/cmds/dumpstate/dumpstate.rc b/cmds/dumpstate/dumpstate.rc
index 2e72574..14937b8 100644
--- a/cmds/dumpstate/dumpstate.rc
+++ b/cmds/dumpstate/dumpstate.rc
@@ -17,3 +17,9 @@
     class main
     disabled
     oneshot
+
+# bugreportd starts dumpstate binder service and makes it wait for a listener to connect.
+service bugreportd /system/bin/dumpstate -w
+    class main
+    disabled
+    oneshot
diff --git a/cmds/dumpstate/main.cpp b/cmds/dumpstate/main.cpp
index 78aad11..68d3733 100644
--- a/cmds/dumpstate/main.cpp
+++ b/cmds/dumpstate/main.cpp
@@ -14,8 +14,55 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "dumpstate"
+
+#include <binder/IPCThreadState.h>
+
+#include "DumpstateInternal.h"
+#include "DumpstateService.h"
 #include "dumpstate.h"
 
+namespace {
+
+// Returns true if we should start the service and wait for a listener
+// to bind with bugreport options.
+bool ShouldStartServiceAndWait(int argc, char* argv[]) {
+    bool do_wait = false;
+    int c;
+    // Keep flags in sync with Dumpstate::DumpOptions::Initialize.
+    while ((c = getopt(argc, argv, "wdho:svqzpPBRSV:")) != -1 && !do_wait) {
+        switch (c) {
+            case 'w':
+                do_wait = true;
+                break;
+            default:
+                // Ignore all other options
+                break;
+        }
+    }
+
+    // Reset next index used by getopt so getopt can be called called again in Dumpstate::Run to
+    // parse bugreport options.
+    optind = 1;
+    return do_wait;
+}
+
+}  // namespace
+
 int main(int argc, char* argv[]) {
-    return run_main(argc, argv);
+    if (ShouldStartServiceAndWait(argc, argv)) {
+        int ret;
+        if ((ret = android::os::DumpstateService::Start()) != android::OK) {
+            MYLOGE("Unable to start 'dumpstate' service: %d", ret);
+            exit(1);
+        }
+        MYLOGI("'dumpstate' service started and will wait for a call to startBugreport()");
+
+        // Waits forever for an incoming connection.
+        // TODO(b/111441001): should this time out?
+        android::IPCThreadState::self()->joinThreadPool();
+        return 0;
+    } else {
+        return run_main(argc, argv);
+    }
 }
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index 61a5ef5..570c6c9 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -53,6 +53,18 @@
     DumpstateListener(int fd, std::shared_ptr<std::vector<SectionInfo>> sections)
         : outFd_(fd), max_progress_(5000), sections_(sections) {
     }
+    binder::Status onProgress(int32_t progress) override {
+        dprintf(outFd_, "\rIn progress %d", progress);
+        return binder::Status::ok();
+    }
+    binder::Status onError(int32_t error_code) override {
+        dprintf(outFd_, "\rError %d", error_code);
+        return binder::Status::ok();
+    }
+    binder::Status onFinished() override {
+        dprintf(outFd_, "\rFinished");
+        return binder::Status::ok();
+    }
     binder::Status onProgressUpdated(int32_t progress) override {
         dprintf(outFd_, "\rIn progress %d/%d", progress, max_progress_);
         return binder::Status::ok();
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index fcf9371..eb73d41 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -59,6 +59,9 @@
 
 class DumpstateListenerMock : public IDumpstateListener {
   public:
+    MOCK_METHOD1(onProgress, binder::Status(int32_t progress));
+    MOCK_METHOD1(onError, binder::Status(int32_t error_code));
+    MOCK_METHOD0(onFinished, binder::Status());
     MOCK_METHOD1(onProgressUpdated, binder::Status(int32_t progress));
     MOCK_METHOD1(onMaxProgressUpdated, binder::Status(int32_t max_progress));
     MOCK_METHOD4(onSectionComplete, binder::Status(const ::std::string& name, int32_t status,
@@ -169,7 +172,6 @@
 
     EXPECT_FALSE(options_.do_add_date);
     EXPECT_FALSE(options_.do_zip_file);
-    EXPECT_EQ("", options_.use_outfile);
     EXPECT_FALSE(options_.use_socket);
     EXPECT_FALSE(options_.use_control_socket);
     EXPECT_FALSE(options_.show_header_only);
@@ -187,7 +189,6 @@
         const_cast<char*>("-S"),
         const_cast<char*>("-d"),
         const_cast<char*>("-z"),
-        const_cast<char*>("-o abc"),
     };
     // clang-format on
 
@@ -197,7 +198,6 @@
     EXPECT_TRUE(options_.do_add_date);
     EXPECT_TRUE(options_.do_zip_file);
     EXPECT_TRUE(options_.use_control_socket);
-    EXPECT_EQ(" abc", std::string(options_.use_outfile));
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
@@ -224,7 +224,6 @@
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
-    EXPECT_EQ("", options_.use_outfile);
     EXPECT_FALSE(options_.do_add_date);
     EXPECT_FALSE(options_.do_zip_file);
     EXPECT_FALSE(options_.use_control_socket);
@@ -243,7 +242,6 @@
         const_cast<char*>("-p"),
         const_cast<char*>("-B"),
         const_cast<char*>("-z"),
-        const_cast<char*>("-o abc"),
     };
     // clang-format on
     property_set("dumpstate.options", "bugreportfull");
@@ -255,7 +253,6 @@
     EXPECT_TRUE(options_.do_fb);
     EXPECT_TRUE(options_.do_zip_file);
     EXPECT_TRUE(options_.do_broadcast);
-    EXPECT_EQ(" abc", std::string(options_.use_outfile));
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
@@ -275,7 +272,6 @@
         const_cast<char*>("-p"),
         const_cast<char*>("-B"),
         const_cast<char*>("-z"),
-        const_cast<char*>("-o abc"),
     };
     // clang-format on
 
@@ -290,7 +286,6 @@
     EXPECT_TRUE(options_.do_progress_updates);
     EXPECT_TRUE(options_.do_start_service);
     EXPECT_FALSE(options_.do_fb);
-    EXPECT_EQ(" abc", std::string(options_.use_outfile));
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
@@ -308,7 +303,6 @@
         const_cast<char*>("-p"),
         const_cast<char*>("-B"),
         const_cast<char*>("-z"),
-        const_cast<char*>("-o abc"),
     };
     // clang-format on
 
@@ -323,7 +317,6 @@
     EXPECT_TRUE(options_.is_remote_mode);
     EXPECT_FALSE(options_.do_vibrate);
     EXPECT_FALSE(options_.do_fb);
-    EXPECT_EQ(" abc", std::string(options_.use_outfile));
 
     // Other options retain default values
     EXPECT_FALSE(options_.use_control_socket);
@@ -340,7 +333,6 @@
         const_cast<char*>("-p"),
         const_cast<char*>("-B"),
         const_cast<char*>("-z"),
-        const_cast<char*>("-o abc"),
     };
     // clang-format on
 
@@ -355,7 +347,6 @@
     EXPECT_TRUE(options_.do_zip_file);
     EXPECT_TRUE(options_.do_progress_updates);
     EXPECT_TRUE(options_.do_start_service);
-    EXPECT_EQ(" abc", std::string(options_.use_outfile));
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
@@ -373,7 +364,6 @@
         const_cast<char*>("-p"),
         const_cast<char*>("-B"),
         const_cast<char*>("-z"),
-        const_cast<char*>("-o abc"),
     };
     // clang-format on
 
@@ -387,7 +377,6 @@
     EXPECT_TRUE(options_.do_broadcast);
     EXPECT_TRUE(options_.do_zip_file);
     EXPECT_TRUE(options_.telephony_only);
-    EXPECT_EQ(" abc", std::string(options_.use_outfile));
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
@@ -406,7 +395,6 @@
         const_cast<char*>("-p"),
         const_cast<char*>("-B"),
         const_cast<char*>("-z"),
-        const_cast<char*>("-o abc"),
     };
     // clang-format on
 
@@ -420,7 +408,6 @@
     EXPECT_TRUE(options_.do_broadcast);
     EXPECT_TRUE(options_.do_zip_file);
     EXPECT_TRUE(options_.wifi_only);
-    EXPECT_EQ(" abc", std::string(options_.use_outfile));
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
@@ -440,7 +427,6 @@
         const_cast<char*>("-p"),
         const_cast<char*>("-B"),
         const_cast<char*>("-z"),
-        const_cast<char*>("-o abc"),
     };
     // clang-format on
 
@@ -453,7 +439,6 @@
     EXPECT_TRUE(options_.do_fb);
     EXPECT_TRUE(options_.do_zip_file);
     EXPECT_TRUE(options_.do_broadcast);
-    EXPECT_EQ(" abc", std::string(options_.use_outfile));
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
@@ -471,7 +456,6 @@
         const_cast<char*>("dumpstate"),
         const_cast<char*>("-d"),
         const_cast<char*>("-z"),
-        const_cast<char*>("-o abc"),
         const_cast<char*>("-s"),
         const_cast<char*>("-S"),
 
@@ -484,7 +468,6 @@
     EXPECT_TRUE(options_.do_add_date);
     EXPECT_TRUE(options_.do_zip_file);
     // TODO: Maybe we should trim the filename
-    EXPECT_EQ(" abc", std::string(options_.use_outfile));
     EXPECT_TRUE(options_.use_socket);
     EXPECT_TRUE(options_.use_control_socket);
 
@@ -523,7 +506,6 @@
     // Other options retain default values
     EXPECT_FALSE(options_.do_add_date);
     EXPECT_FALSE(options_.do_zip_file);
-    EXPECT_EQ("", options_.use_outfile);
     EXPECT_FALSE(options_.use_socket);
     EXPECT_FALSE(options_.use_control_socket);
 }
@@ -558,15 +540,21 @@
 
 TEST_F(DumpOptionsTest, ValidateOptionsNeedOutfile1) {
     options_.do_zip_file = true;
+    // Writing to socket = !writing to file.
+    options_.use_socket = true;
     EXPECT_FALSE(options_.ValidateOptions());
-    options_.use_outfile = "a/b/c";
+
+    options_.use_socket = false;
     EXPECT_TRUE(options_.ValidateOptions());
 }
 
 TEST_F(DumpOptionsTest, ValidateOptionsNeedOutfile2) {
     options_.do_broadcast = true;
+    // Writing to socket = !writing to file.
+    options_.use_socket = true;
     EXPECT_FALSE(options_.ValidateOptions());
-    options_.use_outfile = "a/b/c";
+
+    options_.use_socket = false;
     EXPECT_TRUE(options_.ValidateOptions());
 }
 
@@ -575,13 +563,11 @@
     EXPECT_FALSE(options_.ValidateOptions());
 
     options_.do_zip_file = true;
-    options_.use_outfile = "a/b/c";  // do_zip_file needs outfile
     EXPECT_TRUE(options_.ValidateOptions());
 }
 
 TEST_F(DumpOptionsTest, ValidateOptionsUpdateProgressNeedsBroadcast) {
     options_.do_progress_updates = true;
-    options_.use_outfile = "a/b/c";  // do_progress_updates needs outfile
     EXPECT_FALSE(options_.ValidateOptions());
 
     options_.do_broadcast = true;
@@ -595,7 +581,6 @@
     options_.do_broadcast = true;
     options_.do_zip_file = true;
     options_.do_add_date = true;
-    options_.use_outfile = "a/b/c";  // do_broadcast needs outfile
     EXPECT_TRUE(options_.ValidateOptions());
 }
 
@@ -652,8 +637,9 @@
         }
 
         if (update_progress) {
-            message += android::base::StringPrintf("Setting progress (%s): %d/%d\n",
-                                                   listener_name.c_str(), progress, max);
+            message += android::base::StringPrintf("Setting progress (%s): %d/%d (%d%%)\n",
+                                                   listener_name.c_str(), progress, max,
+                                                   (100 * progress / max));
         }
 
         return message;
@@ -812,12 +798,14 @@
     SetProgress(0, 30);
 
     EXPECT_CALL(*listener, onProgressUpdated(20));
+    EXPECT_CALL(*listener, onProgress(66));  // 20/30 %
     EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(20).Build()));
     std::string progress_message = GetProgressMessage(ds.listener_name_, 20, 30);
     EXPECT_THAT(out, StrEq("stdout\n"));
     EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
 
     EXPECT_CALL(*listener, onProgressUpdated(30));
+    EXPECT_CALL(*listener, onProgress(100));  // 35/35 %
     EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(10).Build()));
     progress_message = GetProgressMessage(ds.listener_name_, 30, 30);
     EXPECT_THAT(out, StrEq("stdout\n"));
@@ -826,6 +814,7 @@
     // Run a command that will increase maximum timeout.
     EXPECT_CALL(*listener, onProgressUpdated(31));
     EXPECT_CALL(*listener, onMaxProgressUpdated(37));
+    EXPECT_CALL(*listener, onProgress(83));  // 31/37 %
     EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).Build()));
     progress_message = GetProgressMessage(ds.listener_name_, 31, 37, 30);  // 20% increase
     EXPECT_THAT(out, StrEq("stdout\n"));
@@ -834,6 +823,7 @@
     // Make sure command ran while in dry_run is counted.
     SetDryRun(true);
     EXPECT_CALL(*listener, onProgressUpdated(35));
+    EXPECT_CALL(*listener, onProgress(94));  // 35/37 %
     EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(4).Build()));
     progress_message = GetProgressMessage(ds.listener_name_, 35, 37);
     EXPECT_THAT(out, IsEmpty());
@@ -850,6 +840,7 @@
 
     // First update should always be sent.
     EXPECT_CALL(*listener, onProgressUpdated(1));
+    EXPECT_CALL(*listener, onProgress(12));  // 1/12 %
     EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).Build()));
     std::string progress_message = GetProgressMessage(ds.listener_name_, 1, 8);
     EXPECT_THAT(out, StrEq("stdout\n"));
@@ -862,6 +853,7 @@
 
     // Third update should be sent because it reaches threshold (6 - 1 = 5).
     EXPECT_CALL(*listener, onProgressUpdated(6));
+    EXPECT_CALL(*listener, onProgress(75));  // 6/8 %
     EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).Build()));
     progress_message = GetProgressMessage(ds.listener_name_, 6, 8);
     EXPECT_THAT(out, StrEq("stdout\n"));
@@ -1101,6 +1093,7 @@
     SetProgress(0, 30);
 
     EXPECT_CALL(*listener, onProgressUpdated(5));
+    EXPECT_CALL(*listener, onProgress(16));  // 5/30 %
     EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line.txt"));
 
     std::string progress_message =
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp
index 990fd43..528e43d 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -949,16 +949,22 @@
         fsync(control_socket_fd_);
     }
 
+    int percent = 100 * progress / max;
     if (listener_ != nullptr) {
-        if (progress % 100 == 0) {
-            // We don't want to spam logcat, so only log multiples of 100.
-            MYLOGD("Setting progress (%s): %d/%d\n", listener_name_.c_str(), progress, max);
+        if (percent % 5 == 0) {
+            // We don't want to spam logcat, so only log multiples of 5.
+            MYLOGD("Setting progress (%s): %d/%d (%d%%)\n", listener_name_.c_str(), progress, max,
+                   percent);
         } else {
             // stderr is ignored on normal invocations, but useful when calling
             // /system/bin/dumpstate directly for debuggging.
-            fprintf(stderr, "Setting progress (%s): %d/%d\n", listener_name_.c_str(), progress, max);
+            fprintf(stderr, "Setting progress (%s): %d/%d (%d%%)\n", listener_name_.c_str(),
+                    progress, max, percent);
         }
+        // TODO(b/111441001): Remove in favor of onProgress
         listener_->onProgressUpdated(progress);
+
+        listener_->onProgress(percent);
     }
 }
 
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 2e9701f..488070d 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -18,6 +18,8 @@
         "dexopt.cpp",
         "globals.cpp",
         "utils.cpp",
+        "utils_default.cpp",
+        "view_compiler.cpp",
         ":installd_aidl",
     ],
     header_libs: [
@@ -30,6 +32,7 @@
         "libcutils",
         "liblog",
         "liblogwrap",
+        "libprocessgroup",
         "libselinux",
         "libutils",
     ],
@@ -136,10 +139,23 @@
     ],
     clang: true,
 
-    srcs: ["otapreopt_chroot.cpp"],
+    srcs: [
+        "otapreopt_chroot.cpp",
+        "otapreopt_utils.cpp",
+    ],
     shared_libs: [
         "libbase",
         "liblog",
+        "libprotobuf-cpp-full",
+        "libselinux",
+        "libziparchive",
+    ],
+    static_libs: [
+        "libapex",
+        "libapexd",
+        "lib_apex_manifest_proto",
+        "libavb",
+        "libdm",
     ],
 }
 
@@ -168,6 +184,7 @@
         "libbase",
         "libcutils",
         "liblog",
+        "libprocessgroup",
         "libutils",
     ],
 }
@@ -187,7 +204,10 @@
         "dexopt.cpp",
         "globals.cpp",
         "otapreopt.cpp",
+        "otapreopt_utils.cpp",
         "utils.cpp",
+        "utils_default.cpp",
+        "view_compiler.cpp",
     ],
 
     header_libs: ["dex2oat_headers"],
@@ -204,7 +224,28 @@
         "libcutils",
         "liblog",
         "liblogwrap",
+        "libprocessgroup",
         "libselinux",
         "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 81055d8..9169aea 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -41,6 +41,7 @@
 
 #include <android-base/logging.h>
 #include <android-base/properties.h>
+#include <android-base/scopeguard.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
@@ -60,6 +61,7 @@
 #include "installd_deps.h"
 #include "otapreopt_utils.h"
 #include "utils.h"
+#include "view_compiler.h"
 
 #include "CacheTracker.h"
 #include "MatchExtensionGen.h"
@@ -75,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";
 
@@ -783,6 +788,189 @@
     return ok();
 }
 
+static int32_t copy_directory_recursive(const char* from, const char* to) {
+    char *argv[] = {
+        (char*) kCpPath,
+        (char*) "-F", /* delete any existing destination file first (--remove-destination) */
+        (char*) "-p", /* preserve timestamps, ownership, and permissions */
+        (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */
+        (char*) "-P", /* Do not follow symlinks [default] */
+        (char*) "-d", /* don't dereference symlinks */
+        (char*) from,
+        (char*) to
+    };
+
+    LOG(DEBUG) << "Copying " << from << " to " << to;
+    return android_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, true);
+}
+
+binder::Status InstalldNativeService::snapshotAppData(
+        const std::unique_ptr<std::string>& volumeUuid,
+        const std::string& packageName, int32_t user, int32_t storageFlags) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr;
+    const char* package_name = packageName.c_str();
+
+    if (volume_uuid && strcmp(volume_uuid, kTestUuid)) {
+        return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
+                StringPrintf("volumeUuid must be null or \"%s\", got: %s", kTestUuid, volume_uuid));
+    }
+
+    binder::Status res = ok();
+    bool clear_ce_on_exit = false;
+    bool clear_de_on_exit = false;
+
+    auto deleter = [&clear_ce_on_exit, &clear_de_on_exit, &volume_uuid, &user, &package_name] {
+        if (clear_de_on_exit) {
+            auto to = create_data_misc_de_rollback_package_path(volume_uuid, user, package_name);
+            if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) {
+                LOG(WARNING) << "Failed to delete app data snapshot: " << to;
+            }
+        }
+
+        if (clear_ce_on_exit) {
+            auto to = create_data_misc_ce_rollback_package_path(volume_uuid, user, package_name);
+            if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) {
+                LOG(WARNING) << "Failed to delete app data snapshot: " << to;
+            }
+        }
+    };
+
+    auto scope_guard = android::base::make_scope_guard(deleter);
+
+    // The app may not have any data at all, in which case it's OK to skip here.
+    auto from_ce = create_data_user_ce_package_path(volume_uuid, user, package_name);
+    if (access(from_ce.c_str(), F_OK) != 0) {
+        LOG(INFO) << "Missing source " << from_ce;
+        return ok();
+    }
+
+    // ce_data_inode is not needed when FLAG_CLEAR_CACHE_ONLY is set.
+    binder::Status clear_cache_result = clearAppData(volumeUuid, packageName, user,
+            storageFlags | FLAG_CLEAR_CACHE_ONLY, 0);
+    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);
+            clear_de_on_exit = true;
+            return res;
+        }
+    }
+
+    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;
+        }
+    }
+
+    return res;
+}
+
+binder::Status InstalldNativeService::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) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr;
+    const char* package_name = packageName.c_str();
+
+    if (volume_uuid && strcmp(volume_uuid, kTestUuid)) {
+        return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
+                StringPrintf("volumeUuid must be null or \"%s\", got: %s", kTestUuid, volume_uuid));
+    }
+
+    auto from_ce = create_data_misc_ce_rollback_package_path(volume_uuid,
+            user, package_name);
+    auto from_de = create_data_misc_de_rollback_package_path(volume_uuid,
+            user, package_name);
+
+    const bool needs_ce_rollback = (storageFlags & FLAG_STORAGE_CE) &&
+        (access(from_ce.c_str(), F_OK) == 0);
+    const bool needs_de_rollback = (storageFlags & FLAG_STORAGE_DE) &&
+        (access(from_de.c_str(), F_OK) == 0);
+
+    if (!needs_ce_rollback && !needs_de_rollback) {
+        return ok();
+    }
+
+    // We know we're going to rollback one of the CE or DE data, so we clear
+    // application data first. Note that it's possible that we're asked to
+    // restore both CE & DE data but that one of the restores fail. Leaving the
+    // app with no data in those cases is arguably better than leaving the app
+    // with mismatched / stale data.
+    LOG(INFO) << "Clearing app data for " << packageName << " to restore snapshot.";
+    binder::Status res = clearAppData(volumeUuid, packageName, user, storageFlags, ceDataInode);
+    if (!res.isOk()) {
+        return res;
+    }
+
+    if (needs_ce_rollback) {
+        auto to_ce = create_data_user_ce_path(volume_uuid, user);
+        int rc = copy_directory_recursive(from_ce.c_str(), to_ce.c_str());
+        if (rc != 0) {
+            res = error(rc, "Failed copying " + from_ce + " to " + to_ce);
+            return res;
+        }
+    }
+
+    if (needs_de_rollback) {
+        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.
+            res = error(rc, "Failed copying " + from_de + " to " + to_de);
+            return res;
+        }
+    }
+
+    // Finally, restore the SELinux label on the app data.
+    return restoreconAppData(volumeUuid, packageName, user, storageFlags, appId, seInfo);
+}
+
 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,
@@ -807,19 +995,7 @@
         auto to = create_data_app_package_path(to_uuid, data_app_name);
         auto to_parent = create_data_app_path(to_uuid);
 
-        char *argv[] = {
-            (char*) kCpPath,
-            (char*) "-F", /* delete any existing destination file first (--remove-destination) */
-            (char*) "-p", /* preserve timestamps, ownership, and permissions */
-            (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */
-            (char*) "-P", /* Do not follow symlinks [default] */
-            (char*) "-d", /* don't dereference symlinks */
-            (char*) from.c_str(),
-            (char*) to_parent.c_str()
-        };
-
-        LOG(DEBUG) << "Copying " << from << " to " << to;
-        int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, true);
+        int rc = copy_directory_recursive(from.c_str(), to_parent.c_str());
         if (rc != 0) {
             res = error(rc, "Failed copying " + from + " to " + to);
             goto fail;
@@ -847,25 +1023,11 @@
             goto fail;
         }
 
-        char *argv[] = {
-            (char*) kCpPath,
-            (char*) "-F", /* delete any existing destination file first (--remove-destination) */
-            (char*) "-p", /* preserve timestamps, ownership, and permissions */
-            (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */
-            (char*) "-P", /* Do not follow symlinks [default] */
-            (char*) "-d", /* don't dereference symlinks */
-            nullptr,
-            nullptr
-        };
-
         {
             auto from = create_data_user_de_package_path(from_uuid, user, package_name);
             auto to = create_data_user_de_path(to_uuid, user);
-            argv[6] = (char*) from.c_str();
-            argv[7] = (char*) to.c_str();
 
-            LOG(DEBUG) << "Copying " << from << " to " << to;
-            int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, true);
+            int rc = copy_directory_recursive(from.c_str(), to.c_str());
             if (rc != 0) {
                 res = error(rc, "Failed copying " + from + " to " + to);
                 goto fail;
@@ -874,11 +1036,8 @@
         {
             auto from = create_data_user_ce_package_path(from_uuid, user, package_name);
             auto to = create_data_user_ce_path(to_uuid, user);
-            argv[6] = (char*) from.c_str();
-            argv[7] = (char*) to.c_str();
 
-            LOG(DEBUG) << "Copying " << from << " to " << to;
-            int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, true);
+            int rc = copy_directory_recursive(from.c_str(), to.c_str());
             if (rc != 0) {
                 res = error(rc, "Failed copying " + from + " to " + to);
                 goto fail;
@@ -1871,6 +2030,17 @@
     return res ? error(res, error_msg) : ok();
 }
 
+binder::Status InstalldNativeService::compileLayouts(const std::string& apkPath,
+                                                     const std::string& packageName,
+                                                     const std ::string& outDexFile, int uid,
+                                                     bool* _aidl_return) {
+    const char* apk_path = apkPath.c_str();
+    const char* package_name = packageName.c_str();
+    const char* out_dex_file = outDexFile.c_str();
+    *_aidl_return = android::installd::view_compiler(apk_path, package_name, out_dex_file, uid);
+    return *_aidl_return ? ok() : error("viewcompiler failed");
+}
+
 binder::Status InstalldNativeService::markBootComplete(const std::string& instructionSet) {
     ENFORCE_UID(AID_SYSTEM);
     std::lock_guard<std::recursive_mutex> lock(mLock);
@@ -2237,7 +2407,7 @@
     if (validate_apk_path(packageDir.c_str())) {
         return error("Invalid path " + packageDir);
     }
-    if (delete_dir_contents_and_dir(packageDir) != 0) {
+    if (rm_package_dir(packageDir) != 0) {
         return error("Failed to delete " + packageDir);
     }
     return ok();
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 367f2c1..098a0c2 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -60,6 +60,12 @@
 
     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);
+    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 getAppSize(const std::unique_ptr<std::string>& uuid,
             const std::vector<std::string>& packageNames, int32_t userId, int32_t flags,
             int32_t appId, const std::vector<int64_t>& ceDataInodes,
@@ -89,6 +95,9 @@
             const std::unique_ptr<std::string>& dexMetadataPath,
             const std::unique_ptr<std::string>& compilationReason);
 
+    binder::Status compileLayouts(const std::string& apkPath, const std::string& packageName,
+                                  const std::string& outDexFile, int uid, bool* _aidl_return);
+
     binder::Status rmdex(const std::string& codePath, const std::string& instructionSet);
 
     binder::Status mergeProfiles(int32_t uid, const std::string& packageName,
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 89b08e5..3d093b8 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -55,6 +55,8 @@
             @nullable @utf8InCpp String profileName,
             @nullable @utf8InCpp String dexMetadataPath,
             @nullable @utf8InCpp String compilationReason);
+    boolean compileLayouts(@utf8InCpp String apkPath, @utf8InCpp String packageName,
+            @utf8InCpp String outDexFile, int uid);
 
     void rmdex(@utf8InCpp String codePath, @utf8InCpp String instructionSet);
 
@@ -103,6 +105,15 @@
         int userId, int appId, @utf8InCpp String profileName, @utf8InCpp String codePath,
         @nullable @utf8InCpp String dexMetadata);
 
+    void 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);
+
     const int FLAG_STORAGE_DE = 0x1;
     const int FLAG_STORAGE_CE = 0x2;
 
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 32c1313..940ba79 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -43,6 +43,7 @@
 #include <log/log.h>               // TODO: Move everything to base/logging.
 #include <openssl/sha.h>
 #include <private/android_filesystem_config.h>
+#include <processgroup/sched_policy.h>
 #include <selinux/android.h>
 #include <system/thread_defs.h>
 
@@ -332,8 +333,7 @@
             MapPropertyToArg("dalvik.vm.dex2oat-very-large", "--very-large-app-threshold=%s");
 
         // If the runtime was requested to use libartd.so, we'll run dex2oatd, otherwise dex2oat.
-        const char* dex2oat_bin = "/system/bin/dex2oat";
-        constexpr const char* kDex2oatDebugPath = "/system/bin/dex2oatd";
+        const char* dex2oat_bin = kDex2oatPath;
         // Do not use dex2oatd for release candidates (give dex2oat more soak time).
         bool is_release = android::base::GetProperty("ro.build.version.codename", "") == "REL";
         if (is_debug_runtime() ||
@@ -627,27 +627,6 @@
     }
 }
 
-static void drop_capabilities(uid_t uid) {
-    if (setgid(uid) != 0) {
-        PLOG(ERROR) << "setgid(" << uid << ") failed in installd during dexopt";
-        exit(DexoptReturnCodes::kSetGid);
-    }
-    if (setuid(uid) != 0) {
-        PLOG(ERROR) << "setuid(" << uid << ") failed in installd during dexopt";
-        exit(DexoptReturnCodes::kSetUid);
-    }
-    // drop capabilities
-    struct __user_cap_header_struct capheader;
-    struct __user_cap_data_struct capdata[2];
-    memset(&capheader, 0, sizeof(capheader));
-    memset(&capdata, 0, sizeof(capdata));
-    capheader.version = _LINUX_CAPABILITY_VERSION_3;
-    if (capset(&capheader, &capdata[0]) < 0) {
-        PLOG(ERROR) << "capset failed";
-        exit(DexoptReturnCodes::kCapSet);
-    }
-}
-
 static constexpr int PROFMAN_BIN_RETURN_CODE_COMPILE = 0;
 static constexpr int PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION = 1;
 static constexpr int PROFMAN_BIN_RETURN_CODE_BAD_PROFILES = 2;
@@ -662,8 +641,7 @@
                   const std::vector<std::string>& dex_locations,
                   bool copy_and_update,
                   bool store_aggregation_counters) {
-        const char* profman_bin =
-                is_debug_runtime() ? "/system/bin/profmand" : "/system/bin/profman";
+        const char* profman_bin = is_debug_runtime() ? kProfmanDebugPath: kProfmanPath;
 
         if (copy_and_update) {
             CHECK_EQ(1u, profile_fds.size());
@@ -1479,9 +1457,7 @@
                     const char* class_loader_context) {
         CHECK_GE(zip_fd, 0);
         const char* dexoptanalyzer_bin =
-                is_debug_runtime()
-                        ? "/system/bin/dexoptanalyzerd"
-                        : "/system/bin/dexoptanalyzer";
+            is_debug_runtime() ? kDexoptanalyzerDebugPath : kDexoptanalyzerPath;
 
         std::string dex_file_arg = "--dex-file=" + dex_file;
         std::string oat_fd_arg = "--oat-fd=" + std::to_string(oat_fd);
diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h
index 0db11e1..5902659 100644
--- a/cmds/installd/dexopt.h
+++ b/cmds/installd/dexopt.h
@@ -32,6 +32,16 @@
 static constexpr int DEX2OAT_FOR_BOOT_IMAGE      = 2;
 static constexpr int DEX2OAT_FOR_FILTER          = 3;
 
+#define ANDROID_RUNTIME_APEX_BIN "/apex/com.android.runtime/bin"
+// Location of binaries in the Android Runtime APEX.
+static constexpr const char* kDex2oatPath = ANDROID_RUNTIME_APEX_BIN "/dex2oat";
+static constexpr const char* kDex2oatDebugPath = ANDROID_RUNTIME_APEX_BIN "/dex2oatd";
+static constexpr const char* kProfmanPath = ANDROID_RUNTIME_APEX_BIN "/profmand";
+static constexpr const char* kProfmanDebugPath = ANDROID_RUNTIME_APEX_BIN "/profmand";
+static constexpr const char* kDexoptanalyzerPath = ANDROID_RUNTIME_APEX_BIN "/dexoptanalyzer";
+static constexpr const char* kDexoptanalyzerDebugPath = ANDROID_RUNTIME_APEX_BIN "/dexoptanalyzerd";
+#undef ANDROID_RUNTIME_APEX_BIN
+
 // Clear the reference profile identified by the given profile name.
 bool clear_primary_reference_profile(const std::string& pkgname, const std::string& profile_name);
 // Clear the current profile identified by the given profile name (for single user).
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index b2e7047..de7b249 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -26,7 +26,6 @@
 #include <sys/capability.h>
 #include <sys/prctl.h>
 #include <sys/stat.h>
-#include <sys/wait.h>
 
 #include <android-base/logging.h>
 #include <android-base/macros.h>
@@ -58,7 +57,6 @@
 #define REPLY_MAX     256   /* largest reply allowed */
 
 using android::base::EndsWith;
-using android::base::Join;
 using android::base::Split;
 using android::base::StartsWith;
 using android::base::StringPrintf;
@@ -440,7 +438,7 @@
                           const char* isa) const {
         // This needs to be kept in sync with ART, see art/runtime/gc/space/image_space.cc.
         std::vector<std::string> cmd;
-        cmd.push_back("/system/bin/dex2oat");
+        cmd.push_back(kDex2oatPath);
         cmd.push_back(StringPrintf("--image=%s", art_path.c_str()));
         for (const std::string& boot_part : Split(boot_cp, ":")) {
             cmd.push_back(StringPrintf("--dex-file=%s", boot_part.c_str()));
@@ -619,61 +617,6 @@
     // Helpers, mostly taken from ART //
     ////////////////////////////////////
 
-    // Wrapper on fork/execv to run a command in a subprocess.
-    static bool Exec(const std::vector<std::string>& arg_vector, std::string* error_msg) {
-        const std::string command_line = Join(arg_vector, ' ');
-
-        CHECK_GE(arg_vector.size(), 1U) << command_line;
-
-        // Convert the args to char pointers.
-        const char* program = arg_vector[0].c_str();
-        std::vector<char*> args;
-        for (size_t i = 0; i < arg_vector.size(); ++i) {
-            const std::string& arg = arg_vector[i];
-            char* arg_str = const_cast<char*>(arg.c_str());
-            CHECK(arg_str != nullptr) << i;
-            args.push_back(arg_str);
-        }
-        args.push_back(nullptr);
-
-        // Fork and exec.
-        pid_t pid = fork();
-        if (pid == 0) {
-            // No allocation allowed between fork and exec.
-
-            // Change process groups, so we don't get reaped by ProcessManager.
-            setpgid(0, 0);
-
-            execv(program, &args[0]);
-
-            PLOG(ERROR) << "Failed to execv(" << command_line << ")";
-            // _exit to avoid atexit handlers in child.
-            _exit(1);
-        } else {
-            if (pid == -1) {
-                *error_msg = StringPrintf("Failed to execv(%s) because fork failed: %s",
-                        command_line.c_str(), strerror(errno));
-                return false;
-            }
-
-            // wait for subprocess to finish
-            int status;
-            pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
-            if (got_pid != pid) {
-                *error_msg = StringPrintf("Failed after fork for execv(%s) because waitpid failed: "
-                        "wanted %d, got %d: %s",
-                        command_line.c_str(), pid, got_pid, strerror(errno));
-                return false;
-            }
-            if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
-                *error_msg = StringPrintf("Failed execv(%s) because non-0 exit status",
-                        command_line.c_str());
-                return false;
-            }
-        }
-        return true;
-    }
-
     // Choose a random relocation offset. Taken from art/runtime/gc/image_space.cc.
     static int32_t ChooseRelocationOffsetDelta(int32_t min_delta, int32_t max_delta) {
         constexpr size_t kPageSize = PAGE_SIZE;
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index e90cf3b..c0f8e91 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -17,6 +17,7 @@
 #include <fcntl.h>
 #include <linux/unistd.h>
 #include <sys/mount.h>
+#include <sys/stat.h>
 #include <sys/wait.h>
 
 #include <sstream>
@@ -24,6 +25,9 @@
 #include <android-base/logging.h>
 #include <android-base/macros.h>
 #include <android-base/stringprintf.h>
+#include <selinux/android.h>
+
+#include <apexd.h>
 
 #include "installd_constants.h"
 #include "otapreopt_utils.h"
@@ -138,6 +142,44 @@
       UNUSED(product_result);
     }
 
+    // Setup APEX mount point and its security context.
+    static constexpr const char* kPostinstallApexDir = "/postinstall/apex";
+    // The following logic is similar to the one in system/core/rootdir/init.rc:
+    //
+    //   mount tmpfs tmpfs /apex nodev noexec nosuid
+    //   chmod 0755 /apex
+    //   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);
+    }
+    if (chown(kPostinstallApexDir, 0, 0) != 0) {
+        PLOG(ERROR) << "Failed to chown " << kPostinstallApexDir << " to root:root";
+        exit(211);
+    }
+
     // Chdir into /postinstall.
     if (chdir("/postinstall") != 0) {
         PLOG(ERROR) << "Unable to chdir into /postinstall.";
@@ -155,22 +197,52 @@
         exit(205);
     }
 
+    // Try to mount APEX packages in "/apex" in the chroot dir. We need at least
+    // the Android Runtime APEX, as it is required by otapreopt to run dex2oat.
+    // The logic here is (partially) copied and adapted from
+    // system/apex/apexd/apexd_main.cpp.
+    //
+    // Only scan the APEX directory under /system (within the chroot dir).
+    // Note that this leaves around the loop devices created and used by
+    // libapexd's code, but this is fine, as we expect to reboot soon after.
+    apex::scanPackagesDirAndActivate(apex::kApexPackageSystemDir);
+    // Collect activated packages.
+    std::vector<apex::ApexFile> active_packages = apex::getActivePackages();
+
     // Now go on and run otapreopt.
 
-    // Incoming:  cmd + status-fd + target-slot + cmd... + null      | Incoming | = argc + 1
-    // Outgoing:  cmd             + target-slot + cmd... + null      | Outgoing | = argc
-    const char** argv = new const char*[argc];
-
-    argv[0] = "/system/bin/otapreopt";
+    // Incoming:  cmd + status-fd + target-slot + cmd...      | Incoming | = argc
+    // Outgoing:  cmd             + target-slot + cmd...      | Outgoing | = argc - 1
+    std::vector<std::string> cmd;
+    cmd.reserve(argc);
+    cmd.push_back("/system/bin/otapreopt");
 
     // The first parameter is the status file descriptor, skip.
-    for (size_t i = 2; i <= static_cast<size_t>(argc); ++i) {
-        argv[i - 1] = arg[i];
+    for (size_t i = 2; i < static_cast<size_t>(argc); ++i) {
+        cmd.push_back(arg[i]);
     }
 
-    execv(argv[0], static_cast<char * const *>(const_cast<char**>(argv)));
-    PLOG(ERROR) << "execv(OTAPREOPT) failed.";
-    exit(99);
+    // Fork and execute otapreopt in its own process.
+    std::string error_msg;
+    bool exec_result = Exec(cmd, &error_msg);
+    if (!exec_result) {
+        LOG(ERROR) << "Running otapreopt failed: " << error_msg;
+    }
+
+    // Tear down the work down by the apexd logic above (i.e. deactivate packages).
+    for (const apex::ApexFile& apex_file : active_packages) {
+        const std::string& package_path = apex_file.GetPath();
+        apex::Status status = apex::deactivatePackage(package_path);
+        if (!status.Ok()) {
+            LOG(ERROR) << "Failed to deactivate " << package_path << ": " << status.ErrorMessage();
+        }
+    }
+
+    if (!exec_result) {
+        exit(213);
+    }
+
+    return 0;
 }
 
 }  // namespace installd
diff --git a/cmds/installd/otapreopt_utils.cpp b/cmds/installd/otapreopt_utils.cpp
new file mode 100644
index 0000000..124f726
--- /dev/null
+++ b/cmds/installd/otapreopt_utils.cpp
@@ -0,0 +1,88 @@
+/*
+ ** 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 "otapreopt_utils.h"
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+using android::base::Join;
+using android::base::StringPrintf;
+
+namespace android {
+namespace installd {
+
+bool Exec(const std::vector<std::string>& arg_vector, std::string* error_msg) {
+    const std::string command_line = Join(arg_vector, ' ');
+
+    CHECK_GE(arg_vector.size(), 1U) << command_line;
+
+    // Convert the args to char pointers.
+    const char* program = arg_vector[0].c_str();
+    std::vector<char*> args;
+    for (size_t i = 0; i < arg_vector.size(); ++i) {
+        const std::string& arg = arg_vector[i];
+        char* arg_str = const_cast<char*>(arg.c_str());
+        CHECK(arg_str != nullptr) << i;
+        args.push_back(arg_str);
+    }
+    args.push_back(nullptr);
+
+    // Fork and exec.
+    pid_t pid = fork();
+    if (pid == 0) {
+        // No allocation allowed between fork and exec.
+
+        // Change process groups, so we don't get reaped by ProcessManager.
+        setpgid(0, 0);
+
+        execv(program, &args[0]);
+
+        PLOG(ERROR) << "Failed to execv(" << command_line << ")";
+        // _exit to avoid atexit handlers in child.
+        _exit(1);
+    } else {
+        if (pid == -1) {
+            *error_msg = StringPrintf("Failed to execv(%s) because fork failed: %s",
+                    command_line.c_str(), strerror(errno));
+            return false;
+        }
+
+        // wait for subprocess to finish
+        int status;
+        pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
+        if (got_pid != pid) {
+            *error_msg = StringPrintf("Failed after fork for execv(%s) because waitpid failed: "
+                    "wanted %d, got %d: %s",
+                    command_line.c_str(), pid, got_pid, strerror(errno));
+            return false;
+        }
+        if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+            *error_msg = StringPrintf("Failed execv(%s) because non-0 exit status",
+                    command_line.c_str());
+            return false;
+        }
+    }
+    return true;
+}
+
+}  // namespace installd
+}  // namespace android
diff --git a/cmds/installd/otapreopt_utils.h b/cmds/installd/otapreopt_utils.h
index 436e554..03a6d87 100644
--- a/cmds/installd/otapreopt_utils.h
+++ b/cmds/installd/otapreopt_utils.h
@@ -18,6 +18,8 @@
 #define OTAPREOPT_UTILS_H_
 
 #include <regex>
+#include <string>
+#include <vector>
 
 namespace android {
 namespace installd {
@@ -28,6 +30,9 @@
     return std::regex_match(input, slot_suffix_match, slot_suffix_regex);
 }
 
+// Wrapper on fork/execv to run a command in a subprocess.
+bool Exec(const std::vector<std::string>& arg_vector, std::string* error_msg);
+
 }  // namespace installd
 }  // namespace android
 
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
index 739f33f..9c9db0f 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -28,6 +28,7 @@
         "libbinder",
         "libcrypto",
         "libcutils",
+        "libprocessgroup",
         "libselinux",
         "libutils",
     ],
@@ -50,6 +51,7 @@
         "libbinder",
         "libcrypto",
         "libcutils",
+        "libprocessgroup",
         "libselinux",
         "libutils",
     ],
@@ -72,6 +74,7 @@
         "libbinder",
         "libcrypto",
         "libcutils",
+        "libprocessgroup",
         "libselinux",
         "libutils",
     ],
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
index 9dad995..12664ac 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -21,6 +21,8 @@
 #include <sys/xattr.h>
 
 #include <android-base/logging.h>
+#include <android-base/file.h>
+#include <android-base/scopeguard.h>
 #include <android-base/stringprintf.h>
 #include <cutils/properties.h>
 #include <gtest/gtest.h>
@@ -240,5 +242,335 @@
     EXPECT_EQ("/data/dalvik-cache/isa/path@to@file.apk@classes.dex", std::string(buf));
 }
 
+static bool mkdirs(const std::string& path, mode_t mode) {
+    struct stat sb;
+    if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) {
+        return true;
+    }
+
+    if (!mkdirs(android::base::Dirname(path), mode)) {
+        return false;
+    }
+
+    return (::mkdir(path.c_str(), mode) != -1);
+}
+
+TEST_F(ServiceTest, CreateAppDataSnapshot) {
+  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_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 */));
+
+  // 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());
+
+  std::string ce_content, de_content;
+  // At this point, we should have the CE content but not the DE content.
+  ASSERT_TRUE(android::base::ReadFileToString(
+      rollback_ce_dir + "/com.foo/file1", &ce_content, false /* follow_symlinks */));
+  ASSERT_FALSE(android::base::ReadFileToString(
+      rollback_de_dir + "/com.foo/file1", &de_content, false /* follow_symlinks */));
+  ASSERT_EQ("TEST_CONTENT_CE", ce_content);
+
+  // Modify the CE content, so we can assert later that it's reflected
+  // in the snapshot.
+  ASSERT_TRUE(android::base::WriteStringToFile(
+          "TEST_CONTENT_CE_MODIFIED", fake_package_ce_path + "/file1",
+          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());
+
+  // At this point, both the CE as well as the DE content should be fully
+  // populated.
+  ASSERT_TRUE(android::base::ReadFileToString(
+      rollback_ce_dir + "/com.foo/file1", &ce_content, false /* follow_symlinks */));
+  ASSERT_TRUE(android::base::ReadFileToString(
+      rollback_de_dir + "/com.foo/file1", &de_content, false /* follow_symlinks */));
+  ASSERT_EQ("TEST_CONTENT_CE", ce_content);
+  ASSERT_EQ("TEST_CONTENT_DE", de_content);
+
+  // Modify the DE content, so we can assert later that it's reflected
+  // in our final snapshot.
+  ASSERT_TRUE(android::base::WriteStringToFile(
+          "TEST_CONTENT_DE_MODIFIED", fake_package_de_path + "/file1",
+          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_TRUE(android::base::ReadFileToString(
+      rollback_ce_dir + "/com.foo/file1", &ce_content, false /* follow_symlinks */));
+  ASSERT_TRUE(android::base::ReadFileToString(
+      rollback_de_dir + "/com.foo/file1", &de_content, false /* follow_symlinks */));
+  ASSERT_EQ("TEST_CONTENT_CE_MODIFIED", ce_content);
+  ASSERT_EQ("TEST_CONTENT_DE_MODIFIED", de_content);
+}
+
+TEST_F(ServiceTest, CreateAppDataSnapshot_AppDataAbsent) {
+  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);
+
+  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());
+
+  // The snapshot calls must succeed but there should be no snapshot
+  // created.
+  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));
+}
+
+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_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+          "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk());
+
+  // 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);
+
+  ASSERT_FALSE(service->snapshotAppData(std::make_unique<std::string>("FOO"),
+          "com.foo", 0, FLAG_STORAGE_DE).isOk());
+}
+
+TEST_F(ServiceTest, CreateAppDataSnapshot_ClearsCache) {
+  auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo");
+  auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo");
+  auto fake_package_ce_cache_path = read_path_inode(fake_package_ce_path,
+      "cache", kXattrInodeCache);
+  auto fake_package_ce_code_cache_path = read_path_inode(fake_package_ce_path,
+      "code_cache", kXattrInodeCache);
+  auto fake_package_de_cache_path = fake_package_de_path + "/cache";
+  auto fake_package_de_code_cache_path = fake_package_de_path + "/code_cache";
+
+  ASSERT_TRUE(mkdirs(fake_package_ce_path, 700));
+  ASSERT_TRUE(mkdirs(fake_package_de_path, 700));
+  ASSERT_TRUE(mkdirs(fake_package_ce_cache_path, 700));
+  ASSERT_TRUE(mkdirs(fake_package_ce_code_cache_path, 700));
+  ASSERT_TRUE(mkdirs(fake_package_de_cache_path, 700));
+  ASSERT_TRUE(mkdirs(fake_package_de_code_cache_path, 700));
+
+  auto deleter = [&fake_package_ce_path, &fake_package_de_path,
+          &fake_package_ce_cache_path, &fake_package_ce_code_cache_path,
+          &fake_package_de_cache_path, &fake_package_de_code_cache_path]() {
+      delete_dir_contents(fake_package_ce_path, true);
+      delete_dir_contents(fake_package_de_path, true);
+      delete_dir_contents(fake_package_ce_cache_path, true);
+      delete_dir_contents(fake_package_ce_code_cache_path, true);
+      delete_dir_contents(fake_package_de_cache_path, true);
+      delete_dir_contents(fake_package_de_code_cache_path, true);
+      rmdir(fake_package_ce_cache_path.c_str());
+      rmdir(fake_package_ce_code_cache_path.c_str());
+      rmdir(fake_package_de_cache_path.c_str());
+      rmdir(fake_package_de_code_cache_path.c_str());
+  };
+  auto scope_guard = android::base::make_scope_guard(deleter);
+
+  ASSERT_TRUE(android::base::WriteStringToFile(
+          "TEST_CONTENT_CE", fake_package_ce_cache_path + "/file1",
+          0700, 10000, 20000, false /* follow_symlinks */));
+  ASSERT_TRUE(android::base::WriteStringToFile(
+          "TEST_CONTENT_CE", fake_package_ce_code_cache_path + "/file1",
+          0700, 10000, 20000, false /* follow_symlinks */));
+  ASSERT_TRUE(android::base::WriteStringToFile(
+          "TEST_CONTENT_DE", fake_package_de_cache_path + "/file1",
+          0700, 10000, 20000, false /* follow_symlinks */));
+  ASSERT_TRUE(android::base::WriteStringToFile(
+          "TEST_CONTENT_DE", fake_package_de_code_cache_path + "/file1",
+          0700, 10000, 20000, false /* follow_symlinks */));
+  ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+          "com.foo", 0, FLAG_STORAGE_CE | FLAG_STORAGE_DE).isOk());
+  // The snapshot call must clear cache.
+  struct stat sb;
+  ASSERT_EQ(-1, stat((fake_package_ce_cache_path + "/file1").c_str(), &sb));
+  ASSERT_EQ(-1, stat((fake_package_ce_code_cache_path + "/file1").c_str(), &sb));
+  ASSERT_EQ(-1, stat((fake_package_de_cache_path + "/file1").c_str(), &sb));
+  ASSERT_EQ(-1, stat((fake_package_de_code_cache_path + "/file1").c_str(), &sb));
+}
+
+TEST_F(ServiceTest, RestoreAppDataSnapshot) {
+  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);
+
+  // Write contents to the rollback location. We'll write the same files to the
+  // app data location and make sure the restore has overwritten them.
+  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(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 */));
+
+  ASSERT_TRUE(service->restoreAppDataSnapshot(std::make_unique<std::string>("TEST"),
+          "com.foo", 10000, -1, "", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk());
+
+  std::string ce_content, de_content;
+  ASSERT_TRUE(android::base::ReadFileToString(
+      fake_package_ce_path + "/file1", &ce_content, false /* follow_symlinks */));
+  ASSERT_TRUE(android::base::ReadFileToString(
+      fake_package_de_path + "/file1", &de_content, false /* follow_symlinks */));
+  ASSERT_EQ("CE_RESTORE_CONTENT", ce_content);
+  ASSERT_EQ("DE_RESTORE_CONTENT", de_content);
+}
+
+
+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);
+
+  ASSERT_FALSE(service->restoreAppDataSnapshot(std::make_unique<std::string>("BAR"),
+          "com.foo", 10000, -1, "", 0, FLAG_STORAGE_DE).isOk());
+}
+
 }  // namespace installd
 }  // namespace android
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index ad7a5ae..ce99fff 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -544,5 +544,35 @@
     EXPECT_EQ(0, MatchExtension("docx"));
 }
 
+TEST_F(UtilsTest, TestRollbackPaths) {
+    EXPECT_EQ("/data/misc_ce/0/rollback/com.foo",
+            create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo"));
+    EXPECT_EQ("/data/misc_ce/10/rollback/com.foo",
+            create_data_misc_ce_rollback_package_path(nullptr, 10, "com.foo"));
+
+    EXPECT_EQ("/data/misc_de/0/rollback/com.foo",
+            create_data_misc_de_rollback_package_path(nullptr, 0, "com.foo"));
+    EXPECT_EQ("/data/misc_de/10/rollback/com.foo",
+            create_data_misc_de_rollback_package_path(nullptr, 10, "com.foo"));
+
+    EXPECT_EQ("/data/misc_ce/0/rollback",
+            create_data_misc_ce_rollback_path(nullptr, 0));
+    EXPECT_EQ("/data/misc_ce/10/rollback",
+            create_data_misc_ce_rollback_path(nullptr, 10));
+
+    EXPECT_EQ("/data/misc_de/0/rollback",
+            create_data_misc_de_rollback_path(nullptr, 0));
+    EXPECT_EQ("/data/misc_de/10/rollback",
+            create_data_misc_de_rollback_path(nullptr, 10));
+
+    // 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.
+    EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/misc_ce/0/rollback/com.example",
+            create_data_misc_ce_rollback_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 0, "com.example"));
+    EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/misc_de/0/rollback/com.example",
+            create_data_misc_de_rollback_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 0, "com.example"));
+}
+
 }  // namespace installd
 }  // namespace android
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 74ad184..24f5eab 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -20,6 +20,7 @@
 #include <fcntl.h>
 #include <fts.h>
 #include <stdlib.h>
+#include <sys/capability.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
 #include <sys/xattr.h>
@@ -34,6 +35,7 @@
 #include <log/log.h>
 #include <private/android_filesystem_config.h>
 
+#include "dexopt_return_codes.h"
 #include "globals.h"  // extern variables.
 
 #ifndef LOG_TAG
@@ -192,6 +194,27 @@
     return StringPrintf("%s/user_de/%u", data.c_str(), userid);
 }
 
+
+std::string create_data_misc_ce_rollback_path(const char* volume_uuid, userid_t user) {
+    return StringPrintf("%s/misc_ce/%u/rollback", create_data_path(volume_uuid).c_str(), user);
+}
+
+std::string create_data_misc_de_rollback_path(const char* volume_uuid, userid_t user) {
+    return StringPrintf("%s/misc_de/%u/rollback", create_data_path(volume_uuid).c_str(), user);
+}
+
+std::string create_data_misc_ce_rollback_package_path(const char* volume_uuid,
+        userid_t user, const char* package_name) {
+    return StringPrintf("%s/%s",
+           create_data_misc_ce_rollback_path(volume_uuid, user).c_str(), package_name);
+}
+
+std::string create_data_misc_de_rollback_package_path(const char* volume_uuid,
+        userid_t user, const char* package_name) {
+    return StringPrintf("%s/%s",
+           create_data_misc_de_rollback_path(volume_uuid, user).c_str(), package_name);
+}
+
 /**
  * Create the path name for media for a certain userid.
  */
@@ -1063,5 +1086,26 @@
     }
 }
 
+void drop_capabilities(uid_t uid) {
+    if (setgid(uid) != 0) {
+        PLOG(ERROR) << "setgid(" << uid << ") failed in installd during dexopt";
+        exit(DexoptReturnCodes::kSetGid);
+    }
+    if (setuid(uid) != 0) {
+        PLOG(ERROR) << "setuid(" << uid << ") failed in installd during dexopt";
+        exit(DexoptReturnCodes::kSetUid);
+    }
+    // drop capabilities
+    struct __user_cap_header_struct capheader;
+    struct __user_cap_data_struct capdata[2];
+    memset(&capheader, 0, sizeof(capheader));
+    memset(&capdata, 0, sizeof(capdata));
+    capheader.version = _LINUX_CAPABILITY_VERSION_3;
+    if (capset(&capheader, &capdata[0]) < 0) {
+        PLOG(ERROR) << "capset failed";
+        exit(DexoptReturnCodes::kCapSet);
+    }
+}
+
 }  // namespace installd
 }  // namespace android
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index d05724a..5afe059 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -61,6 +61,13 @@
 std::string create_data_user_ce_package_path_as_user_link(
         const char* volume_uuid, userid_t userid, const char* package_name);
 
+std::string create_data_misc_ce_rollback_path(const char* volume_uuid, userid_t user);
+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_de_rollback_package_path(const char* volume_uuid,
+        userid_t user, const char* package_name);
+
 std::string create_data_media_path(const char* volume_uuid, userid_t userid);
 std::string create_data_media_obb_path(const char* volume_uuid, const char* package_name);
 std::string create_data_media_package_path(const char* volume_uuid, userid_t userid,
@@ -110,6 +117,8 @@
 
 int delete_dir_contents_fd(int dfd, const char *name);
 
+int rm_package_dir(const std::string& package_dir);
+
 int copy_dir_files(const char *srcname, const char *dstname, uid_t owner, gid_t group);
 
 int64_t data_disk_free(const std::string& data_path);
@@ -140,6 +149,8 @@
 // It returns true if there were no errors at all, and false otherwise.
 bool collect_profiles(std::vector<std::string>* profiles_paths);
 
+void drop_capabilities(uid_t uid);
+
 }  // namespace installd
 }  // namespace android
 
diff --git a/cmds/installd/utils_default.cpp b/cmds/installd/utils_default.cpp
new file mode 100644
index 0000000..a6025e6
--- /dev/null
+++ b/cmds/installd/utils_default.cpp
@@ -0,0 +1,30 @@
+/*
+** 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 "utils.h"
+
+namespace android {
+namespace installd {
+
+// In this file are default definitions of the functions that may contain
+// platform dependent logic.
+
+int rm_package_dir(const std::string& package_dir) {
+    return delete_dir_contents_and_dir(package_dir);
+}
+
+}  // namespace installd
+}  // namespace android
diff --git a/cmds/installd/view_compiler.cpp b/cmds/installd/view_compiler.cpp
new file mode 100644
index 0000000..f1ac717
--- /dev/null
+++ b/cmds/installd/view_compiler.cpp
@@ -0,0 +1,99 @@
+/*
+ * 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 "view_compiler.h"
+
+#include <string>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "utils.h"
+
+#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
+#include "android-base/unique_fd.h"
+
+namespace android {
+namespace installd {
+
+using base::unique_fd;
+
+bool view_compiler(const char* apk_path, const char* package_name, const char* out_dex_file,
+                   int uid) {
+    CHECK(apk_path != nullptr);
+    CHECK(package_name != nullptr);
+    CHECK(out_dex_file != nullptr);
+
+    // viewcompiler won't have permission to open anything, so we have to open the files first
+    // and pass file descriptors.
+
+    // Open input file
+    unique_fd infd{open(apk_path, 0)};
+    if (infd.get() < 0) {
+        PLOG(ERROR) << "Could not open input file: " << apk_path;
+        return false;
+    }
+
+    // Set up output file. viewcompiler can't open outputs by fd, but it can write to stdout, so
+    // we close stdout and open it towards the right output.
+    unique_fd outfd{open(out_dex_file, O_CREAT | O_TRUNC | O_WRONLY, 0644)};
+    if (outfd.get() < 0) {
+        PLOG(ERROR) << "Could not open output file: " << out_dex_file;
+        return false;
+    }
+    if (fchmod(outfd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) != 0) {
+        PLOG(ERROR) << "Could not change output file permissions";
+        return false;
+    }
+    if (close(STDOUT_FILENO) != 0) {
+        PLOG(ERROR) << "Could not close stdout";
+        return false;
+    }
+    if (dup2(outfd, STDOUT_FILENO) < 0) {
+        PLOG(ERROR) << "Could not duplicate output file descriptor";
+        return false;
+    }
+
+    // Prepare command line arguments for viewcompiler
+    std::string args[] = {"/system/bin/viewcompiler",
+                          "--apk",
+                          "--infd",
+                          android::base::StringPrintf("%d", infd.get()),
+                          "--dex",
+                          "--package",
+                          package_name};
+    char* const argv[] = {const_cast<char*>(args[0].c_str()), const_cast<char*>(args[1].c_str()),
+                          const_cast<char*>(args[2].c_str()), const_cast<char*>(args[3].c_str()),
+                          const_cast<char*>(args[4].c_str()), const_cast<char*>(args[5].c_str()),
+                          const_cast<char*>(args[6].c_str()), nullptr};
+
+    pid_t pid = fork();
+    if (pid == 0) {
+        // Now that we've opened the files we need, drop privileges.
+        drop_capabilities(uid);
+        execv("/system/bin/viewcompiler", argv);
+        _exit(1);
+    }
+
+    return wait_child(pid) == 0;
+}
+
+} // namespace installd
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/cmds/installd/view_compiler.h
similarity index 66%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to cmds/installd/view_compiler.h
index e6ac6bf..f7c6e57 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/cmds/installd/view_compiler.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -14,14 +14,16 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#ifndef VIEW_COMPILER_H_
+#define VIEW_COMPILER_H_
 
 namespace android {
-namespace mock {
+namespace installd {
 
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+bool view_compiler(const char* apk_path, const char* package_name, const char* out_dex_file,
+                   int uid);
 
-} // namespace mock
+} // namespace installd
 } // namespace android
+
+#endif // VIEW_COMPILER_H_
\ No newline at end of file
diff --git a/cmds/lshal/Command.h b/cmds/lshal/Command.h
index 4f128ab..e19e3f7 100644
--- a/cmds/lshal/Command.h
+++ b/cmds/lshal/Command.h
@@ -27,7 +27,7 @@
 // Base class for all *Commands
 class Command {
 public:
-    Command(Lshal& lshal) : mLshal(lshal) {}
+    explicit Command(Lshal& lshal) : mLshal(lshal) {}
     virtual ~Command() = default;
     // Expect optind to be set by Lshal::main and points to the next argument
     // to process.
diff --git a/cmds/lshal/DebugCommand.h b/cmds/lshal/DebugCommand.h
index 6e12008..3c3f56f 100644
--- a/cmds/lshal/DebugCommand.h
+++ b/cmds/lshal/DebugCommand.h
@@ -31,7 +31,7 @@
 
 class DebugCommand : public Command {
 public:
-    DebugCommand(Lshal &lshal) : Command(lshal) {}
+    explicit DebugCommand(Lshal &lshal) : Command(lshal) {}
     ~DebugCommand() = default;
     Status main(const Arg &arg) override;
     void usage() const override;
diff --git a/cmds/lshal/HelpCommand.h b/cmds/lshal/HelpCommand.h
index cc709f8..da0cba6 100644
--- a/cmds/lshal/HelpCommand.h
+++ b/cmds/lshal/HelpCommand.h
@@ -31,7 +31,7 @@
 
 class HelpCommand : public Command {
 public:
-    HelpCommand(Lshal &lshal) : Command(lshal) {}
+    explicit HelpCommand(Lshal &lshal) : Command(lshal) {}
     ~HelpCommand() = default;
     Status main(const Arg &arg) override;
     void usage() const override;
diff --git a/cmds/lshal/ListCommand.h b/cmds/lshal/ListCommand.h
index 3f7321d..85195fc 100644
--- a/cmds/lshal/ListCommand.h
+++ b/cmds/lshal/ListCommand.h
@@ -57,7 +57,7 @@
 
 class ListCommand : public Command {
 public:
-    ListCommand(Lshal &lshal) : Command(lshal) {}
+    explicit ListCommand(Lshal &lshal) : Command(lshal) {}
     virtual ~ListCommand() = default;
     Status main(const Arg &arg) override;
     void usage() const override;
diff --git a/cmds/lshal/NullableOStream.h b/cmds/lshal/NullableOStream.h
index ab37a04..737d3a2 100644
--- a/cmds/lshal/NullableOStream.h
+++ b/cmds/lshal/NullableOStream.h
@@ -25,8 +25,8 @@
 template<typename S>
 class NullableOStream {
 public:
-    NullableOStream(S &os) : mOs(&os) {}
-    NullableOStream(S *os) : mOs(os) {}
+    explicit NullableOStream(S &os) : mOs(&os) {}
+    explicit NullableOStream(S *os) : mOs(os) {}
     NullableOStream &operator=(S &os) {
         mOs = &os;
         return *this;
@@ -57,7 +57,7 @@
     S& buf() const {
         return *mOs;
     }
-    operator bool() const {
+    operator bool() const { // NOLINT(google-explicit-constructor)
         return mOs != nullptr;
     }
 private:
diff --git a/cmds/lshal/TableEntry.h b/cmds/lshal/TableEntry.h
index 7294b0a..601b7e2 100644
--- a/cmds/lshal/TableEntry.h
+++ b/cmds/lshal/TableEntry.h
@@ -149,7 +149,7 @@
 
 class MergedTable {
 public:
-    MergedTable(std::vector<const Table*>&& tables) : mTables(std::move(tables)) {}
+    explicit MergedTable(std::vector<const Table*>&& tables) : mTables(std::move(tables)) {}
     TextTable createTextTable();
 private:
     std::vector<const Table*> mTables;
diff --git a/cmds/lshal/TextTable.h b/cmds/lshal/TextTable.h
index 91d522a..301b4bd 100644
--- a/cmds/lshal/TextTable.h
+++ b/cmds/lshal/TextTable.h
@@ -33,11 +33,11 @@
     TextTableRow() {}
 
     // A row of cells.
-    TextTableRow(std::vector<std::string>&& v) : mFields(std::move(v)) {}
+    explicit TextTableRow(std::vector<std::string>&& v) : mFields(std::move(v)) {}
 
     // A single comment string.
-    TextTableRow(std::string&& s) : mLine(std::move(s)) {}
-    TextTableRow(const std::string& s) : mLine(s) {}
+    explicit TextTableRow(std::string&& s) : mLine(std::move(s)) {}
+    explicit TextTableRow(const std::string& s) : mLine(s) {}
 
     // Whether this row is an actual row of cells.
     bool isRow() const { return !fields().empty(); }
diff --git a/cmds/lshal/Timeout.h b/cmds/lshal/Timeout.h
index 58119a6..46d8177 100644
--- a/cmds/lshal/Timeout.h
+++ b/cmds/lshal/Timeout.h
@@ -29,7 +29,7 @@
 
 class BackgroundTaskState {
 public:
-    BackgroundTaskState(std::function<void(void)> &&func)
+    explicit BackgroundTaskState(std::function<void(void)> &&func)
             : mFunc(std::forward<decltype(func)>(func)) {}
     void notify() {
         std::unique_lock<std::mutex> lock(mMutex);
diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp
index 8d7405b..fc8d58b 100644
--- a/cmds/lshal/test.cpp
+++ b/cmds/lshal/test.cpp
@@ -191,7 +191,7 @@
 // expose protected fields and methods for ListCommand
 class MockListCommand : public ListCommand {
 public:
-    MockListCommand(Lshal* lshal) : ListCommand(*lshal) {}
+    explicit MockListCommand(Lshal* lshal) : ListCommand(*lshal) {}
 
     Status parseArgs(const Arg& arg) { return ListCommand::parseArgs(arg); }
     Status main(const Arg& arg) { return ListCommand::main(arg); }
@@ -308,7 +308,7 @@
 // Fake service returned by mocked IServiceManager::get.
 class TestService : public IBase {
 public:
-    TestService(pid_t id) : mId(id) {}
+    explicit TestService(pid_t id) : mId(id) {}
     hardware::Return<void> getDebugInfo(getDebugInfo_cb cb) override {
         cb({ mId /* pid */, getPtr(mId), DebugInfo::Architecture::IS_64BIT });
         return hardware::Void();
diff --git a/cmds/surfacereplayer/OWNERS b/cmds/surfacereplayer/OWNERS
new file mode 100644
index 0000000..cc4c842
--- /dev/null
+++ b/cmds/surfacereplayer/OWNERS
@@ -0,0 +1,2 @@
+mathias@google.com
+racarr@google.com
diff --git a/data/etc/android.hardware.telephony.ims.xml b/data/etc/android.hardware.telephony.ims.xml
new file mode 100644
index 0000000..eeb7b00
--- /dev/null
+++ b/data/etc/android.hardware.telephony.ims.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- Feature for devices that support IMS via ImsService APIs. -->
+<permissions>
+    <feature name="android.hardware.telephony.ims" />
+</permissions>
diff --git a/data/etc/android.software.secure_lock_screen.xml b/data/etc/android.software.secure_lock_screen.xml
new file mode 100644
index 0000000..3464487
--- /dev/null
+++ b/data/etc/android.software.secure_lock_screen.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<permissions>
+    <feature name="android.software.secure_lock_screen" />
+</permissions>
diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml
index d6021c0..6cbe4ae 100644
--- a/data/etc/car_core_hardware.xml
+++ b/data/etc/car_core_hardware.xml
@@ -45,6 +45,7 @@
     <feature name="android.software.companion_device_setup" />
     <feature name="android.software.autofill" />
     <feature name="android.software.cant_save_state" />
+    <feature name="android.software.secure_lock_screen" />
 
     <!-- Feature to specify if the device supports adding device admins. -->
     <feature name="android.software.device_admin" />
diff --git a/data/etc/go_handheld_core_hardware.xml b/data/etc/go_handheld_core_hardware.xml
index 8b5a461..915e579 100644
--- a/data/etc/go_handheld_core_hardware.xml
+++ b/data/etc/go_handheld_core_hardware.xml
@@ -43,6 +43,7 @@
     <feature name="android.software.companion_device_setup" />
     <feature name="android.software.autofill" />
     <feature name="android.software.cant_save_state" />
+    <feature name="android.software.secure_lock_screen" />
 
     <!-- Feature to specify if the device supports adding device admins. -->
     <feature name="android.software.device_admin" />
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
index 060a334..619d017 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -51,6 +51,7 @@
     <feature name="android.software.companion_device_setup" />
     <feature name="android.software.autofill" />
     <feature name="android.software.cant_save_state" />
+    <feature name="android.software.secure_lock_screen" />
 
     <!-- Feature to specify if the device supports adding device admins. -->
     <feature name="android.software.device_admin" />
diff --git a/data/etc/tablet_core_hardware.xml b/data/etc/tablet_core_hardware.xml
index 6db2627..52524ca 100644
--- a/data/etc/tablet_core_hardware.xml
+++ b/data/etc/tablet_core_hardware.xml
@@ -51,6 +51,7 @@
     <feature name="android.software.companion_device_setup" />
     <feature name="android.software.autofill" />
     <feature name="android.software.cant_save_state" />
+    <feature name="android.software.secure_lock_screen" />
 
     <!-- Feature to specify if the device supports adding device admins. -->
     <feature name="android.software.device_admin" />
diff --git a/data/etc/wearable_core_hardware.xml b/data/etc/wearable_core_hardware.xml
index e2ab71a..0f364c1 100644
--- a/data/etc/wearable_core_hardware.xml
+++ b/data/etc/wearable_core_hardware.xml
@@ -35,6 +35,7 @@
 
     <!-- basic system services -->
     <feature name="android.software.home_screen" />
+    <feature name="android.software.secure_lock_screen" />
 
     <!-- input management and third-party input method editors -->
     <feature name="android.software.input_methods" />
diff --git a/headers/media_plugin/media/drm/DrmAPI.h b/headers/media_plugin/media/drm/DrmAPI.h
index aa8bd3d..0ce50f2 100644
--- a/headers/media_plugin/media/drm/DrmAPI.h
+++ b/headers/media_plugin/media/drm/DrmAPI.h
@@ -84,6 +84,7 @@
             kDrmPluginEventSessionReclaimed,
             kDrmPluginEventExpirationUpdate,
             kDrmPluginEventKeysChange,
+            kDrmPluginEventSessionLostState,
         };
 
         // Drm keys can be for offline content or for online streaming.
@@ -139,6 +140,8 @@
             kHdcpV2_1,
             // HDCP version 2.2 Type 1.
             kHdcpV2_2,
+            // HDCP version 2.3 Type 1.
+            kHdcpV2_3,
             // No digital output, implicitly secure
             kHdcpNoOutput = 0x7fff
         };
diff --git a/include/android/keycodes.h b/include/android/keycodes.h
index cfd2b40..214559d 100644
--- a/include/android/keycodes.h
+++ b/include/android/keycodes.h
@@ -774,7 +774,8 @@
     AKEYCODE_THUMBS_UP = 286,
     /** Thumbs down key. Apps can use this to let user downvote content. */
     AKEYCODE_THUMBS_DOWN = 287,
-    /** Consumed by system to switch current viewer profile. */
+    /** Used to switch current account that is consuming content.
+     * May be consumed by system to switch current viewer profile. */
     AKEYCODE_PROFILE_SWITCH = 288
 
     // NOTE: If you add a new keycode here you must also add it to several other files.
diff --git a/include/android/multinetwork.h b/include/android/multinetwork.h
index 05d96ff..d31d1f1 100644
--- a/include/android/multinetwork.h
+++ b/include/android/multinetwork.h
@@ -112,15 +112,25 @@
 
 #if __ANDROID_API__ >= 29
 
+/**
+ * Possible values of the flags argument to android_res_nsend and android_res_nquery.
+ * Values are ORed together.
+ */
 enum ResNsendFlags : uint32_t {
-    // Send a single request to a single resolver and fail on timeout or network errors
+    /**
+     * Send a single request to a single resolver and fail on timeout or network errors
+     */
     ANDROID_RESOLV_NO_RETRY = 1 << 0,
 
-    // Do not cache the result of the lookup. The lookup may return a result that is already
-    // in the cache, unless the ANDROID_RESOLV_NO_CACHE_LOOKUP flag is also specified.
+    /**
+     * Do not cache the result of the lookup. The lookup may return a result that is already
+     * in the cache, unless the ANDROID_RESOLV_NO_CACHE_LOOKUP flag is also specified.
+     */
     ANDROID_RESOLV_NO_CACHE_STORE = 1 << 1,
 
-    // Don't lookup the request in cache, do not write back the response into the cache
+    /**
+     * Don't lookup the request in cache.
+     */
     ANDROID_RESOLV_NO_CACHE_LOOKUP = 1 << 2,
 };
 
@@ -136,8 +146,7 @@
  * POSIX error code (see errno.h) if an immediate error occurs.
  */
 int android_res_nquery(net_handle_t network,
-        const char *dname, int ns_class, int ns_type,
-        enum ResNsendFlags flags) __INTRODUCED_IN(29);
+        const char *dname, int ns_class, int ns_type, uint32_t flags) __INTRODUCED_IN(29);
 
 /**
  * Issue the query |msg| on the given |network|.
@@ -148,10 +157,11 @@
  * POSIX error code (see errno.h) if an immediate error occurs.
  */
 int android_res_nsend(net_handle_t network,
-        const uint8_t *msg, size_t msglen, enum ResNsendFlags flags) __INTRODUCED_IN(29);
+        const uint8_t *msg, size_t msglen, uint32_t flags) __INTRODUCED_IN(29);
 
 /**
  * Read a result for the query associated with the |fd| descriptor.
+ * Closes |fd| before returning.
  *
  * Returns:
  *     < 0: negative POSIX error code (see errno.h for possible values). |rcode| is not set.
diff --git a/include/android/sensor.h b/include/android/sensor.h
index 005564d..e9d5c16 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -64,6 +64,7 @@
 #define ASENSOR_RESOLUTION_INVALID     (nanf(""))
 #define ASENSOR_FIFO_COUNT_INVALID     (-1)
 #define ASENSOR_DELAY_INVALID          INT32_MIN
+#define ASENSOR_INVALID                (-1)
 
 /* (Keep in sync with hardware/sensors-base.h and Sensor.java.) */
 
@@ -208,6 +209,35 @@
      */
     ASENSOR_TYPE_HEART_BEAT = 31,
     /**
+     * This sensor type is for delivering additional sensor information aside
+     * from sensor event data.
+     *
+     * Additional information may include:
+     *     - {@link ASENSOR_ADDITIONAL_INFO_INTERNAL_TEMPERATURE}
+     *     - {@link ASENSOR_ADDITIONAL_INFO_SAMPLING}
+     *     - {@link ASENSOR_ADDITIONAL_INFO_SENSOR_PLACEMENT}
+     *     - {@link ASENSOR_ADDITIONAL_INFO_UNTRACKED_DELAY}
+     *     - {@link ASENSOR_ADDITIONAL_INFO_VEC3_CALIBRATION}
+     *
+     * This type will never bind to a sensor. In other words, no sensor in the
+     * sensor list can have the type {@link ASENSOR_TYPE_ADDITIONAL_INFO}.
+     *
+     * If a device supports the sensor additional information feature, it will
+     * report additional information events via {@link ASensorEvent} and will
+     * have {@link ASensorEvent#type} set to
+     * {@link ASENSOR_TYPE_ADDITIONAL_INFO} and {@link ASensorEvent#sensor} set
+     * to the handle of the reporting sensor.
+     *
+     * Additional information reports consist of multiple frames ordered by
+     * {@link ASensorEvent#timestamp}. The first frame in the report will have
+     * a {@link AAdditionalInfoEvent#type} of
+     * {@link ASENSOR_ADDITIONAL_INFO_BEGIN}, and the last frame in the report
+     * will have a {@link AAdditionalInfoEvent#type} of
+     * {@link ASENSOR_ADDITIONAL_INFO_END}.
+     *
+     */
+    ASENSOR_TYPE_ADDITIONAL_INFO = 33,
+    /**
      * {@link ASENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT}
      */
     ASENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT = 34,
@@ -273,6 +303,51 @@
     ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER = 2
 };
 
+/**
+ * Sensor Additional Info Types.
+ *
+ * Used to populate {@link AAdditionalInfoEvent#type}.
+ */
+enum {
+    /** Marks the beginning of additional information frames */
+    ASENSOR_ADDITIONAL_INFO_BEGIN = 0,
+
+    /** Marks the end of additional information frames */
+    ASENSOR_ADDITIONAL_INFO_END = 1,
+
+    /**
+     * Estimation of the delay that is not tracked by sensor timestamps. This
+     * includes delay introduced by sensor front-end filtering, data transport,
+     * etc.
+     * float[2]: delay in seconds, standard deviation of estimated value
+     */
+    ASENSOR_ADDITIONAL_INFO_UNTRACKED_DELAY = 0x10000,
+
+    /** float: Celsius temperature */
+    ASENSOR_ADDITIONAL_INFO_INTERNAL_TEMPERATURE,
+
+    /**
+     * First three rows of a homogeneous matrix, which represents calibration to
+     * a three-element vector raw sensor reading.
+     * float[12]: 3x4 matrix in row major order
+     */
+    ASENSOR_ADDITIONAL_INFO_VEC3_CALIBRATION,
+
+    /**
+     * Location and orientation of sensor element in the device frame: origin is
+     * the geometric center of the mobile device screen surface; the axis
+     * definition corresponds to Android sensor definitions.
+     * float[12]: 3x4 matrix in row major order
+     */
+    ASENSOR_ADDITIONAL_INFO_SENSOR_PLACEMENT,
+
+    /**
+     * float[2]: raw sample period in seconds,
+     *           standard deviation of sampling period
+     */
+    ASENSOR_ADDITIONAL_INFO_SAMPLING,
+};
+
 /*
  * A few useful constants
  */
@@ -341,7 +416,7 @@
     int32_t  handle;
 } ADynamicSensorEvent;
 
-typedef struct {
+typedef struct AAdditionalInfoEvent {
     int32_t type;
     int32_t serial;
     union {
@@ -420,6 +495,7 @@
  * - ASensorEventQueue_hasEvents()
  * - ASensorEventQueue_getEvents()
  * - ASensorEventQueue_setEventRate()
+ * - ASensorEventQueue_requestAdditionalInfoEvents()
  */
 typedef struct ASensorEventQueue ASensorEventQueue;
 
@@ -444,6 +520,7 @@
  * - ASensor_getStringType()
  * - ASensor_getReportingMode()
  * - ASensor_isWakeUpSensor()
+ * - ASensor_getHandle()
  */
 typedef struct ASensor ASensor;
 /**
@@ -703,6 +780,29 @@
  */
 ssize_t ASensorEventQueue_getEvents(ASensorEventQueue* queue, ASensorEvent* events, size_t count);
 
+#if __ANDROID_API__ >= __ANDROID_API_Q__
+/**
+ * Request that {@link ASENSOR_TYPE_ADDITIONAL_INFO} events to be delivered on
+ * the given {@link ASensorEventQueue}.
+ *
+ * Sensor data events are always delivered to the {@ASensorEventQueue}.
+ *
+ * The {@link ASENSOR_TYPE_ADDITIONAL_INFO} events will be returned through
+ * {@link ASensorEventQueue_getEvents}. The client is responsible for checking
+ * {@link ASensorEvent#type} to determine the event type prior to handling of
+ * the event.
+ *
+ * The client must be tolerant of any value for
+ * {@link AAdditionalInfoEvent#type}, as new values may be defined in the future
+ * and may delivered to the client.
+ *
+ * \param queue {@link ASensorEventQueue} to configure
+ * \param enable true to request {@link ASENSOR_TYPE_ADDITIONAL_INFO} events,
+ *        false to stop receiving events
+ * \return 0 on success or a negative error code on failure
+ */
+int ASensorEventQueue_requestAdditionalInfoEvents(ASensorEventQueue* queue, bool enable);
+#endif /* __ANDROID_API__ >= __ANDRDOID_API_Q__ */
 
 /*****************************************************************************/
 
@@ -785,6 +885,24 @@
 int ASensor_getHighestDirectReportRateLevel(ASensor const* sensor) __INTRODUCED_IN(26);
 #endif /* __ANDROID_API__ >= 26 */
 
+#if __ANDROID_API__ >= __ANDROID_API_Q__
+/**
+ * Returns the sensor's handle.
+ *
+ * The handle identifies the sensor within the system and is included in the
+ * {@link ASensorEvent#sensor} field of sensor events, including those sent with type
+ * {@link ASENSOR_TYPE_ADDITIONAL_INFO}.
+ *
+ * A sensor's handle is able to be used to map {@link ASENSOR_TYPE_ADDITIONAL_INFO} events to the
+ * sensor that generated the event.
+ *
+ * It is important to note that the value returned by {@link ASensor_getHandle} is not the same as
+ * the value returned by the Java API {@link android.hardware.Sensor#getId} and no mapping exists
+ * between the values.
+ */
+int ASensor_getHandle(ASensor const* sensor) __INTRODUCED_IN(__ANDROID_API_Q__);
+#endif /* __ANDROID_API__ >= ANDROID_API_Q__ */
+
 #ifdef __cplusplus
 };
 #endif
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
new file mode 100644
index 0000000..0573187
--- /dev/null
+++ b/include/android/surface_control.h
@@ -0,0 +1,345 @@
+/*
+ * Copyright 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.
+ */
+
+/**
+ * @addtogroup NativeActivity Native Activity
+ * @{
+ */
+
+/**
+ * @file surface_control.h
+ */
+
+#ifndef ANDROID_SURFACE_CONTROL_H
+#define ANDROID_SURFACE_CONTROL_H
+
+#include <sys/cdefs.h>
+
+#include <android/hardware_buffer.h>
+#include <android/hdr_metadata.h>
+#include <android/native_window.h>
+
+__BEGIN_DECLS
+
+#if __ANDROID_API__ >= 29
+
+struct ASurfaceControl;
+
+/**
+ * The SurfaceControl API can be used to provide a heirarchy of surfaces for
+ * composition to the system compositor. ASurfaceControl represents a content node in
+ * this heirarchy.
+ */
+typedef struct ASurfaceControl ASurfaceControl;
+
+/*
+ * Creates an ASurfaceControl with either ANativeWindow or an ASurfaceControl as its parent.
+ * |debug_name| is a debug name associated with this surface. It can be used to
+ * identify this surface in the SurfaceFlinger's layer tree. It must not be
+ * null.
+ *
+ * The caller takes ownership of the ASurfaceControl returned and must release it
+ * using ASurfaceControl_release below.
+ */
+ASurfaceControl* ASurfaceControl_createFromWindow(ANativeWindow* parent, const char* debug_name)
+                                                  __INTRODUCED_IN(29);
+
+ASurfaceControl* ASurfaceControl_create(ASurfaceControl* parent, const char* debug_name)
+                                        __INTRODUCED_IN(29);
+
+/**
+ * Releases the |surface_control| object. After releasing the ASurfaceControl the caller no longer
+ * has ownership of the AsurfaceControl. The surface and it's children may remain on display as long
+ * as their parent remains on display.
+ */
+void ASurfaceControl_release(ASurfaceControl* surface_control) __INTRODUCED_IN(29);
+
+struct ASurfaceTransaction;
+
+/**
+ * ASurfaceTransaction is a collection of updates to the surface tree that must
+ * be applied atomically.
+ */
+typedef struct ASurfaceTransaction ASurfaceTransaction;
+
+/**
+ * The caller takes ownership of the transaction and must release it using
+ * ASurfaceControl_delete below.
+ */
+ASurfaceTransaction* ASurfaceTransaction_create() __INTRODUCED_IN(29);
+
+/**
+ * Destroys the |transaction| object.
+ */
+void ASurfaceTransaction_delete(ASurfaceTransaction* transaction) __INTRODUCED_IN(29);
+
+/**
+ * Applies the updates accumulated in |transaction|.
+ *
+ * Note that the transaction is guaranteed to be applied atomically. The
+ * transactions which are applied on the same thread are also guaranteed to be
+ * applied in order.
+ */
+void ASurfaceTransaction_apply(ASurfaceTransaction* transaction) __INTRODUCED_IN(29);
+
+/**
+ * An opaque handle returned during a callback that can be used to query general stats and stats for
+ * surfaces which were either removed or for which buffers were updated after this transaction was
+ * applied.
+ */
+typedef struct ASurfaceTransactionStats ASurfaceTransactionStats;
+
+/**
+ * Since the transactions are applied asynchronously, the
+ * ASurfaceTransaction_OnComplete callback can be used to be notified when a frame
+ * including the updates in a transaction was presented.
+ *
+ * |context| is the optional context provided by the client that is passed into
+ * the callback.
+ *
+ * |stats| is an opaque handle that can be passed to ASurfaceTransactionStats functions to query
+ * information about the transaction. The handle is only valid during the the callback.
+ *
+ * THREADING
+ * The transaction completed callback can be invoked on any thread.
+ */
+typedef void (*ASurfaceTransaction_OnComplete)(void* context, ASurfaceTransactionStats* stats)
+                                               __INTRODUCED_IN(29);
+
+/**
+ * Returns the timestamp of when the frame was latched by the framework. Once a frame is
+ * latched by the framework, it is presented at the following hardware vsync.
+ */
+int64_t ASurfaceTransactionStats_getLatchTime(ASurfaceTransactionStats* surface_transaction_stats)
+                                              __INTRODUCED_IN(29);
+
+/**
+ * Returns a sync fence that signals when the transaction has been presented.
+ * The recipient of the callback takes ownership of the fence and is responsible for closing
+ * it.
+ */
+int ASurfaceTransactionStats_getPresentFenceFd(ASurfaceTransactionStats* surface_transaction_stats)
+                                               __INTRODUCED_IN(29);
+
+/**
+ * |outASurfaceControls| returns an array of ASurfaceControl pointers that were updated during the
+ * transaction. Stats for the surfaces can be queried through ASurfaceTransactionStats functions.
+ * When the client is done using the array, it must release it by calling
+ * ASurfaceTransactionStats_releaseASurfaceControls.
+ *
+ * |outASurfaceControlsSize| returns the size of the ASurfaceControls array.
+ */
+void ASurfaceTransactionStats_getASurfaceControls(ASurfaceTransactionStats* surface_transaction_stats,
+                                                  ASurfaceControl*** outASurfaceControls,
+                                                  size_t* outASurfaceControlsSize)
+                                                  __INTRODUCED_IN(29);
+/**
+ * Releases the array of ASurfaceControls that were returned by
+ * ASurfaceTransactionStats_getASurfaceControls.
+ */
+void ASurfaceTransactionStats_releaseASurfaceControls(ASurfaceControl** surface_controls)
+                                                      __INTRODUCED_IN(29);
+
+/**
+ * Returns the timestamp of when the CURRENT buffer was acquired. A buffer is considered
+ * acquired when its acquire_fence_fd has signaled. A buffer cannot be latched or presented until
+ * it is acquired. If no acquire_fence_fd was provided, this timestamp will be set to -1.
+ */
+int64_t ASurfaceTransactionStats_getAcquireTime(ASurfaceTransactionStats* surface_transaction_stats,
+                                                ASurfaceControl* surface_control)
+                                                __INTRODUCED_IN(29);
+
+/**
+ * The returns the fence used to signal the release of the PREVIOUS buffer set on
+ * this surface. If this fence is valid (>=0), the PREVIOUS buffer has not yet been released and the
+ * fence will signal when the PREVIOUS buffer has been released. If the fence is -1 , the PREVIOUS
+ * buffer is already released. The recipient of the callback takes ownership of the
+ * previousReleaseFenceFd and is responsible for closing it.
+ *
+ * Each time a buffer is set through ASurfaceTransaction_setBuffer()/_setCachedBuffer() on a
+ * transaction which is applied, the framework takes a ref on this buffer. The framework treats the
+ * addition of a buffer to a particular surface as a unique ref. When a transaction updates or
+ * removes a buffer from a surface, or removes the surface itself from the tree, this ref is
+ * guaranteed to be released in the OnComplete callback for this transaction. The
+ * ASurfaceControlStats provided in the callback for this surface may contain an optional fence
+ * which must be signaled before the ref is assumed to be released.
+ *
+ * The client must ensure that all pending refs on a buffer are released before attempting to reuse
+ * this buffer, otherwise synchronization errors may occur.
+ */
+int ASurfaceTransactionStats_getPreviousReleaseFenceFd(
+                                                ASurfaceTransactionStats* surface_transaction_stats,
+                                                ASurfaceControl* surface_control)
+                                                __INTRODUCED_IN(29);
+
+/**
+ * Sets the callback that will be invoked when the updates from this transaction
+ * are presented. For details on the callback semantics and data, see the
+ * comments on the ASurfaceTransaction_OnComplete declaration above.
+ */
+void ASurfaceTransaction_setOnComplete(ASurfaceTransaction* transaction, void* context,
+                                       ASurfaceTransaction_OnComplete func) __INTRODUCED_IN(29);
+
+/**
+ * Reparents the |surface_control| from its old parent to the |new_parent| surface control.
+ * Any children of the* reparented |surface_control| will remain children of the |surface_control|.
+ *
+ * The |new_parent| can be null. Surface controls with a null parent do not appear on the display.
+ */
+void ASurfaceTransaction_reparent(ASurfaceTransaction* transaction,
+                                  ASurfaceControl* surface_control, ASurfaceControl* new_parent)
+                                  __INTRODUCED_IN(29);
+
+/* Parameter for ASurfaceTransaction_setVisibility */
+enum {
+    ASURFACE_TRANSACTION_VISIBILITY_HIDE = 0,
+    ASURFACE_TRANSACTION_VISIBILITY_SHOW = 1,
+};
+/**
+ * Updates the visibility of |surface_control|. If show is set to
+ * ASURFACE_TRANSACTION_VISIBILITY_HIDE, the |surface_control| and all surfaces in its subtree will
+ * be hidden.
+ */
+void ASurfaceTransaction_setVisibility(ASurfaceTransaction* transaction,
+                                       ASurfaceControl* surface_control, int8_t visibility)
+                                       __INTRODUCED_IN(29);
+
+/**
+ * Updates the z order index for |surface_control|. Note that the z order for a surface
+ * is relative to other surfaces which are siblings of this surface. The behavior of sibilings with
+ * the same z order is undefined.
+ *
+ * Z orders may be from MIN_INT32 to MAX_INT32. A layer's default z order index is 0.
+ */
+void ASurfaceTransaction_setZOrder(ASurfaceTransaction* transaction,
+                                   ASurfaceControl* surface_control, int32_t z_order)
+                                   __INTRODUCED_IN(29);
+
+/**
+ * Updates the AHardwareBuffer displayed for |surface_control|. If not -1, the
+ * acquire_fence_fd should be a file descriptor that is signaled when all pending work
+ * for the buffer is complete and the buffer can be safely read.
+ *
+ * The frameworks takes ownership of the |acquire_fence_fd| passed and is responsible
+ * for closing it.
+ */
+void ASurfaceTransaction_setBuffer(ASurfaceTransaction* transaction,
+                                   ASurfaceControl* surface_control, AHardwareBuffer* buffer,
+                                   int acquire_fence_fd = -1) __INTRODUCED_IN(29);
+
+/**
+ * Updates the color for |surface_control|.  This will make the background color for the
+ * ASurfaceControl visible in transparent regions of the surface.  Colors |r|, |g|,
+ * and |b| must be within the range that is valid for |dataspace|.  |dataspace| and |alpha|
+ * will be the dataspace and alpha set for the background color layer.
+ */
+void ASurfaceTransaction_setColor(ASurfaceTransaction* transaction,
+                                  ASurfaceControl* surface_control, float r, float g, float b,
+                                  float alpha, ADataSpace dataspace)
+                                  __INTRODUCED_IN(29);
+
+/**
+ * |source| the sub-rect within the buffer's content to be rendered inside the surface's area
+ * The surface's source rect is clipped by the bounds of its current buffer. The source rect's width
+ * and height must be > 0.
+ *
+ * |destination| specifies the rect in the parent's space where this surface will be drawn. The post
+ * source rect bounds are scaled to fit the destination rect. The surface's destination rect is
+ * clipped by the bounds of its parent. The destination rect's width and height must be > 0.
+ *
+ * |transform| the transform applied after the source rect is applied to the buffer. This parameter
+ * should be set to 0 for no transform. To specify a transfrom use the NATIVE_WINDOW_TRANSFORM_*
+ * enum.
+ */
+void ASurfaceTransaction_setGeometry(ASurfaceTransaction* transaction,
+                                     ASurfaceControl* surface_control, const ARect& source,
+                                     const ARect& destination, int32_t transform)
+                                     __INTRODUCED_IN(29);
+
+
+/* Parameter for ASurfaceTransaction_setBufferTransparency */
+enum {
+    ASURFACE_TRANSACTION_TRANSPARENCY_TRANSPARENT = 0,
+    ASURFACE_TRANSACTION_TRANSPARENCY_TRANSLUCENT = 1,
+    ASURFACE_TRANSACTION_TRANSPARENCY_OPAQUE = 2,
+};
+/**
+ * Updates whether the content for the buffer associated with this surface is
+ * completely opaque. If true, every pixel of content inside the buffer must be
+ * opaque or visual errors can occur.
+ */
+void ASurfaceTransaction_setBufferTransparency(ASurfaceTransaction* transaction,
+                                               ASurfaceControl* surface_control,
+                                               int8_t transparency)
+                                               __INTRODUCED_IN(29);
+
+/**
+ * Updates the region for the content on this surface updated in this
+ * transaction. If unspecified, the complete surface is assumed to be damaged.
+ */
+void ASurfaceTransaction_setDamageRegion(ASurfaceTransaction* transaction,
+                                         ASurfaceControl* surface_control, const ARect rects[],
+                                         uint32_t count) __INTRODUCED_IN(29);
+
+/**
+ * Specifies a desiredPresentTime for the transaction. The framework will try to present
+ * the transaction at or after the time specified.
+ *
+ * Transactions will not be presented until all of their acquire fences have signaled even if the
+ * app requests an earlier present time.
+ *
+ * If an earlier transaction has a desired present time of x, and a later transaction has a desired
+ * present time that is before x, the later transaction will not preempt the earlier transaction.
+ */
+void ASurfaceTransaction_setDesiredPresentTime(ASurfaceTransaction* transaction,
+                                               int64_t desiredPresentTime) __INTRODUCED_IN(29);
+
+/**
+ * Sets the alpha for the buffer. It uses a premultiplied blending.
+ *
+ * The |alpha| must be between 0.0 and 1.0.
+ */
+void ASurfaceTransaction_setBufferAlpha(ASurfaceTransaction* transaction,
+                                        ASurfaceControl* surface_control, float alpha)
+                                        __INTRODUCED_IN(29);
+
+/*
+ * SMPTE ST 2086 "Mastering Display Color Volume" static metadata
+ *
+ * When |metadata| is set to null, the framework does not use any smpte2086 metadata when rendering
+ * the surface's buffer.
+ */
+void ASurfaceTransaction_setHdrMetadata_smpte2086(ASurfaceTransaction* transaction,
+                                                  ASurfaceControl* surface_control,
+                                                  struct AHdrMetadata_smpte2086* metadata)
+                                                  __INTRODUCED_IN(29);
+
+/*
+ * Sets the CTA 861.3 "HDR Static Metadata Extension" static metadata on a surface.
+ *
+ * When |metadata| is set to null, the framework does not use any cta861.3 metadata when rendering
+ * the surface's buffer.
+ */
+void ASurfaceTransaction_setHdrMetadata_cta861_3(ASurfaceTransaction* transaction,
+                                                 ASurfaceControl* surface_control,
+                                                 struct AHdrMetadata_cta861_3* metadata)
+                                                 __INTRODUCED_IN(29);
+
+#endif // __ANDROID_API__ >= 29
+
+__END_DECLS
+
+#endif // ANDROID_SURFACE_CONTROL_H
diff --git a/include/input/Input.h b/include/input/Input.h
index 2c4a511..805957a 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -241,6 +241,11 @@
     DEEP_PRESS = 2,
 };
 
+/**
+ * String representation of MotionClassification
+ */
+const char* motionClassificationToString(MotionClassification classification);
+
 /*
  * Pointer coordinate data.
  */
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index dddbfb0..63606e5 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -105,8 +105,9 @@
             int32_t flags;
             int32_t metaState;
             int32_t buttonState;
+            MotionClassification classification; // base type: uint8_t
+            uint8_t empty2[3];
             int32_t edgeFlags;
-            uint32_t empty2;
             nsecs_t downTime __attribute__((aligned(8)));
             float xOffset;
             float yOffset;
@@ -271,6 +272,7 @@
             int32_t edgeFlags,
             int32_t metaState,
             int32_t buttonState,
+            MotionClassification classification,
             float xOffset,
             float yOffset,
             float xPrecision,
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/arect/Android.bp b/libs/arect/Android.bp
index a20154f..ad8287c 100644
--- a/libs/arect/Android.bp
+++ b/libs/arect/Android.bp
@@ -25,4 +25,9 @@
     host_supported: true,
     vendor_available: true,
     export_include_dirs: ["include"],
+    target: {
+        windows: {
+            enabled: true,
+        },
+    },
 }
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index f6cc3af..96ee295 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -86,6 +86,10 @@
 class BBinder::Extras
 {
 public:
+    // unlocked objects
+    bool mRequestingSid = false;
+
+    // for below objects
     Mutex mLock;
     BpBinder::ObjectManager mObjects;
 };
@@ -163,19 +167,8 @@
     const void* objectID, void* object, void* cleanupCookie,
     object_cleanup_func func)
 {
-    Extras* e = mExtras.load(std::memory_order_acquire);
-
-    if (!e) {
-        e = new Extras;
-        Extras* expected = nullptr;
-        if (!mExtras.compare_exchange_strong(expected, e,
-                                             std::memory_order_release,
-                                             std::memory_order_acquire)) {
-            delete e;
-            e = expected;  // Filled in by CAS
-        }
-        if (e == nullptr) return; // out of memory
-    }
+    Extras* e = getOrCreateExtras();
+    if (!e) return; // out of memory
 
     AutoMutex _l(e->mLock);
     e->mObjects.attach(objectID, object, cleanupCookie, func);
@@ -204,6 +197,30 @@
     return this;
 }
 
+bool BBinder::isRequestingSid()
+{
+    Extras* e = mExtras.load(std::memory_order_acquire);
+
+    return e && e->mRequestingSid;
+}
+
+void BBinder::setRequestingSid(bool requestingSid)
+{
+    Extras* e = mExtras.load(std::memory_order_acquire);
+
+    if (!e) {
+        // default is false. Most things don't need sids, so avoiding allocations when possible.
+        if (!requestingSid) {
+            return;
+        }
+
+        e = getOrCreateExtras();
+        if (!e) return; // out of memory
+    }
+
+    e->mRequestingSid = true;
+}
+
 BBinder::~BBinder()
 {
     Extras* e = mExtras.load(std::memory_order_relaxed);
@@ -267,6 +284,25 @@
     }
 }
 
+BBinder::Extras* BBinder::getOrCreateExtras()
+{
+    Extras* e = mExtras.load(std::memory_order_acquire);
+
+    if (!e) {
+        e = new Extras;
+        Extras* expected = nullptr;
+        if (!mExtras.compare_exchange_strong(expected, e,
+                                             std::memory_order_release,
+                                             std::memory_order_acquire)) {
+            delete e;
+            e = expected;  // Filled in by CAS
+        }
+        if (e == nullptr) return nullptr; // out of memory
+    }
+
+    return e;
+}
+
 // ---------------------------------------------------------------------------
 
 enum {
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 8df83f1..9a561cb 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -23,7 +23,9 @@
 #include <binder/BpBinder.h>
 #include <binder/TextOutput.h>
 
+#include <android-base/macros.h>
 #include <cutils/sched_policy.h>
+#include <utils/CallStack.h>
 #include <utils/Log.h>
 #include <utils/SystemClock.h>
 #include <utils/threads.h>
@@ -86,7 +88,8 @@
     "BR_FINISHED",
     "BR_DEAD_BINDER",
     "BR_CLEAR_DEATH_NOTIFICATION_DONE",
-    "BR_FAILED_REPLY"
+    "BR_FAILED_REPLY",
+    "BR_TRANSACTION_SEC_CTX",
 };
 
 static const char *kCommandStrings[] = {
@@ -363,6 +366,11 @@
     return mCallingPid;
 }
 
+const char* IPCThreadState::getCallingSid() const
+{
+    return mCallingSid;
+}
+
 uid_t IPCThreadState::getCallingUid() const
 {
     return mCallingUid;
@@ -370,6 +378,7 @@
 
 int64_t IPCThreadState::clearCallingIdentity()
 {
+    // ignore mCallingSid for legacy reasons
     int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid;
     clearCaller();
     return token;
@@ -440,12 +449,14 @@
 void IPCThreadState::restoreCallingIdentity(int64_t token)
 {
     mCallingUid = (int)(token>>32);
+    mCallingSid = nullptr;  // not enough data to restore
     mCallingPid = (int)token;
 }
 
 void IPCThreadState::clearCaller()
 {
     mCallingPid = getpid();
+    mCallingSid = nullptr;  // expensive to lookup
     mCallingUid = getuid();
 }
 
@@ -661,6 +672,16 @@
     }
 
     if ((flags & TF_ONE_WAY) == 0) {
+        if (UNLIKELY(mCallRestriction != ProcessState::CallRestriction::NONE)) {
+            if (mCallRestriction == ProcessState::CallRestriction::ERROR_IF_NOT_ONEWAY) {
+                ALOGE("Process making non-oneway call but is restricted.");
+                CallStack::logStack("non-oneway call", CallStack::getCurrent(10).get(),
+                    ANDROID_LOG_ERROR);
+            } else /* FATAL_IF_NOT_ONEWAY */ {
+                LOG_ALWAYS_FATAL("Process may not make oneway calls.");
+            }
+        }
+
         #if 0
         if (code == 4) { // relayout
             ALOGI(">>>>>> CALLING transaction 4");
@@ -783,7 +804,8 @@
       mWorkSource(kUnsetWorkSource),
       mPropagateWorkSource(false),
       mStrictModePolicy(0),
-      mLastTransactionBinderFlags(0)
+      mLastTransactionBinderFlags(0),
+      mCallRestriction(mProcess->mCallRestriction)
 {
     pthread_setspecific(gTLS, this);
     clearCaller();
@@ -1122,10 +1144,19 @@
         }
         break;
 
+    case BR_TRANSACTION_SEC_CTX:
     case BR_TRANSACTION:
         {
-            binder_transaction_data tr;
-            result = mIn.read(&tr, sizeof(tr));
+            binder_transaction_data_secctx tr_secctx;
+            binder_transaction_data& tr = tr_secctx.transaction_data;
+
+            if (cmd == (int) BR_TRANSACTION_SEC_CTX) {
+                result = mIn.read(&tr_secctx, sizeof(tr_secctx));
+            } else {
+                result = mIn.read(&tr, sizeof(tr));
+                tr_secctx.secctx = 0;
+            }
+
             ALOG_ASSERT(result == NO_ERROR,
                 "Not enough command data for brTRANSACTION");
             if (result != NO_ERROR) break;
@@ -1141,6 +1172,7 @@
                 tr.offsets_size/sizeof(binder_size_t), freeBuffer, this);
 
             const pid_t origPid = mCallingPid;
+            const char* origSid = mCallingSid;
             const uid_t origUid = mCallingUid;
             const int32_t origStrictModePolicy = mStrictModePolicy;
             const int32_t origTransactionBinderFlags = mLastTransactionBinderFlags;
@@ -1153,10 +1185,12 @@
             clearPropagateWorkSource();
 
             mCallingPid = tr.sender_pid;
+            mCallingSid = reinterpret_cast<const char*>(tr_secctx.secctx);
             mCallingUid = tr.sender_euid;
             mLastTransactionBinderFlags = tr.flags;
 
-            //ALOGI(">>>> TRANSACT from pid %d uid %d\n", mCallingPid, mCallingUid);
+            // ALOGI(">>>> TRANSACT from pid %d sid %s uid %d\n", mCallingPid,
+            //    (mCallingSid ? mCallingSid : "<N/A>"), mCallingUid);
 
             Parcel reply;
             status_t error;
@@ -1188,8 +1222,8 @@
             }
 
             mIPCThreadStateBase->popCurrentState();
-            //ALOGI("<<<< TRANSACT from pid %d restore pid %d uid %d\n",
-            //     mCallingPid, origPid, origUid);
+            //ALOGI("<<<< TRANSACT from pid %d restore pid %d sid %s uid %d\n",
+            //     mCallingPid, origPid, (origSid ? origSid : "<N/A>"), origUid);
 
             if ((tr.flags & TF_ONE_WAY) == 0) {
                 LOG_ONEWAY("Sending reply to %d!", mCallingPid);
@@ -1200,6 +1234,7 @@
             }
 
             mCallingPid = origPid;
+            mCallingSid = origSid;
             mCallingUid = origUid;
             mStrictModePolicy = origStrictModePolicy;
             mLastTransactionBinderFlags = origTransactionBinderFlags;
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index d285030..f779d6e 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -220,7 +220,7 @@
     }
 
     if (binder != nullptr) {
-        IBinder *local = binder->localBinder();
+        BBinder *local = binder->localBinder();
         if (!local) {
             BpBinder *proxy = binder->remoteBinder();
             if (proxy == nullptr) {
@@ -232,6 +232,9 @@
             obj.handle = handle;
             obj.cookie = 0;
         } else {
+            if (local->isRequestingSid()) {
+                obj.flags |= FLAT_BINDER_FLAG_TXN_SECURITY_CTX;
+            }
             obj.hdr.type = BINDER_TYPE_BINDER;
             obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
             obj.cookie = reinterpret_cast<uintptr_t>(local);
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 53f8ddd..79db0cb 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -181,8 +181,20 @@
         mBinderContextCheckFunc = checkFunc;
         mBinderContextUserData = userData;
 
-        int dummy = 0;
-        status_t result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy);
+        flat_binder_object obj {
+            .flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX,
+        };
+
+        status_t result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR_EXT, &obj);
+
+        // fallback to original method
+        if (result != 0) {
+            android_errorWriteLog(0x534e4554, "121035042");
+
+            int dummy = 0;
+            result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy);
+        }
+
         if (result == 0) {
             mManagesContexts = true;
         } else if (result == -1) {
@@ -234,6 +246,12 @@
     return count;
 }
 
+void ProcessState::setCallRestriction(CallRestriction restriction) {
+    LOG_ALWAYS_FATAL_IF(IPCThreadState::selfOrNull(), "Call restrictions must be set before the threadpool is started.");
+
+    mCallRestriction = restriction;
+}
+
 ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle)
 {
     const size_t N=mHandleToObject.size();
@@ -426,6 +444,7 @@
     , mBinderContextUserData(nullptr)
     , mThreadPoolStarted(false)
     , mThreadPoolSeq(1)
+    , mCallRestriction(CallRestriction::NONE)
 {
     if (mDriverFD >= 0) {
         // mmap the binder, providing a chunk of virtual address space to receive transactions.
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/include/binder/Binder.h b/libs/binder/include/binder/Binder.h
index c251468..cf3ef84 100644
--- a/libs/binder/include/binder/Binder.h
+++ b/libs/binder/include/binder/Binder.h
@@ -60,6 +60,10 @@
 
     virtual BBinder*    localBinder();
 
+    bool                isRequestingSid();
+    // This must be called before the object is sent to another process. Not thread safe.
+    void                setRequestingSid(bool requestSid);
+
 protected:
     virtual             ~BBinder();
 
@@ -75,6 +79,8 @@
 
     class Extras;
 
+    Extras*             getOrCreateExtras();
+
     std::atomic<Extras*> mExtras;
             void*       mReserved0;
 };
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 26e8c0b..614b0b3 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -42,6 +42,11 @@
             status_t            clearLastError();
 
             pid_t               getCallingPid() const;
+            // nullptr if unavailable
+            //
+            // this can't be restored once it's cleared, and it does not return the
+            // context of the current process when not in a binder call.
+            const char*         getCallingSid() const;
             uid_t               getCallingUid() const;
 
             void                setStrictModePolicy(int32_t policy);
@@ -64,6 +69,7 @@
             int32_t             getLastTransactionBinderFlags() const;
 
             int64_t             clearCallingIdentity();
+            // Restores PID/UID (not SID)
             void                restoreCallingIdentity(int64_t token);
             
             int                 setupPolling(int* fd);
@@ -174,6 +180,7 @@
             Parcel              mOut;
             status_t            mLastError;
             pid_t               mCallingPid;
+            const char*         mCallingSid;
             uid_t               mCallingUid;
             // The UID of the process who is responsible for this transaction.
             // This is used for resource attribution.
@@ -183,6 +190,8 @@
             int32_t             mStrictModePolicy;
             int32_t             mLastTransactionBinderFlags;
             IPCThreadStateBase  *mIPCThreadStateBase;
+
+            ProcessState::CallRestriction mCallRestriction;
 };
 
 }; // namespace android
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index cd151ee..240b708 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -27,8 +27,8 @@
 #include <utils/String16.h>
 #include <utils/Vector.h>
 #include <utils/Flattenable.h>
-#include <linux/android/binder.h>
 
+#include <binder/binder_kernel.h>
 #include <binder/IInterface.h>
 #include <binder/Parcelable.h>
 #include <binder/Map.h>
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 3712c84..224cb36 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -77,6 +77,18 @@
 
             ssize_t             getKernelReferences(size_t count, uintptr_t* buf);
 
+            enum class CallRestriction {
+                // all calls okay
+                NONE,
+                // log when calls are blocking
+                ERROR_IF_NOT_ONEWAY,
+                // abort process on blocking calls
+                FATAL_IF_NOT_ONEWAY,
+            };
+            // Sets calling restrictions for all transactions in this process. This must be called
+            // before any threads are spawned.
+            void setCallRestriction(CallRestriction restriction);
+
 private:
     friend class IPCThreadState;
     
@@ -123,6 +135,8 @@
             String8             mRootDir;
             bool                mThreadPoolStarted;
     volatile int32_t            mThreadPoolSeq;
+
+            CallRestriction     mCallRestriction;
 };
     
 }; // namespace android
diff --git a/libs/binder/include/binder/binder_kernel.h b/libs/binder/include/binder/binder_kernel.h
new file mode 100644
index 0000000..c7f6b4b
--- /dev/null
+++ b/libs/binder/include/binder/binder_kernel.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef ANDROID_BINDER_KERNEL_H
+#define ANDROID_BINDER_KERNEL_H
+
+#include <linux/android/binder.h>
+
+/**
+ * This file exists because the uapi kernel headers in bionic are built
+ * from upstream kernel headers only, and not all of the hwbinder kernel changes
+ * have made it upstream yet. Therefore, the modifications to the
+ * binder header are added locally in this file.
+ */
+
+enum {
+        FLAT_BINDER_FLAG_TXN_SECURITY_CTX = 0x1000,
+};
+
+#define BINDER_SET_CONTEXT_MGR_EXT      _IOW('b', 13, struct flat_binder_object)
+
+struct binder_transaction_data_secctx {
+        struct binder_transaction_data transaction_data;
+        binder_uintptr_t secctx;
+};
+
+enum {
+        BR_TRANSACTION_SEC_CTX = _IOR('r', 2,
+                              struct binder_transaction_data_secctx),
+};
+
+#endif // ANDROID_BINDER_KERNEL_H
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index 05655c1..0826544 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",
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/cputimeinstate/Android.bp b/libs/cputimeinstate/Android.bp
new file mode 100644
index 0000000..28cb138
--- /dev/null
+++ b/libs/cputimeinstate/Android.bp
@@ -0,0 +1,30 @@
+cc_library {
+    name: "libtimeinstate",
+    srcs: ["cputimeinstate.cpp"],
+    shared_libs: [
+        "libbase",
+        "libbpf",
+        "libbpf_android",
+        "liblog",
+        "libnetdutils"
+    ],
+    cflags: [
+        "-Werror",
+        "-Wall",
+        "-Wextra",
+    ],
+}
+
+cc_test {
+    name: "libtimeinstate_test",
+    srcs: ["testtimeinstate.cpp"],
+    shared_libs: [
+        "libtimeinstate",
+    ],
+    cflags: [
+        "-Werror",
+        "-Wall",
+        "-Wextra",
+    ],
+}
+
diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp
new file mode 100644
index 0000000..4cddf94
--- /dev/null
+++ b/libs/cputimeinstate/cputimeinstate.cpp
@@ -0,0 +1,262 @@
+/*
+ * 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 "libtimeinstate"
+
+#include "cputimeinstate.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <mutex>
+#include <set>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <bpf/BpfMap.h>
+#include <libbpf.h>
+#include <log/log.h>
+
+#define BPF_FS_PATH "/sys/fs/bpf/"
+
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+namespace android {
+namespace bpf {
+
+typedef struct {
+    uint32_t uid;
+    uint32_t freq;
+} time_key_t;
+
+typedef struct {
+    uint64_t ar[100];
+} val_t;
+
+static std::mutex gInitializedMutex;
+static bool gInitialized = false;
+static uint32_t gNPolicies = 0;
+static std::vector<std::vector<uint32_t>> gPolicyFreqs;
+static std::vector<std::vector<uint32_t>> gPolicyCpus;
+static std::set<uint32_t> gAllFreqs;
+static unique_fd gMapFd;
+
+static bool readNumbersFromFile(const std::string &path, std::vector<uint32_t> *out) {
+    std::string data;
+
+    if (!android::base::ReadFileToString(path, &data)) {
+        ALOGD("Failed to read file %s", path.c_str());
+        return false;
+    }
+
+    auto strings = android::base::Split(data, " \n");
+    for (const auto &s : strings) {
+        if (s.empty()) continue;
+        uint32_t n;
+        if (!android::base::ParseUint(s, &n)) {
+            ALOGD("Failed to parse file %s", path.c_str());
+            return false;
+        }
+        out->emplace_back(n);
+    }
+    return true;
+}
+
+static int isPolicyFile(const struct dirent *d) {
+    return android::base::StartsWith(d->d_name, "policy");
+}
+
+static int comparePolicyFiles(const struct dirent **d1, const struct dirent **d2) {
+    uint32_t policyN1, policyN2;
+    if (sscanf((*d1)->d_name, "policy%" SCNu32 "", &policyN1) != 1 ||
+        sscanf((*d2)->d_name, "policy%" SCNu32 "", &policyN2) != 1)
+        return 0;
+    return policyN1 - policyN2;
+}
+
+static bool initGlobals() {
+    std::lock_guard<std::mutex> guard(gInitializedMutex);
+    if (gInitialized) return true;
+
+    struct dirent **dirlist;
+    const char basepath[] = "/sys/devices/system/cpu/cpufreq";
+    int ret = scandir(basepath, &dirlist, isPolicyFile, comparePolicyFiles);
+    if (ret == -1) return false;
+    gNPolicies = ret;
+
+    std::vector<std::string> policyFileNames;
+    for (uint32_t i = 0; i < gNPolicies; ++i) {
+        policyFileNames.emplace_back(dirlist[i]->d_name);
+        free(dirlist[i]);
+    }
+    free(dirlist);
+
+    for (const auto &policy : policyFileNames) {
+        std::vector<uint32_t> freqs;
+        for (const auto &name : {"available", "boost"}) {
+            std::string path =
+                    StringPrintf("%s/%s/scaling_%s_frequencies", basepath, policy.c_str(), name);
+            if (!readNumbersFromFile(path, &freqs)) return false;
+        }
+        std::sort(freqs.begin(), freqs.end());
+        gPolicyFreqs.emplace_back(freqs);
+
+        for (auto freq : freqs) gAllFreqs.insert(freq);
+
+        std::vector<uint32_t> cpus;
+        std::string path = StringPrintf("%s/%s/%s", basepath, policy.c_str(), "related_cpus");
+        if (!readNumbersFromFile(path, &cpus)) return false;
+        gPolicyCpus.emplace_back(cpus);
+    }
+
+    gMapFd = unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_times")};
+    if (gMapFd < 0) return false;
+
+    gInitialized = true;
+    return true;
+}
+
+static bool attachTracepointProgram(const std::string &eventType, const std::string &eventName) {
+    std::string path = StringPrintf(BPF_FS_PATH "prog_time_in_state_tracepoint_%s_%s",
+                                    eventType.c_str(), eventName.c_str());
+    int prog_fd = bpf_obj_get(path.c_str());
+    if (prog_fd < 0) {
+        ALOGD("bpf_obj_get() failed for program %s", path.c_str());
+        return false;
+    }
+    if (bpf_attach_tracepoint(prog_fd, eventType.c_str(), eventName.c_str()) < 0) {
+        ALOGD("Failed to attach bpf program to tracepoint %s/%s", eventType.c_str(),
+              eventName.c_str());
+        return false;
+    }
+    return true;
+}
+
+// Start tracking and aggregating data to be reported by getUidCpuFreqTimes and getUidsCpuFreqTimes.
+// Returns true on success, false otherwise.
+// Tracking is active only once a live process has successfully called this function; if the calling
+// process dies then it must be called again to resume tracking.
+// This function should *not* be called while tracking is already active; doing so is unnecessary
+// and can lead to accounting errors.
+bool startTrackingUidCpuFreqTimes() {
+    return attachTracepointProgram("sched", "sched_switch") &&
+            attachTracepointProgram("power", "cpu_frequency");
+}
+
+// Retrieve the times in ns that uid spent running at each CPU frequency and store in freqTimes.
+// Returns false on error. Otherwise, returns true and populates freqTimes with a vector of vectors
+// using the format:
+// [[t0_0, t0_1, ...],
+//  [t1_0, t1_1, ...], ...]
+// where ti_j is the ns that uid spent running on the ith cluster at that cluster's jth lowest freq.
+bool getUidCpuFreqTimes(uint32_t uid, std::vector<std::vector<uint64_t>> *freqTimes) {
+    if (!gInitialized && !initGlobals()) return false;
+    time_key_t key = {.uid = uid, .freq = 0};
+
+    freqTimes->clear();
+    freqTimes->resize(gNPolicies);
+    std::vector<uint32_t> idxs(gNPolicies, 0);
+
+    val_t value;
+    for (uint32_t freq : gAllFreqs) {
+        key.freq = freq;
+        int ret = findMapEntry(gMapFd, &key, &value);
+        if (ret) {
+            if (errno == ENOENT)
+                memset(&value.ar, 0, sizeof(value.ar));
+            else
+                return false;
+        }
+        for (uint32_t i = 0; i < gNPolicies; ++i) {
+            if (idxs[i] == gPolicyFreqs[i].size() || freq != gPolicyFreqs[i][idxs[i]]) continue;
+            uint64_t time = 0;
+            for (uint32_t cpu : gPolicyCpus[i]) time += value.ar[cpu];
+            idxs[i] += 1;
+            (*freqTimes)[i].emplace_back(time);
+        }
+    }
+
+    return true;
+}
+
+// Retrieve the times in ns that each uid spent running at each CPU freq and store in freqTimeMap.
+// Returns false on error. Otherwise, returns true and populates freqTimeMap with a map from uids to
+// vectors of vectors using the format:
+// { uid0 -> [[t0_0_0, t0_0_1, ...], [t0_1_0, t0_1_1, ...], ...],
+//   uid1 -> [[t1_0_0, t1_0_1, ...], [t1_1_0, t1_1_1, ...], ...], ... }
+// where ti_j_k is the ns uid i spent running on the jth cluster at the cluster's kth lowest freq.
+bool getUidsCpuFreqTimes(
+        std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>> *freqTimeMap) {
+    if (!gInitialized && !initGlobals()) return false;
+
+    int fd = bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_times");
+    if (fd < 0) return false;
+    BpfMap<time_key_t, val_t> m(fd);
+
+    std::vector<std::unordered_map<uint32_t, uint32_t>> policyFreqIdxs;
+    for (uint32_t i = 0; i < gNPolicies; ++i) {
+        std::unordered_map<uint32_t, uint32_t> freqIdxs;
+        for (size_t j = 0; j < gPolicyFreqs[i].size(); ++j) freqIdxs[gPolicyFreqs[i][j]] = j;
+        policyFreqIdxs.emplace_back(freqIdxs);
+    }
+
+    auto fn = [freqTimeMap, &policyFreqIdxs](const time_key_t &key, const val_t &val,
+                                             const BpfMap<time_key_t, val_t> &) {
+        if (freqTimeMap->find(key.uid) == freqTimeMap->end()) {
+            std::vector<std::vector<uint64_t>> v;
+            for (uint32_t i = 0; i < gNPolicies; ++i) {
+                std::vector<uint64_t> v2(gPolicyFreqs[i].size(), 0);
+                v.emplace_back(v2);
+            }
+            (*freqTimeMap)[key.uid] = v;
+        }
+
+        for (size_t policy = 0; policy < gNPolicies; ++policy) {
+            for (const auto &cpu : gPolicyCpus[policy]) {
+                uint32_t cpuTime = val.ar[cpu];
+                if (cpuTime == 0) continue;
+                auto freqIdx = policyFreqIdxs[policy][key.freq];
+                (*freqTimeMap)[key.uid][policy][freqIdx] += cpuTime;
+            }
+        }
+        return android::netdutils::status::ok;
+    };
+    return isOk(m.iterateWithValue(fn));
+}
+
+// Clear all time in state data for a given uid. Returns false on error, true otherwise.
+bool clearUidCpuFreqTimes(uint32_t uid) {
+    if (!gInitialized && !initGlobals()) return false;
+    time_key_t key = {.uid = uid, .freq = 0};
+
+    std::vector<uint32_t> idxs(gNPolicies, 0);
+    for (auto freq : gAllFreqs) {
+        key.freq = freq;
+        if (deleteMapEntry(gMapFd, &key) && errno != ENOENT) return false;
+    }
+    return true;
+}
+
+} // namespace bpf
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/libs/cputimeinstate/cputimeinstate.h
similarity index 64%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to libs/cputimeinstate/cputimeinstate.h
index e6ac6bf..0205452 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/libs/cputimeinstate/cputimeinstate.h
@@ -14,14 +14,16 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include <unordered_map>
+#include <vector>
 
 namespace android {
-namespace mock {
+namespace bpf {
 
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+bool startTrackingUidCpuFreqTimes();
+bool getUidCpuFreqTimes(unsigned int uid, std::vector<std::vector<uint64_t>> *freqTimes);
+bool getUidsCpuFreqTimes(std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>> *tisMap);
+bool clearUidCpuFreqTimes(unsigned int uid);
 
-} // namespace mock
+} // namespace bpf
 } // namespace android
diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp
new file mode 100644
index 0000000..9837865
--- /dev/null
+++ b/libs/cputimeinstate/testtimeinstate.cpp
@@ -0,0 +1,58 @@
+
+#include <unordered_map>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <cputimeinstate.h>
+
+namespace android {
+namespace bpf {
+
+using std::vector;
+
+TEST(TimeInStateTest, SingleUid) {
+    vector<vector<uint64_t>> times;
+    ASSERT_TRUE(getUidCpuFreqTimes(0, &times));
+    EXPECT_FALSE(times.empty());
+}
+
+TEST(TimeInStateTest, AllUid) {
+    vector<size_t> sizes;
+    std::unordered_map<uint32_t, vector<vector<uint64_t>>> map;
+    ASSERT_TRUE(getUidsCpuFreqTimes(&map));
+
+    ASSERT_FALSE(map.empty());
+
+    auto firstEntry = map.begin()->second;
+    for (const auto &subEntry : firstEntry) sizes.emplace_back(subEntry.size());
+
+    for (const auto &vec : map) {
+        ASSERT_EQ(vec.second.size(), sizes.size());
+        for (size_t i = 0; i < vec.second.size(); ++i) ASSERT_EQ(vec.second[i].size(), sizes[i]);
+    }
+}
+
+TEST(TimeInStateTest, RemoveUid) {
+    vector<vector<uint64_t>> times, times2;
+    ASSERT_TRUE(getUidCpuFreqTimes(0, &times));
+    ASSERT_FALSE(times.empty());
+
+    uint64_t sum = 0;
+    for (size_t i = 0; i < times.size(); ++i) {
+        for (auto x : times[i]) sum += x;
+    }
+    ASSERT_GT(sum, (uint64_t)0);
+
+    ASSERT_TRUE(clearUidCpuFreqTimes(0));
+
+    ASSERT_TRUE(getUidCpuFreqTimes(0, &times2));
+    ASSERT_EQ(times2.size(), times.size());
+    for (size_t i = 0; i < times.size(); ++i) {
+        ASSERT_EQ(times2[i].size(), times[i].size());
+        for (size_t j = 0; j < times[i].size(); ++j) ASSERT_LE(times2[i][j], times[i][j]);
+    }
+}
+
+} // namespace bpf
+} // namespace android
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 9dc7431..aa2f394 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -262,7 +262,7 @@
 bool GraphicsEnv::shouldUseAngle() {
     // Make sure we are init'ed
     if (mAngleAppName.empty()) {
-        ALOGE("App name is empty. setAngleInfo() must be called first to enable ANGLE.");
+        ALOGV("App name is empty. setAngleInfo() has not been called to enable ANGLE.");
         return false;
     }
 
@@ -284,16 +284,7 @@
     } else {
         // The "Developer Options" value wasn't set to force the use of ANGLE.  Need to temporarily
         // load ANGLE and call the updatable opt-in/out logic:
-
-        // Check if ANGLE is enabled. Workaround for several bugs:
-        // b/119305693 b/119322355 b/119305887
-        // Something is not working correctly in the feature library
-        char prop[PROPERTY_VALUE_MAX];
-        property_get("debug.angle.enable", prop, "0");
-        void* featureSo = nullptr;
-        if (atoi(prop)) {
-            featureSo = loadLibrary("feature_support");
-        }
+        void* featureSo = loadLibrary("feature_support");
         if (featureSo) {
             ALOGV("loaded ANGLE's opt-in/out logic from namespace");
             mUseAngle = checkAngleRules(featureSo);
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 37c24ef..429ce0e 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -24,7 +24,7 @@
 
 namespace android {
 
-class NativeLoaderNamespace;
+struct NativeLoaderNamespace;
 
 class GraphicsEnv {
 public:
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 a481e81..7195786 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -66,7 +66,8 @@
     virtual void setTransactionState(const Vector<ComposerState>& state,
                                      const Vector<DisplayState>& displays, uint32_t flags,
                                      const sp<IBinder>& applyToken,
-                                     const InputWindowCommands& commands) {
+                                     const InputWindowCommands& commands,
+                                     int64_t desiredPresentTime) {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
 
@@ -83,6 +84,7 @@
         data.writeUint32(flags);
         data.writeStrongBinder(applyToken);
         commands.write(data);
+        data.writeInt64(desiredPresentTime);
         remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply);
     }
 
@@ -681,6 +683,74 @@
         result = reply.readUint64Vector(&outStats->component_3_sample);
         return result;
     }
+
+    virtual status_t getProtectedContentSupport(bool* outSupported) const {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        status_t error =
+                remote()->transact(BnSurfaceComposer::GET_PROTECTED_CONTENT_SUPPORT, data, &reply);
+        if (error != NO_ERROR) {
+            return error;
+        }
+        error = reply.readBool(outSupported);
+        return error;
+    }
+
+    virtual status_t cacheBuffer(const sp<IBinder>& token, const sp<GraphicBuffer>& buffer,
+                                 int32_t* outBufferId) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+
+        data.writeStrongBinder(token);
+        if (buffer) {
+            data.writeBool(true);
+            data.write(*buffer);
+        } else {
+            data.writeBool(false);
+        }
+
+        status_t result = remote()->transact(BnSurfaceComposer::CACHE_BUFFER, data, &reply);
+        if (result != NO_ERROR) {
+            return result;
+        }
+
+        int32_t id = -1;
+        result = reply.readInt32(&id);
+        if (result == NO_ERROR) {
+            *outBufferId = id;
+        }
+        return result;
+    }
+
+    virtual status_t uncacheBuffer(const sp<IBinder>& token, int32_t bufferId) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+
+        data.writeStrongBinder(token);
+        data.writeInt32(bufferId);
+
+        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
@@ -736,7 +806,10 @@
             sp<IBinder> applyToken = data.readStrongBinder();
             InputWindowCommands inputWindowCommands;
             inputWindowCommands.read(data);
-            setTransactionState(state, displays, stateFlags, applyToken, inputWindowCommands);
+
+            int64_t desiredPresentTime = data.readInt64();
+            setTransactionState(state, displays, stateFlags, applyToken, inputWindowCommands,
+                                desiredPresentTime);
             return NO_ERROR;
         }
         case BOOT_FINISHED: {
@@ -1017,7 +1090,7 @@
                 reply->writeInt32(static_cast<int32_t>(wideColorGamutDataspace));
                 reply->writeInt32(static_cast<int32_t>(wideColorGamutPixelFormat));
             }
-            return NO_ERROR;
+            return error;
         }
         case GET_COLOR_MANAGEMENT: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
@@ -1026,7 +1099,7 @@
             if (error == NO_ERROR) {
                 reply->writeBool(result);
             }
-            return result;
+            return error;
         }
         case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
@@ -1110,6 +1183,71 @@
             }
             return result;
         }
+        case GET_PROTECTED_CONTENT_SUPPORT: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            bool result;
+            status_t error = getProtectedContentSupport(&result);
+            if (error == NO_ERROR) {
+                reply->writeBool(result);
+            }
+            return error;
+        }
+        case CACHE_BUFFER: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> token;
+            status_t result = data.readStrongBinder(&token);
+            if (result != NO_ERROR) {
+                ALOGE("cache buffer failure in reading token: %d", result);
+                return result;
+            }
+
+            sp<GraphicBuffer> buffer = new GraphicBuffer();
+            if (data.readBool()) {
+                result = data.read(*buffer);
+                if (result != NO_ERROR) {
+                    ALOGE("cache buffer failure in reading buffer: %d", result);
+                    return result;
+                }
+            }
+            int32_t bufferId = -1;
+            status_t error = cacheBuffer(token, buffer, &bufferId);
+            if (error == NO_ERROR) {
+                reply->writeInt32(bufferId);
+            }
+            return error;
+        }
+        case UNCACHE_BUFFER: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> token;
+            status_t result = data.readStrongBinder(&token);
+            if (result != NO_ERROR) {
+                ALOGE("uncache buffer failure in reading token: %d", result);
+                return result;
+            }
+
+            int32_t bufferId = -1;
+            result = data.readInt32(&bufferId);
+            if (result != NO_ERROR) {
+                ALOGE("uncache buffer failure in reading buffer id: %d", result);
+                return result;
+            }
+
+            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/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 1be55e6..ce88d7b 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -39,7 +39,16 @@
     if (err != NO_ERROR) {
         return err;
     }
-    return output->writeBool(releasePreviousBuffer);
+    if (previousReleaseFence) {
+        err = output->writeBool(true);
+        if (err != NO_ERROR) {
+            return err;
+        }
+        err = output->write(*previousReleaseFence);
+    } else {
+        err = output->writeBool(false);
+    }
+    return err;
 }
 
 status_t SurfaceStats::readFromParcel(const Parcel* input) {
@@ -51,7 +60,19 @@
     if (err != NO_ERROR) {
         return err;
     }
-    return input->readBool(&releasePreviousBuffer);
+    bool hasFence = false;
+    err = input->readBool(&hasFence);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    if (hasFence) {
+        previousReleaseFence = new Fence();
+        err = input->read(*previousReleaseFence);
+        if (err != NO_ERROR) {
+            return err;
+        }
+    }
+    return NO_ERROR;
 }
 
 status_t TransactionStats::writeToParcel(Parcel* output) const {
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 e5170ab..6091d3f 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -94,6 +94,13 @@
         }
     }
 
+    output.writeStrongBinder(cachedBuffer.token);
+    output.writeInt32(cachedBuffer.bufferId);
+    output.writeParcelable(metadata);
+
+    output.writeFloat(colorAlpha);
+    output.writeUint32(static_cast<uint32_t>(colorDataspace));
+
     return NO_ERROR;
 }
 
@@ -164,6 +171,13 @@
         listenerCallbacks.emplace_back(listener, callbackIds);
     }
 
+    cachedBuffer.token = input.readStrongBinder();
+    cachedBuffer.bufferId = input.readInt32();
+    input.readParcelable(&metadata);
+
+    colorAlpha = input.readFloat();
+    colorDataspace = static_cast<ui::Dataspace>(input.readUint32());
+
     return NO_ERROR;
 }
 
@@ -372,6 +386,22 @@
     }
 #endif
 
+    if (other.what & eCachedBufferChanged) {
+        what |= eCachedBufferChanged;
+        cachedBuffer = other.cachedBuffer;
+    }
+    if (other.what & eColorAlphaChanged) {
+        what |= eColorAlphaChanged;
+        colorAlpha = other.colorAlpha;
+    }
+    if (other.what & eColorDataspaceChanged) {
+        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/OWNERS b/libs/gui/OWNERS
index a7c7e79..73150dc 100644
--- a/libs/gui/OWNERS
+++ b/libs/gui/OWNERS
@@ -1,8 +1,7 @@
-brianderson@google.com
 jessehall@google.com
 jwcai@google.com
+lpy@google.com
+marissaw@google.com
 mathias@google.com
-olv@google.com
-pceballos@google.com
 racarr@google.com
 stoza@google.com
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 8b9e4d7..a5d43c0 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -102,6 +102,27 @@
     mDeathObserver = nullptr;
 }
 
+class DefaultComposerClient: public Singleton<DefaultComposerClient> {
+    Mutex mLock;
+    sp<SurfaceComposerClient> mClient;
+    friend class Singleton<ComposerService>;
+public:
+    static sp<SurfaceComposerClient> getComposerClient() {
+        DefaultComposerClient& dc = DefaultComposerClient::getInstance();
+        Mutex::Autolock _l(dc.mLock);
+        if (dc.mClient == nullptr) {
+            dc.mClient = new SurfaceComposerClient;
+        }
+        return dc.mClient;
+    }
+};
+ANDROID_SINGLETON_STATIC_INSTANCE(DefaultComposerClient);
+
+
+sp<SurfaceComposerClient> SurfaceComposerClient::getDefault() {
+    return DefaultComposerClient::getComposerClient();
+}
+
 // ---------------------------------------------------------------------------
 
 // TransactionCompletedListener does not use ANDROID_SINGLETON_STATIC_INSTANCE because it needs
@@ -132,26 +153,76 @@
     mListening = true;
 }
 
-CallbackId TransactionCompletedListener::addCallback(const TransactionCompletedCallback& callback) {
+CallbackId TransactionCompletedListener::addCallbackFunction(
+        const TransactionCompletedCallback& callbackFunction,
+        const std::unordered_set<sp<SurfaceControl>, SurfaceComposerClient::SCHash>&
+                surfaceControls) {
     std::lock_guard<std::mutex> lock(mMutex);
     startListeningLocked();
 
     CallbackId callbackId = getNextIdLocked();
-    mCallbacks.emplace(callbackId, callback);
+    mCallbacks[callbackId].callbackFunction = callbackFunction;
+
+    auto& callbackSurfaceControls = mCallbacks[callbackId].surfaceControls;
+
+    for (const auto& surfaceControl : surfaceControls) {
+        callbackSurfaceControls[surfaceControl->getHandle()] = surfaceControl;
+    }
+
     return callbackId;
 }
 
+void TransactionCompletedListener::addSurfaceControlToCallbacks(
+        const sp<SurfaceControl>& surfaceControl,
+        const std::unordered_set<CallbackId>& callbackIds) {
+    std::lock_guard<std::mutex> lock(mMutex);
+
+    for (auto callbackId : callbackIds) {
+        mCallbacks[callbackId].surfaceControls.emplace(std::piecewise_construct,
+                                                       std::forward_as_tuple(
+                                                               surfaceControl->getHandle()),
+                                                       std::forward_as_tuple(surfaceControl));
+    }
+}
+
 void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
     std::lock_guard lock(mMutex);
 
+    /* This listener knows all the sp<IBinder> to sp<SurfaceControl> for all its registered
+     * callbackIds, except for when Transactions are merged together. This probably cannot be
+     * solved before this point because the Transactions could be merged together and applied in a
+     * different process.
+     *
+     * Fortunately, we get all the callbacks for this listener for the same frame together at the
+     * same time. This means if any Transactions were merged together, we will get their callbacks
+     * at the same time. We can combine all the sp<IBinder> to sp<SurfaceControl> maps for all the
+     * callbackIds to generate one super map that contains all the sp<IBinder> to sp<SurfaceControl>
+     * that could possibly exist for the callbacks.
+     */
+    std::unordered_map<sp<IBinder>, sp<SurfaceControl>, IBinderHash> surfaceControls;
     for (const auto& [callbackIds, transactionStats] : listenerStats.transactionStats) {
         for (auto callbackId : callbackIds) {
-            const auto& callback = mCallbacks[callbackId];
-            if (!callback) {
+            auto& [callbackFunction, callbackSurfaceControls] = mCallbacks[callbackId];
+            surfaceControls.insert(callbackSurfaceControls.begin(), callbackSurfaceControls.end());
+        }
+    }
+
+    for (const auto& [callbackIds, transactionStats] : listenerStats.transactionStats) {
+        for (auto callbackId : callbackIds) {
+            auto& [callbackFunction, callbackSurfaceControls] = mCallbacks[callbackId];
+            if (!callbackFunction) {
                 ALOGE("cannot call null callback function, skipping");
                 continue;
             }
-            callback(transactionStats);
+            std::vector<SurfaceControlStats> surfaceControlStats;
+            for (const auto& surfaceStats : transactionStats.surfaceStats) {
+                surfaceControlStats.emplace_back(surfaceControls[surfaceStats.surfaceControl],
+                                                 surfaceStats.acquireTime,
+                                                 surfaceStats.previousReleaseFence);
+            }
+
+            callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
+                             surfaceControlStats);
             mCallbacks.erase(callbackId);
         }
     }
@@ -159,11 +230,12 @@
 
 // ---------------------------------------------------------------------------
 
-SurfaceComposerClient::Transaction::Transaction(const Transaction& other) :
-    mForceSynchronous(other.mForceSynchronous),
-    mTransactionNestCount(other.mTransactionNestCount),
-    mAnimation(other.mAnimation),
-    mEarlyWakeup(other.mEarlyWakeup) {
+SurfaceComposerClient::Transaction::Transaction(const Transaction& other)
+      : mForceSynchronous(other.mForceSynchronous),
+        mTransactionNestCount(other.mTransactionNestCount),
+        mAnimation(other.mAnimation),
+        mEarlyWakeup(other.mEarlyWakeup),
+        mDesiredPresentTime(other.mDesiredPresentTime) {
     mDisplayStates = other.mDisplayStates;
     mComposerStates = other.mComposerStates;
     mInputWindowCommands = other.mInputWindowCommands;
@@ -218,7 +290,7 @@
     s.state.parentHandleForChild = nullptr;
 
     composerStates.add(s);
-    sf->setTransactionState(composerStates, displayStates, 0, nullptr, {});
+    sf->setTransactionState(composerStates, displayStates, 0, nullptr, {}, -1);
 }
 
 status_t SurfaceComposerClient::Transaction::apply(bool synchronous) {
@@ -284,7 +356,8 @@
     mEarlyWakeup = false;
 
     sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
-    sf->setTransactionState(composerStates, displayStates, flags, applyToken, mInputWindowCommands);
+    sf->setTransactionState(composerStates, displayStates, flags, applyToken, mInputWindowCommands,
+                            mDesiredPresentTime);
     mInputWindowCommands.clear();
     mStatus = NO_ERROR;
     return NO_ERROR;
@@ -327,7 +400,11 @@
 
 void SurfaceComposerClient::Transaction::registerSurfaceControlForCallback(
         const sp<SurfaceControl>& sc) {
-    mListenerCallbacks[TransactionCompletedListener::getIInstance()].surfaceControls.insert(sc);
+    auto& callbackInfo = mListenerCallbacks[TransactionCompletedListener::getIInstance()];
+    callbackInfo.surfaceControls.insert(sc);
+
+    TransactionCompletedListener::getInstance()
+            ->addSurfaceControlToCallbacks(sc, callbackInfo.callbackIds);
 }
 
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setPosition(
@@ -462,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) {
@@ -587,6 +678,36 @@
     return *this;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setColorAlpha(
+        const sp<SurfaceControl>& sc, float alpha) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+
+    s->what |= layer_state_t::eColorAlphaChanged;
+    s->colorAlpha = alpha;
+
+    registerSurfaceControlForCallback(sc);
+    return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setColorDataspace(
+        const sp<SurfaceControl>& sc, ui::Dataspace dataspace) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+
+    s->what |= layer_state_t::eColorDataspaceChanged;
+    s->colorDataspace = dataspace;
+
+    registerSurfaceControlForCallback(sc);
+    return *this;
+}
+
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setTransform(
         const sp<SurfaceControl>& sc, uint32_t transform) {
     layer_state_t* s = getLayerState(sc);
@@ -658,6 +779,21 @@
     return *this;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCachedBuffer(
+        const sp<SurfaceControl>& sc, int32_t bufferId) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+    s->what |= layer_state_t::eCachedBufferChanged;
+    s->cachedBuffer.token = IInterface::asBinder(TransactionCompletedListener::getIInstance());
+    s->cachedBuffer.bufferId = bufferId;
+
+    registerSurfaceControlForCallback(sc);
+    return *this;
+}
+
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setAcquireFence(
         const sp<SurfaceControl>& sc, const sp<Fence>& fence) {
     layer_state_t* s = getLayerState(sc);
@@ -742,14 +878,23 @@
     return *this;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDesiredPresentTime(
+        nsecs_t desiredPresentTime) {
+    mDesiredPresentTime = desiredPresentTime;
+    return *this;
+}
+
 SurfaceComposerClient::Transaction&
 SurfaceComposerClient::Transaction::addTransactionCompletedCallback(
         TransactionCompletedCallbackTakesContext callback, void* callbackContext) {
     auto listener = TransactionCompletedListener::getInstance();
 
-    auto callbackWithContext = std::bind(callback, callbackContext, std::placeholders::_1);
+    auto callbackWithContext = std::bind(callback, callbackContext, std::placeholders::_1,
+                                         std::placeholders::_2, std::placeholders::_3);
+    const auto& surfaceControls =
+            mListenerCallbacks[TransactionCompletedListener::getIInstance()].surfaceControls;
 
-    CallbackId callbackId = listener->addCallback(callbackWithContext);
+    CallbackId callbackId = listener->addCallbackFunction(callbackWithContext, surfaceControls);
 
     mListenerCallbacks[TransactionCompletedListener::getIInstance()].callbackIds.emplace(
             callbackId);
@@ -849,6 +994,54 @@
     return *this;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setGeometry(
+        const sp<SurfaceControl>& sc, const Rect& source, const Rect& dst, int transform) {
+    setCrop_legacy(sc, source);
+
+    int x = dst.left;
+    int y = dst.top;
+    float xScale = dst.getWidth() / static_cast<float>(source.getWidth());
+    float yScale = dst.getHeight() / static_cast<float>(source.getHeight());
+    float matrix[4] = {1, 0, 0, 1};
+
+    switch (transform) {
+        case NATIVE_WINDOW_TRANSFORM_FLIP_H:
+            matrix[0] = -xScale; matrix[1] = 0;
+            matrix[2] = 0; matrix[3] = yScale;
+            x += source.getWidth();
+            break;
+        case NATIVE_WINDOW_TRANSFORM_FLIP_V:
+            matrix[0] = xScale; matrix[1] = 0;
+            matrix[2] = 0; matrix[3] = -yScale;
+            y += source.getHeight();
+            break;
+        case NATIVE_WINDOW_TRANSFORM_ROT_90:
+            matrix[0] = 0; matrix[1] = -yScale;
+            matrix[2] = xScale; matrix[3] = 0;
+            x += source.getHeight();
+            break;
+        case NATIVE_WINDOW_TRANSFORM_ROT_180:
+            matrix[0] = -xScale; matrix[1] = 0;
+            matrix[2] = 0; matrix[3] = -yScale;
+            x += source.getWidth();
+            y += source.getHeight();
+            break;
+        case NATIVE_WINDOW_TRANSFORM_ROT_270:
+            matrix[0] = 0; matrix[1] = yScale;
+            matrix[2] = -xScale; matrix[3] = 0;
+            y += source.getWidth();
+            break;
+        default:
+            matrix[0] = xScale; matrix[1] = 0;
+            matrix[2] = 0; matrix[3] = yScale;
+            break;
+    }
+    setMatrix(sc, matrix[0], matrix[1], matrix[2], matrix[3]);
+    setPosition(sc, x, y);
+
+    return *this;
+}
+
 // ---------------------------------------------------------------------------
 
 DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
@@ -962,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;
 
@@ -990,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 */);
@@ -1000,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;
 
@@ -1022,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 */);
@@ -1049,6 +1230,26 @@
 
 // ----------------------------------------------------------------------------
 
+status_t SurfaceComposerClient::cacheBuffer(const sp<GraphicBuffer>& buffer, int32_t* outBufferId) {
+    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+    if (buffer == nullptr || outBufferId == nullptr) {
+        return BAD_VALUE;
+    }
+    return sf->cacheBuffer(IInterface::asBinder(TransactionCompletedListener::getIInstance()),
+                           buffer, outBufferId);
+}
+
+status_t SurfaceComposerClient::uncacheBuffer(int32_t bufferId) {
+    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+    if (bufferId < 0) {
+        return BAD_VALUE;
+    }
+    return sf->uncacheBuffer(IInterface::asBinder(TransactionCompletedListener::getIInstance()),
+                             bufferId);
+}
+
+// ----------------------------------------------------------------------------
+
 status_t SurfaceComposerClient::enableVSyncInjections(bool enable) {
     sp<ISurfaceComposer> sf(ComposerService::getComposerService());
     return sf->enableVSyncInjections(enable);
@@ -1118,6 +1319,12 @@
                                        wideColorGamutDataspace, wideColorGamutPixelFormat);
 }
 
+bool SurfaceComposerClient::getProtectedContentSupport() {
+    bool supported = false;
+    ComposerService::getComposerService()->getProtectedContentSupport(&supported);
+    return supported;
+}
+
 status_t SurfaceComposerClient::clearAnimationFrameStats() {
     return ComposerService::getComposerService()->clearAnimationFrameStats();
 }
@@ -1156,6 +1363,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/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h
index 537c957..b377a41 100644
--- a/libs/gui/include/gui/BufferQueueCore.h
+++ b/libs/gui/include/gui/BufferQueueCore.h
@@ -40,13 +40,14 @@
 #define BQ_LOGW(x, ...) ALOGW("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
 #define BQ_LOGE(x, ...) ALOGE("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
 
-#define ATRACE_BUFFER_INDEX(index)                                   \
-    if (ATRACE_ENABLED()) {                                          \
-        char ___traceBuf[1024];                                      \
-        snprintf(___traceBuf, 1024, "%s: %d",                        \
-                mCore->mConsumerName.string(), (index));             \
-        android::ScopedTrace ___bufTracer(ATRACE_TAG, ___traceBuf);  \
-    }
+#define ATRACE_BUFFER_INDEX(index)                                                         \
+    do {                                                                                   \
+        if (ATRACE_ENABLED()) {                                                            \
+            char ___traceBuf[1024];                                                        \
+            snprintf(___traceBuf, 1024, "%s: %d", mCore->mConsumerName.string(), (index)); \
+            android::ScopedTrace ___bufTracer(ATRACE_TAG, ___traceBuf);                    \
+        }                                                                                  \
+    } while (false)
 
 namespace android {
 
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 1534fca..bbfc8c4 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -116,7 +116,8 @@
     virtual void setTransactionState(const Vector<ComposerState>& state,
                                      const Vector<DisplayState>& displays, uint32_t flags,
                                      const sp<IBinder>& applyToken,
-                                     const InputWindowCommands& inputWindowCommands) = 0;
+                                     const InputWindowCommands& inputWindowCommands,
+                                     int64_t desiredPresentTime) = 0;
 
     /* signal that we're done booting.
      * Requires ACCESS_SURFACE_FLINGER permission
@@ -309,6 +310,24 @@
     virtual status_t getDisplayedContentSample(const sp<IBinder>& display, uint64_t maxFrames,
                                                uint64_t timestamp,
                                                DisplayedFrameStats* outStats) const = 0;
+
+    /*
+     * Gets whether SurfaceFlinger can support protected content in GPU composition.
+     * Requires the ACCESS_SURFACE_FLINGER permission.
+     */
+    virtual status_t getProtectedContentSupport(bool* outSupported) const = 0;
+
+    virtual status_t cacheBuffer(const sp<IBinder>& token, const sp<GraphicBuffer>& buffer,
+                                 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;
 };
 
 // ----------------------------------------------------------------------------
@@ -350,6 +369,11 @@
         GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES,
         SET_DISPLAY_CONTENT_SAMPLING_ENABLED,
         GET_DISPLAYED_CONTENT_SAMPLE,
+        GET_PROTECTED_CONTENT_SUPPORT,
+        CACHE_BUFFER,
+        UNCACHE_BUFFER,
+        IS_WIDE_COLOR_DISPLAY,
+        // 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/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index 8acfa7a..29ab026 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -52,12 +52,12 @@
     status_t readFromParcel(const Parcel* input) override;
 
     SurfaceStats() = default;
-    SurfaceStats(const sp<IBinder>& sc, nsecs_t time, bool releasePrevBuffer)
-          : surfaceControl(sc), acquireTime(time), releasePreviousBuffer(releasePrevBuffer) {}
+    SurfaceStats(const sp<IBinder>& sc, nsecs_t time, const sp<Fence>& prevReleaseFence)
+          : surfaceControl(sc), acquireTime(time), previousReleaseFence(prevReleaseFence) {}
 
     sp<IBinder> surfaceControl;
     nsecs_t acquireTime = -1;
-    bool releasePreviousBuffer = false;
+    sp<Fence> previousReleaseFence;
 };
 
 class TransactionStats : public Parcelable {
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 e7564f5..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>
@@ -84,6 +85,10 @@
         eInputInfoChanged = 0x40000000,
         eCornerRadiusChanged = 0x80000000,
         eFrameChanged = 0x1'00000000,
+        eCachedBufferChanged = 0x2'00000000,
+        eColorAlphaChanged = 0x4'00000000,
+        eColorDataspaceChanged = 0x8'00000000,
+        eMetadataChanged = 0x10'00000000,
     };
 
     layer_state_t()
@@ -109,7 +114,9 @@
             dataspace(ui::Dataspace::UNKNOWN),
             surfaceDamageRegion(),
             api(-1),
-            colorTransform(mat4()) {
+            colorTransform(mat4()),
+            colorAlpha(0),
+            colorDataspace(ui::Dataspace::UNKNOWN) {
         matrix.dsdx = matrix.dtdy = 1.0f;
         matrix.dsdy = matrix.dtdx = 0.0f;
         hdrMetadata.validTypes = 0;
@@ -125,6 +132,10 @@
         float dtdy{0};
         float dsdy{0};
     };
+    struct cached_buffer_t {
+        sp<IBinder> token = nullptr;
+        int32_t bufferId = -1;
+    };
     sp<IBinder> surface;
     uint64_t what;
     float x;
@@ -173,6 +184,13 @@
 #ifndef NO_INPUT
     InputWindowInfo inputInfo;
 #endif
+
+    cached_buffer_t cachedBuffer;
+
+    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 f16f781..ef3b2e6 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -53,35 +53,24 @@
 
 // ---------------------------------------------------------------------------
 
-using TransactionCompletedCallbackTakesContext =
-        std::function<void(void* /*context*/, const TransactionStats&)>;
-using TransactionCompletedCallback = std::function<void(const TransactionStats&)>;
+struct SurfaceControlStats {
+    SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t time,
+                        const sp<Fence>& prevReleaseFence)
+          : surfaceControl(sc), acquireTime(time), previousReleaseFence(prevReleaseFence) {}
 
-class TransactionCompletedListener : public BnTransactionCompletedListener {
-    TransactionCompletedListener();
-
-    CallbackId getNextIdLocked() REQUIRES(mMutex);
-
-    std::mutex mMutex;
-
-    bool mListening GUARDED_BY(mMutex) = false;
-
-    CallbackId mCallbackIdCounter GUARDED_BY(mMutex) = 1;
-
-    std::map<CallbackId, TransactionCompletedCallback> mCallbacks GUARDED_BY(mMutex);
-
-public:
-    static sp<TransactionCompletedListener> getInstance();
-    static sp<ITransactionCompletedListener> getIInstance();
-
-    void startListeningLocked() REQUIRES(mMutex);
-
-    CallbackId addCallback(const TransactionCompletedCallback& callback);
-
-    // Overrides BnTransactionCompletedListener's onTransactionCompleted
-    void onTransactionCompleted(ListenerStats stats) override;
+    sp<SurfaceControl> surfaceControl;
+    nsecs_t acquireTime = -1;
+    sp<Fence> previousReleaseFence;
 };
 
+using TransactionCompletedCallbackTakesContext =
+        std::function<void(void* /*context*/, nsecs_t /*latchTime*/,
+                           const sp<Fence>& /*presentFence*/,
+                           const std::vector<SurfaceControlStats>& /*stats*/)>;
+using TransactionCompletedCallback =
+        std::function<void(nsecs_t /*latchTime*/, const sp<Fence>& /*presentFence*/,
+                           const std::vector<SurfaceControlStats>& /*stats*/)>;
+
 // ---------------------------------------------------------------------------
 
 class SurfaceComposerClient : public RefBase
@@ -145,6 +134,12 @@
                                              ui::Dataspace* wideColorGamutDataspace,
                                              ui::PixelFormat* wideColorGamutPixelFormat);
 
+    /*
+     * Gets whether SurfaceFlinger can support protected content in GPU composition.
+     * Requires the ACCESS_SURFACE_FLINGER permission.
+     */
+    static bool getProtectedContentSupport();
+
     /**
      * Called from SurfaceControl d'tor to 'destroy' the surface (or rather, reparent it
      * to null), but without needing an sp<SurfaceControl> to avoid infinite ressurection.
@@ -152,43 +147,48 @@
     static void doDropReferenceTransaction(const sp<IBinder>& handle,
             const sp<ISurfaceComposerClient>& client);
 
+    // Caches a buffer with the ISurfaceComposer so the buffer does not need to be resent across
+    // processes
+    static status_t cacheBuffer(const sp<GraphicBuffer>& buffer, int32_t* outBufferId);
+    // 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
@@ -237,6 +237,18 @@
         bool                        mAnimation = false;
         bool                        mEarlyWakeup = false;
 
+        // mDesiredPresentTime is the time in nanoseconds that the client would like the transaction
+        // to be presented. When it is not possible to present at exactly that time, it will be
+        // presented after the time has passed.
+        //
+        // Desired present times that are more than 1 second in the future may be ignored.
+        // When a desired present time has already passed, the transaction will be presented as soon
+        // as possible.
+        //
+        // Transactions from the same process are presented in the same order that they are applied.
+        // The desired present time does not affect this ordering.
+        int64_t mDesiredPresentTime = -1;
+
         InputWindowCommands mInputWindowCommands;
         int mStatus = NO_ERROR;
 
@@ -287,6 +299,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
@@ -311,12 +325,19 @@
 
         Transaction& setColor(const sp<SurfaceControl>& sc, const half3& color);
 
+        // Sets the alpha of the background color layer if it exists.
+        Transaction& setColorAlpha(const sp<SurfaceControl>& sc, float alpha);
+
+        // Sets the dataspace of the background color layer if it exists.
+        Transaction& setColorDataspace(const sp<SurfaceControl>& sc, ui::Dataspace dataspace);
+
         Transaction& setTransform(const sp<SurfaceControl>& sc, uint32_t transform);
         Transaction& setTransformToDisplayInverse(const sp<SurfaceControl>& sc,
                                                   bool transformToDisplayInverse);
         Transaction& setCrop(const sp<SurfaceControl>& sc, const Rect& crop);
         Transaction& setFrame(const sp<SurfaceControl>& sc, const Rect& frame);
         Transaction& setBuffer(const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer);
+        Transaction& setCachedBuffer(const sp<SurfaceControl>& sc, int32_t bufferId);
         Transaction& setAcquireFence(const sp<SurfaceControl>& sc, const sp<Fence>& fence);
         Transaction& setDataspace(const sp<SurfaceControl>& sc, ui::Dataspace dataspace);
         Transaction& setHdrMetadata(const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata);
@@ -325,6 +346,7 @@
         Transaction& setApi(const sp<SurfaceControl>& sc, int32_t api);
         Transaction& setSidebandStream(const sp<SurfaceControl>& sc,
                                        const sp<NativeHandle>& sidebandStream);
+        Transaction& setDesiredPresentTime(nsecs_t desiredPresentTime);
 
         Transaction& addTransactionCompletedCallback(
                 TransactionCompletedCallbackTakesContext callback, void* callbackContext);
@@ -361,6 +383,9 @@
         Transaction& setColorTransform(const sp<SurfaceControl>& sc, const mat3& matrix,
                                        const vec3& translation);
 
+        Transaction& setGeometry(const sp<SurfaceControl>& sc,
+                const Rect& source, const Rect& dst, int transform);
+
         status_t setDisplaySurface(const sp<IBinder>& token,
                 const sp<IGraphicBufferProducer>& bufferProducer);
 
@@ -439,6 +464,50 @@
 
 // ---------------------------------------------------------------------------
 
+class TransactionCompletedListener : public BnTransactionCompletedListener {
+    TransactionCompletedListener();
+
+    CallbackId getNextIdLocked() REQUIRES(mMutex);
+
+    std::mutex mMutex;
+
+    bool mListening GUARDED_BY(mMutex) = false;
+
+    CallbackId mCallbackIdCounter GUARDED_BY(mMutex) = 1;
+
+    struct IBinderHash {
+        std::size_t operator()(const sp<IBinder>& iBinder) const {
+            return std::hash<IBinder*>{}(iBinder.get());
+        }
+    };
+
+    struct CallbackTranslation {
+        TransactionCompletedCallback callbackFunction;
+        std::unordered_map<sp<IBinder>, sp<SurfaceControl>, IBinderHash> surfaceControls;
+    };
+
+    std::unordered_map<CallbackId, CallbackTranslation> mCallbacks GUARDED_BY(mMutex);
+
+public:
+    static sp<TransactionCompletedListener> getInstance();
+    static sp<ITransactionCompletedListener> getIInstance();
+
+    void startListeningLocked() REQUIRES(mMutex);
+
+    CallbackId addCallbackFunction(
+            const TransactionCompletedCallback& callbackFunction,
+            const std::unordered_set<sp<SurfaceControl>, SurfaceComposerClient::SCHash>&
+                    surfaceControls);
+
+    void addSurfaceControlToCallbacks(const sp<SurfaceControl>& surfaceControl,
+                                      const std::unordered_set<CallbackId>& callbackIds);
+
+    // Overrides BnTransactionCompletedListener's onTransactionCompleted
+    void onTransactionCompleted(ListenerStats stats) override;
+};
+
+// ---------------------------------------------------------------------------
+
 }; // namespace android
 
 #endif // ANDROID_GUI_SURFACE_COMPOSER_CLIENT_H
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 7fc69ff..ac97733 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -489,5 +489,13 @@
     toSurface->expectMotionEvent(AMOTION_EVENT_ACTION_UP, 1, 1);
     fromSurface->expectNoMotionEvent(AMOTION_EVENT_ACTION_UP);
 }
+
+TEST_F(InputSurfacesTest, input_respects_outscreen) {
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    surface->showAt(-1, -1);
+
+    injectTap(0, 0);
+    surface->expectTap(1, 1);
+}
 }
 }
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 60173dc..d525a33 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -556,7 +556,9 @@
     void setTransactionState(const Vector<ComposerState>& /*state*/,
                              const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
                              const sp<IBinder>& /*applyToken*/,
-                             const InputWindowCommands& /*inputWindowCommands*/) override {}
+                             const InputWindowCommands& /*inputWindowCommands*/,
+                             int64_t /*desiredPresentTime*/) override {}
+
     void bootFinished() override {}
     bool authenticateSurfaceTexture(
             const sp<IGraphicBufferProducer>& /*surface*/) const override {
@@ -655,7 +657,15 @@
         return NO_ERROR;
     }
 
-    virtual status_t getColorManagement(bool* /*outGetColorManagement*/) const { return NO_ERROR; }
+    status_t getColorManagement(bool* /*outGetColorManagement*/) const override { return NO_ERROR; }
+    status_t getProtectedContentSupport(bool* /*outSupported*/) const override { return NO_ERROR; }
+
+    status_t cacheBuffer(const sp<IBinder>& /*token*/, const sp<GraphicBuffer>& /*buffer*/,
+                         int32_t* /*outBufferId*/) {
+        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/incidentcompanion/Android.bp b/libs/incidentcompanion/Android.bp
new file mode 100644
index 0000000..45eab00
--- /dev/null
+++ b/libs/incidentcompanion/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.
+ */
+
+filegroup {
+    name: "incidentcompanion_aidl",
+    srcs: [
+        "binder/android/os/IIncidentAuthListener.aidl",
+        "binder/android/os/IIncidentCompanion.aidl",
+    ],
+    path: "binder",
+}
+
+cc_library_static {
+    name: "libincidentcompanion",
+    shared_libs: [
+        "libbinder",
+        "libutils",
+    ],
+    aidl: {
+        local_include_dirs: ["binder"],
+        export_aidl_headers: true,
+    },
+    srcs: [
+        ":incidentcompanion_aidl",
+    ],
+    export_include_dirs: ["binder"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-missing-field-initializers",
+        "-Wno-unused-variable",
+        "-Wunused-parameter",
+    ],
+}
+
diff --git a/cmds/dumpstate/binder/android/os/DumpstateOptions.aidl b/libs/incidentcompanion/binder/android/os/IIncidentAuthListener.aidl
similarity index 66%
rename from cmds/dumpstate/binder/android/os/DumpstateOptions.aidl
rename to libs/incidentcompanion/binder/android/os/IIncidentAuthListener.aidl
index c1a7f15..5484be8 100644
--- a/cmds/dumpstate/binder/android/os/DumpstateOptions.aidl
+++ b/libs/incidentcompanion/binder/android/os/IIncidentAuthListener.aidl
@@ -17,7 +17,18 @@
 package android.os;
 
 /**
-  * Specifies arguments for IDumpstate.
-  * {@hide}
-  */
-parcelable DumpstateOptions cpp_header "android/os/DumpstateOptions.h";
+ * Callback for IIncidentCompanion.
+ *
+ * @hide
+ */
+oneway interface IIncidentAuthListener {
+    /**
+     * The user approved the incident or bug report to be sent.
+     */
+    void onReportApproved();
+
+    /**
+     * The user did not approve the incident or bug report to be sent.
+     */
+    void onReportDenied();
+}
diff --git a/libs/incidentcompanion/binder/android/os/IIncidentCompanion.aidl b/libs/incidentcompanion/binder/android/os/IIncidentCompanion.aidl
new file mode 100644
index 0000000..6bf98d2
--- /dev/null
+++ b/libs/incidentcompanion/binder/android/os/IIncidentCompanion.aidl
@@ -0,0 +1,72 @@
+/**
+ * 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;
+
+import android.os.IIncidentAuthListener;
+
+/**
+ * Helper service for incidentd and dumpstated to provide user feedback
+ * and authorization for bug and inicdent reports to be taken.
+ *
+ * @hide
+ */
+interface IIncidentCompanion {
+    /**
+     * Request an authorization for an incident or bug report.
+     * // TODO(b/111441001): Add the permission
+     * <p>
+     * This function requires the ___ permission.
+     *
+     * @param callingUid The original application that requested the report.  This function
+     *      returns via the callback whether the application should be trusted.  It is up
+     *      to the caller to actually implement the restriction to take or not take
+     *      the incident or bug report.
+     * @param flags FLAG_CONFIRMATION_DIALOG (0x1) - to show this as a dialog.  Otherwise
+     *      a dialog will be shown as a notification.
+     * @param callback Interface to receive results.  The results may not come back for
+     *      a long (user's choice) time, or ever (if they never respond to the notification).
+     *      Authorization requests are not persisted across reboot.  It is up to the calling
+     *      service to request another authorization after reboot if they still would like
+     *      to send their report.
+     */
+    oneway void authorizeReport(int callingUid, String callingPackage,
+            int flags, IIncidentAuthListener callback);
+
+    /**
+     * Cancel an authorization.
+     */
+    oneway void cancelAuthorization(IIncidentAuthListener callback);
+
+    /**
+     * Return the list of pending approvals.
+     */
+    List<String> getPendingReports();
+
+    /**
+     * The user has authorized the report to be shared.
+     *
+     * @param uri the report.
+     */
+    void approveReport(String uri);
+
+    /**
+     * The user has denied the report from being shared.
+     *
+     * @param uri the report.
+     */
+    void denyReport(String uri);
+}
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index db86c8e..9fd25f9 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -29,6 +29,17 @@
 
 namespace android {
 
+const char* motionClassificationToString(MotionClassification classification) {
+    switch (classification) {
+        case MotionClassification::NONE:
+            return "NONE";
+        case MotionClassification::AMBIGUOUS_GESTURE:
+            return "AMBIGUOUS_GESTURE";
+        case MotionClassification::DEEP_PRESS:
+            return "DEEP_PRESS";
+    }
+}
+
 // --- InputEvent ---
 
 void InputEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId) {
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 0ddee44..0f7a1f0 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -157,6 +157,8 @@
             msg->body.motion.metaState = body.motion.metaState;
             // int32_t buttonState
             msg->body.motion.buttonState = body.motion.buttonState;
+            // MotionClassification classification
+            msg->body.motion.classification = body.motion.classification;
             // int32_t edgeFlags
             msg->body.motion.edgeFlags = body.motion.edgeFlags;
             // nsecs_t downTime
@@ -448,6 +450,7 @@
         int32_t edgeFlags,
         int32_t metaState,
         int32_t buttonState,
+        MotionClassification classification,
         float xOffset,
         float yOffset,
         float xPrecision,
@@ -461,13 +464,13 @@
     ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, "
             "displayId=%" PRId32 ", "
             "action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, "
-            "metaState=0x%x, buttonState=0x%x, xOffset=%f, yOffset=%f, "
+            "metaState=0x%x, buttonState=0x%x, classification=%s, xOffset=%f, yOffset=%f, "
             "xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", "
             "pointerCount=%" PRIu32,
             mChannel->getName().c_str(), seq,
             deviceId, source, displayId, action, actionButton, flags, edgeFlags, metaState,
-            buttonState, xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime,
-            pointerCount);
+            buttonState, motionClassificationToString(classification),
+            xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount);
 #endif
 
     if (!seq) {
@@ -493,6 +496,7 @@
     msg.body.motion.edgeFlags = edgeFlags;
     msg.body.motion.metaState = metaState;
     msg.body.motion.buttonState = buttonState;
+    msg.body.motion.classification = classification;
     msg.body.motion.xOffset = xOffset;
     msg.body.motion.yOffset = yOffset;
     msg.body.motion.xPrecision = xPrecision;
@@ -1112,7 +1116,7 @@
             msg->body.motion.edgeFlags,
             msg->body.motion.metaState,
             msg->body.motion.buttonState,
-            MotionClassification::NONE,
+            msg->body.motion.classification,
             msg->body.motion.xOffset,
             msg->body.motion.yOffset,
             msg->body.motion.xPrecision,
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/InputChannel_test.cpp b/libs/input/tests/InputChannel_test.cpp
index 96c165c..f1675c0 100644
--- a/libs/input/tests/InputChannel_test.cpp
+++ b/libs/input/tests/InputChannel_test.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <array>
+
 #include "TestHelpers.h"
 
 #include <unistd.h>
@@ -155,5 +157,37 @@
             << "sendMessage should have returned DEAD_OBJECT";
 }
 
+TEST_F(InputChannelTest, SendAndReceive_MotionClassification) {
+    sp<InputChannel> serverChannel, clientChannel;
+    status_t result = InputChannel::openInputChannelPair("channel name",
+            serverChannel, clientChannel);
+    ASSERT_EQ(OK, result)
+            << "should have successfully opened a channel pair";
+
+    std::array<MotionClassification, 3> classifications = {
+        MotionClassification::NONE,
+        MotionClassification::AMBIGUOUS_GESTURE,
+        MotionClassification::DEEP_PRESS,
+    };
+
+    InputMessage serverMsg = {}, clientMsg;
+    serverMsg.header.type = InputMessage::TYPE_MOTION;
+    serverMsg.body.motion.seq = 1;
+    serverMsg.body.motion.pointerCount = 1;
+
+    for (MotionClassification classification : classifications) {
+        // Send and receive a message with classification
+        serverMsg.body.motion.classification = classification;
+        EXPECT_EQ(OK, serverChannel->sendMessage(&serverMsg))
+                << "server channel should be able to send message to client channel";
+
+        EXPECT_EQ(OK, clientChannel->receiveMessage(&clientMsg))
+                << "client channel should be able to receive message from server channel";
+        EXPECT_EQ(serverMsg.header.type, clientMsg.header.type);
+        EXPECT_EQ(classification, clientMsg.body.motion.classification) <<
+                "Expected to receive " << motionClassificationToString(classification);
+    }
+}
+
 
 } // namespace android
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index e4c1514..2b75c82 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <array>
 #include <math.h>
 
 #include <binder/Parcel.h>
@@ -25,11 +26,7 @@
 // Default display id.
 static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
 
-class BaseTest : public testing::Test {
-protected:
-    virtual void SetUp() { }
-    virtual void TearDown() { }
-};
+class BaseTest : public testing::Test { };
 
 // --- PointerCoordsTest ---
 
@@ -316,6 +313,7 @@
     ASSERT_EQ(AMOTION_EVENT_EDGE_FLAG_TOP, event->getEdgeFlags());
     ASSERT_EQ(AMETA_ALT_ON, event->getMetaState());
     ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, event->getButtonState());
+    ASSERT_EQ(MotionClassification::NONE, event->getClassification());
     ASSERT_EQ(X_OFFSET, event->getXOffset());
     ASSERT_EQ(Y_OFFSET, event->getYOffset());
     ASSERT_EQ(2.0f, event->getXPrecision());
@@ -609,4 +607,30 @@
     ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001);
 }
 
+TEST_F(MotionEventTest, Initialize_SetsClassification) {
+    std::array<MotionClassification, 3> classifications = {
+            MotionClassification::NONE,
+            MotionClassification::AMBIGUOUS_GESTURE,
+            MotionClassification::DEEP_PRESS,
+    };
+
+    MotionEvent event;
+    constexpr size_t pointerCount = 1;
+    PointerProperties pointerProperties[pointerCount];
+    PointerCoords pointerCoords[pointerCount];
+    for (size_t i = 0; i < pointerCount; i++) {
+        pointerProperties[i].clear();
+        pointerProperties[i].id = i;
+        pointerCoords[i].clear();
+    }
+
+    for (MotionClassification classification : classifications) {
+        event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID,
+                AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0,
+                classification, 0, 0, 0, 0, 0 /*downTime*/, 0 /*eventTime*/,
+                pointerCount, pointerProperties, pointerCoords);
+        ASSERT_EQ(classification, event.getClassification());
+    }
+}
+
 } // namespace android
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index 0788c89..f2cd1be 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -141,6 +141,7 @@
     constexpr int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP;
     constexpr int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON;
     constexpr int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY;
+    constexpr MotionClassification classification = MotionClassification::AMBIGUOUS_GESTURE;
     constexpr float xOffset = -10;
     constexpr float yOffset = -20;
     constexpr float xPrecision = 0.25;
@@ -168,8 +169,8 @@
     }
 
     status = mPublisher->publishMotionEvent(seq, deviceId, source, displayId, action, actionButton,
-            flags, edgeFlags, metaState, buttonState, xOffset, yOffset, xPrecision, yPrecision,
-            downTime, eventTime, pointerCount,
+            flags, edgeFlags, metaState, buttonState, classification,
+            xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount,
             pointerProperties, pointerCoords);
     ASSERT_EQ(OK, status)
             << "publisher publishMotionEvent should return OK";
@@ -195,6 +196,7 @@
     EXPECT_EQ(edgeFlags, motionEvent->getEdgeFlags());
     EXPECT_EQ(metaState, motionEvent->getMetaState());
     EXPECT_EQ(buttonState, motionEvent->getButtonState());
+    EXPECT_EQ(classification, motionEvent->getClassification());
     EXPECT_EQ(xPrecision, motionEvent->getXPrecision());
     EXPECT_EQ(yPrecision, motionEvent->getYPrecision());
     EXPECT_EQ(downTime, motionEvent->getDownTime());
@@ -264,7 +266,8 @@
         pointerCoords[i].clear();
     }
 
-    status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            MotionClassification::NONE, 0, 0, 0, 0, 0, 0,
             pointerCount, pointerProperties, pointerCoords);
     ASSERT_EQ(BAD_VALUE, status)
             << "publisher publishMotionEvent should return BAD_VALUE";
@@ -276,7 +279,8 @@
     PointerProperties pointerProperties[pointerCount];
     PointerCoords pointerCoords[pointerCount];
 
-    status = mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    status = mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            MotionClassification::NONE, 0, 0, 0, 0, 0, 0,
             pointerCount, pointerProperties, pointerCoords);
     ASSERT_EQ(BAD_VALUE, status)
             << "publisher publishMotionEvent should return BAD_VALUE";
@@ -293,7 +297,8 @@
         pointerCoords[i].clear();
     }
 
-    status = mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    status = mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            MotionClassification::NONE, 0, 0, 0, 0, 0, 0,
             pointerCount, pointerProperties, pointerCoords);
     ASSERT_EQ(BAD_VALUE, status)
             << "publisher publishMotionEvent should return BAD_VALUE";
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/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 12a6782..62023fb 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -57,7 +57,8 @@
   CHECK_OFFSET(InputMessage::Body::Motion, flags, 36);
   CHECK_OFFSET(InputMessage::Body::Motion, metaState, 40);
   CHECK_OFFSET(InputMessage::Body::Motion, buttonState, 44);
-  CHECK_OFFSET(InputMessage::Body::Motion, edgeFlags, 48);
+  CHECK_OFFSET(InputMessage::Body::Motion, classification, 48);
+  CHECK_OFFSET(InputMessage::Body::Motion, edgeFlags, 52);
   CHECK_OFFSET(InputMessage::Body::Motion, downTime, 56);
   CHECK_OFFSET(InputMessage::Body::Motion, xOffset, 64);
   CHECK_OFFSET(InputMessage::Body::Motion, yOffset, 68);
diff --git a/libs/math/OWNERS b/libs/math/OWNERS
new file mode 100644
index 0000000..6fb149a
--- /dev/null
+++ b/libs/math/OWNERS
@@ -0,0 +1,6 @@
+jaesoo@google.com
+jiyong@google.com
+mathias@google.com
+pawin@google.com
+randolphs@google.com
+romainguy@google.com
diff --git a/libs/nativewindow/include/android/hdr_metadata.h b/libs/nativewindow/include/android/hdr_metadata.h
new file mode 100644
index 0000000..7e1313b
--- /dev/null
+++ b/libs/nativewindow/include/android/hdr_metadata.h
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+/**
+ * @file hdr_metadata.h
+ */
+
+#ifndef ANDROID_HDR_METADATA_H
+#define ANDROID_HDR_METADATA_H
+
+#include <inttypes.h>
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/**
+ * These structures are used to define the display's capabilities for HDR content.
+ * They can be used to better tone map content to user's display.
+ */
+
+/**
+ * Color is defined in CIE XYZ coordinates.
+ */
+struct AColor_xy {
+    float x;
+    float y;
+};
+
+/**
+ * SMPTE ST 2086 "Mastering Display Color Volume" static metadata
+ */
+struct AHdrMetadata_smpte2086 {
+    struct AColor_xy displayPrimaryRed;
+    struct AColor_xy displayPrimaryGreen;
+    struct AColor_xy displayPrimaryBlue;
+    struct AColor_xy whitePoint;
+    float maxLuminance;
+    float minLuminance;
+};
+
+/**
+ * CTA 861.3 "HDR Static Metadata Extension" static metadata
+ */
+struct AHdrMetadata_cta861_3 {
+    float maxContentLightLevel;
+    float maxFrameAverageLightLevel;
+};
+
+__END_DECLS
+
+#endif // ANDROID_HDR_METADATA_H
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 2915bb8..c5a9942 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -608,7 +608,17 @@
 }
 
 status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer,
-                                                     sp<Fence> bufferFence) {
+                                                     sp<Fence> bufferFence, bool readCache,
+                                                     bool persistCache) {
+    if (readCache) {
+        auto cachedImage = mImageCache.find(buffer->getId());
+
+        if (cachedImage != mImageCache.end()) {
+            bindExternalTextureImage(texName, *cachedImage->second);
+            return NO_ERROR;
+        }
+    }
+
     std::unique_ptr<Image> newImage = createImage();
 
     bool created = newImage->setNativeWindowBuffer(buffer->getNativeBuffer(),
@@ -644,9 +654,35 @@
         }
     }
 
+    // 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
+    // call drawLayers again, so it's just better to recreate the image again
+    // if needed when we draw.
+    if (persistCache) {
+        mImageCache.insert(std::make_pair(buffer->getId(), std::move(newImage)));
+    }
+
     return NO_ERROR;
 }
 
+void GLESRenderEngine::evictImages(const std::vector<LayerSettings>& layers) {
+    // destroy old image references that we're not going to draw with.
+    std::unordered_set<uint64_t> bufIds;
+    for (auto layer : layers) {
+        if (layer.source.buffer.buffer != nullptr) {
+            bufIds.emplace(layer.source.buffer.buffer->getId());
+        }
+    }
+
+    for (auto it = mImageCache.begin(); it != mImageCache.end();) {
+        if (bufIds.count(it->first) == 0) {
+            it = mImageCache.erase(it);
+        } else {
+            it++;
+        }
+    }
+}
+
 FloatRect GLESRenderEngine::setupLayerCropping(const LayerSettings& layer, Mesh& mesh) {
     // Translate win by the rounded corners rect coordinates, to have all values in
     // layer coordinate space.
@@ -748,6 +784,8 @@
         return fbo.getStatus();
     }
 
+    evictImages(layers);
+
     setViewportAndProjection(display.physicalDisplay, display.clip);
 
     setOutputDataSpace(display.outputDataspace);
@@ -781,8 +819,9 @@
 
             sp<GraphicBuffer> gBuf = layer.source.buffer.buffer;
 
+            bool readCache = layer.source.buffer.cacheHint == Buffer::CachingHint::USE_CACHE;
             bindExternalTextureBuffer(layer.source.buffer.textureName, gBuf,
-                                      layer.source.buffer.fence);
+                                      layer.source.buffer.fence, readCache, /*persistCache=*/true);
 
             usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha;
             Texture texture(Texture::TEXTURE_EXTERNAL, layer.source.buffer.textureName);
@@ -1280,7 +1319,7 @@
             std::lock_guard<std::mutex> lock(mMutex);
 
             mCondition.wait(mMutex,
-                            [&]() REQUIRES(mMutex) { return !mQueue.empty() && !mRunning; });
+                            [&]() REQUIRES(mMutex) { return !mQueue.empty() || !mRunning; });
 
             if (!mRunning) {
                 // if mRunning is false, then FlushTracer is being destroyed, so
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index b596242..e094860 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -30,6 +30,7 @@
 #include <GLES2/gl2.h>
 #include <renderengine/RenderEngine.h>
 #include <renderengine/private/Description.h>
+#include <unordered_map>
 
 #define EGL_NO_CONFIG ((EGLConfig)0)
 
@@ -133,7 +134,10 @@
     // Defines the viewport, and sets the projection matrix to the projection
     // defined by the clip.
     void setViewportAndProjection(Rect viewport, Rect clip);
-    status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer, sp<Fence> fence);
+    status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer, sp<Fence> fence,
+                                       bool readCache, bool persistCache);
+    // Evicts stale images from the buffer cache.
+    void evictImages(const std::vector<LayerSettings>& layers);
     // Computes the cropping window for the layer and sets up cropping
     // coordinates for the mesh.
     FloatRect setupLayerCropping(const LayerSettings& layer, Mesh& mesh);
@@ -179,6 +183,9 @@
     // supports sRGB, DisplayP3 color spaces.
     const bool mUseColorManagement = false;
 
+    // Cache of GL images that we'll store per GraphicBuffer ID
+    std::unordered_map<uint64_t, std::unique_ptr<Image>> mImageCache;
+
     class FlushTracer {
     public:
         FlushTracer(GLESRenderEngine* engine);
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 4d53205..56ac714 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -32,6 +32,16 @@
 
 // Metadata describing the input buffer to render from.
 struct Buffer {
+    // Hint for whether to use the Image cache or not.
+    // If NO_CACHE is specified, then upload the contents of the GraphicBuffer
+    // to the GPU, without checking against any implementation defined cache.
+    // If USE_CACHE is specified, then check against an implementation defined
+    // cache first. If there is an Image cached for the given GraphicBuffer id,
+    // then use that instead of the provided buffer contents. If there is no
+    // cached image or the RenderEngine implementation does not support caching,
+    // then use the GraphicBuffer contents.
+    enum class CachingHint { NO_CACHE, USE_CACHE };
+
     // Buffer containing the image that we will render.
     // If buffer == nullptr, then the rest of the fields in this struct will be
     // ignored.
@@ -40,6 +50,9 @@
     // Fence that will fire when the buffer is ready to be bound.
     sp<Fence> fence = nullptr;
 
+    // Caching hint to use when uploading buffer contents.
+    CachingHint cacheHint = CachingHint::NO_CACHE;
+
     // Texture identifier to bind the external texture to.
     // TODO(alecmouri): This is GL-specific...make the type backend-agnostic.
     uint32_t textureName = 0;
diff --git a/libs/sensor/SensorEventQueue.cpp b/libs/sensor/SensorEventQueue.cpp
index 46ba7c6..4438d45 100644
--- a/libs/sensor/SensorEventQueue.cpp
+++ b/libs/sensor/SensorEventQueue.cpp
@@ -29,6 +29,7 @@
 #include <sensor/ISensorEventConnection.h>
 
 #include <android/sensor.h>
+#include <hardware/sensors-base.h>
 
 using std::min;
 
@@ -188,6 +189,52 @@
     return;
 }
 
+ssize_t SensorEventQueue::filterEvents(ASensorEvent* events, size_t count) const {
+    // Check if this Sensor Event Queue is registered to receive each type of event. If it is not,
+    // then do not copy the event into the final buffer. Minimize the number of copy operations by
+    // finding consecutive sequences of events that the Sensor Event Queue should receive and only
+    // copying the events once an unregistered event type is reached.
+    bool intervalStartLocSet = false;
+    size_t intervalStartLoc = 0;
+    size_t eventsInInterval = 0;
+    ssize_t eventsCopied = 0;
+
+    for (size_t i = 0; i < count; i++) {
+        bool includeEvent =
+                (events[i].type != SENSOR_TYPE_ADDITIONAL_INFO || requestAdditionalInfo);
+
+        if (includeEvent) {
+            // Do not copy events yet since there may be more consecutive events that should be
+            // copied together. Track the start location and number of events in the current
+            // sequence.
+            if (!intervalStartLocSet) {
+                intervalStartLoc = i;
+                intervalStartLocSet = true;
+                eventsInInterval = 0;
+            }
+            eventsInInterval++;
+        }
+
+        // Shift the events from the already processed interval once an event that should not be
+        // included is reached or if this is the final event to be processed.
+        if (!includeEvent || (i + 1 == count)) {
+            // Only shift the events if the interval did not start with the first event. If the
+            // interval started with the first event, the events are already in their correct
+            // location.
+            if (intervalStartLoc > 0) {
+                memmove(&events[eventsCopied], &events[intervalStartLoc],
+                        eventsInInterval * sizeof(ASensorEvent));
+            }
+            eventsCopied += eventsInInterval;
+
+            // Reset the interval information
+            eventsInInterval = 0;
+            intervalStartLocSet = false;
+        }
+    }
+    return eventsCopied;
+}
+
 // ----------------------------------------------------------------------------
 }; // namespace android
 
diff --git a/libs/sensor/include/sensor/Sensor.h b/libs/sensor/include/sensor/Sensor.h
index 6926f7f..324d443 100644
--- a/libs/sensor/include/sensor/Sensor.h
+++ b/libs/sensor/include/sensor/Sensor.h
@@ -57,15 +57,15 @@
             uint8_t b[16];
             int64_t i64[2];
         };
-        uuid_t(const uint8_t (&uuid)[16]) { memcpy(b, uuid, sizeof(b));}
+        explicit uuid_t(const uint8_t (&uuid)[16]) { memcpy(b, uuid, sizeof(b));}
         uuid_t() : b{0} {}
     };
 
     Sensor(const Sensor&) = default;
     Sensor& operator=(const Sensor&) = default;
 
-    Sensor(const char * name = "");
-    Sensor(struct sensor_t const* hwSensor, int halVersion = 0);
+    explicit Sensor(const char * name = "");
+    explicit Sensor(struct sensor_t const* hwSensor, int halVersion = 0);
     Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersion = 0);
     ~Sensor();
 
diff --git a/libs/sensor/include/sensor/SensorEventQueue.h b/libs/sensor/include/sensor/SensorEventQueue.h
index baed2ee..8c3fde0 100644
--- a/libs/sensor/include/sensor/SensorEventQueue.h
+++ b/libs/sensor/include/sensor/SensorEventQueue.h
@@ -34,6 +34,7 @@
 // Concrete types for the NDK
 struct ASensorEventQueue {
     ALooper* looper;
+    bool requestAdditionalInfo;
 };
 
 // ----------------------------------------------------------------------------
@@ -64,7 +65,7 @@
     // Default sensor sample period
     static constexpr int32_t SENSOR_DELAY_NORMAL = 200000;
 
-    SensorEventQueue(const sp<ISensorEventConnection>& connection);
+    explicit SensorEventQueue(const sp<ISensorEventConnection>& connection);
     virtual ~SensorEventQueue();
     virtual void onFirstRef();
 
@@ -92,6 +93,13 @@
     void sendAck(const ASensorEvent* events, int count);
 
     status_t injectSensorEvent(const ASensorEvent& event);
+
+    // Filters the given sensor events in place and returns the new number of events.
+    //
+    // The filtering is controlled by ASensorEventQueue.requestAdditionalInfo, and if this value is
+    // false, then all SENSOR_TYPE_ADDITIONAL_INFO sensor events will be removed.
+    ssize_t filterEvents(ASensorEvent* events, size_t count) const;
+
 private:
     sp<Looper> getLooper() const;
     sp<ISensorEventConnection> mSensorEventConnection;
diff --git a/libs/sensor/include/sensor/SensorManager.h b/libs/sensor/include/sensor/SensorManager.h
index 23f7a91..f09c9c6 100644
--- a/libs/sensor/include/sensor/SensorManager.h
+++ b/libs/sensor/include/sensor/SensorManager.h
@@ -71,7 +71,7 @@
     void sensorManagerDied();
     static status_t waitForSensorService(sp<ISensorServer> *server);
 
-    SensorManager(const String16& opPackageName);
+    explicit SensorManager(const String16& opPackageName);
     status_t assertStateLocked();
 
 private:
diff --git a/libs/sensor/tests/Android.bp b/libs/sensor/tests/Android.bp
index 9fd84bc..c9a7668 100644
--- a/libs/sensor/tests/Android.bp
+++ b/libs/sensor/tests/Android.bp
@@ -21,6 +21,7 @@
 
     srcs: [
         "Sensor_test.cpp",
+        "SensorEventQueue_test.cpp",
     ],
 
     shared_libs: [
diff --git a/libs/sensor/tests/SensorEventQueue_test.cpp b/libs/sensor/tests/SensorEventQueue_test.cpp
new file mode 100644
index 0000000..1eb5883
--- /dev/null
+++ b/libs/sensor/tests/SensorEventQueue_test.cpp
@@ -0,0 +1,172 @@
+/*
+ * 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 <stdint.h>
+
+#include <gtest/gtest.h>
+#include <utils/Errors.h>
+
+#include <android/sensor.h>
+#include <hardware/sensors-base.h>
+#include <sensor/SensorManager.h>
+#include <sensor/SensorEventQueue.h>
+
+namespace android {
+
+class SensorEventQueueTest : public ::testing::Test {
+protected:
+    typedef std::vector<int32_t> Events;
+
+    SensorEventQueueTest() {};
+
+    virtual void SetUp() override {
+        SensorManager& manager = SensorManager::getInstanceForPackage(String16("SensorEventQueueTest"));
+        mQueue = manager.createEventQueue();
+    }
+
+    void configureAdditionalInfo(bool enable) {
+        mQueue->requestAdditionalInfo = enable;
+    }
+
+    Events filterEvents(const Events &types) const {
+        // Convert the events into SensorEvent array
+        ASensorEvent* events = new ASensorEvent[types.size()];
+        for (size_t i = 0; i < types.size(); i++) {
+            events[i].type = types[i];
+        }
+
+        // Filter the events
+        ssize_t filteredCount = mQueue->filterEvents(events, types.size());
+
+        // Copy the result into an output vector
+        Events result;
+        for (size_t i = 0; i < filteredCount; i++) {
+            result.push_back(events[i].type);
+        }
+        delete[] events;
+
+        return result;
+    }
+
+    Events getExpectedEvents(const Events &events) const {
+        Events output;
+        for (size_t i = 0; i != events.size(); i++) {
+            // Copy events if the event queue is configured to receive them
+            if (events[i] != SENSOR_TYPE_ADDITIONAL_INFO || mQueue->requestAdditionalInfo) {
+                output.push_back(events[i]);
+            }
+        }
+        return output;
+    }
+
+    void runFilterTest(const Events& events) {
+        Events filtered = filterEvents(events);
+        Events expected = getExpectedEvents(events);
+        EXPECT_EQ(expected.size(), filtered.size());
+        EXPECT_EQ(expected, filtered);
+    }
+
+private:
+    sp<SensorEventQueue> mQueue;
+};
+
+TEST_F(SensorEventQueueTest, FilterZeroEvents) {
+    configureAdditionalInfo(false /* enable */);
+    runFilterTest({});
+}
+
+TEST_F(SensorEventQueueTest, FilterEvents_ReceiveAdditionalInfo) {
+    configureAdditionalInfo(true /* enable */);
+    runFilterTest({SENSOR_TYPE_ADDITIONAL_INFO,
+                   SENSOR_TYPE_ACCELEROMETER,
+                   SENSOR_TYPE_GYROSCOPE,
+                   SENSOR_TYPE_ADDITIONAL_INFO,
+                   SENSOR_TYPE_ADDITIONAL_INFO,
+                   SENSOR_TYPE_MAGNETIC_FIELD});
+}
+
+TEST_F(SensorEventQueueTest, FilterEvents_RemoveAll) {
+    configureAdditionalInfo(false /* enable */);
+    runFilterTest({SENSOR_TYPE_ADDITIONAL_INFO,
+                   SENSOR_TYPE_ADDITIONAL_INFO,
+                   SENSOR_TYPE_ADDITIONAL_INFO});
+}
+
+TEST_F(SensorEventQueueTest, FilterEvents_RemoveFirst) {
+    configureAdditionalInfo(false /* enable */);
+    runFilterTest({SENSOR_TYPE_ADDITIONAL_INFO,
+                   SENSOR_TYPE_ACCELEROMETER,
+                   SENSOR_TYPE_GYROSCOPE,
+                   SENSOR_TYPE_MAGNETIC_FIELD});
+}
+
+TEST_F(SensorEventQueueTest, FilterEvents_RemoveAllButOne) {
+    configureAdditionalInfo(false /* enable */);
+    runFilterTest({SENSOR_TYPE_ADDITIONAL_INFO,
+                   SENSOR_TYPE_ADDITIONAL_INFO,
+                   SENSOR_TYPE_ACCELEROMETER,
+                   SENSOR_TYPE_ADDITIONAL_INFO});
+}
+
+TEST_F(SensorEventQueueTest, FilterEvents_RemoveLast) {
+    configureAdditionalInfo(false /* enable */);
+    runFilterTest({SENSOR_TYPE_ACCELEROMETER,
+                   SENSOR_TYPE_GYROSCOPE,
+                   SENSOR_TYPE_MAGNETIC_FIELD,
+                   SENSOR_TYPE_ADDITIONAL_INFO});
+}
+
+TEST_F(SensorEventQueueTest, FilterEvents_RemoveConsecutive) {
+    configureAdditionalInfo(false /* enable */);
+    runFilterTest({SENSOR_TYPE_MAGNETIC_FIELD,
+                   SENSOR_TYPE_ADDITIONAL_INFO,
+                   SENSOR_TYPE_ADDITIONAL_INFO,
+                   SENSOR_TYPE_ADDITIONAL_INFO,
+                   SENSOR_TYPE_ADDITIONAL_INFO,
+                   SENSOR_TYPE_ADDITIONAL_INFO,
+                   SENSOR_TYPE_ACCELEROMETER});
+}
+
+TEST_F(SensorEventQueueTest, FilterEvents_RemoveInterleaved) {
+    configureAdditionalInfo(false /* enable */);
+    runFilterTest({SENSOR_TYPE_ACCELEROMETER,
+                   SENSOR_TYPE_GYROSCOPE,
+                   SENSOR_TYPE_ADDITIONAL_INFO,
+                   SENSOR_TYPE_ACCELEROMETER,
+                   SENSOR_TYPE_GYROSCOPE,
+                   SENSOR_TYPE_ADDITIONAL_INFO,
+                   SENSOR_TYPE_MAGNETIC_FIELD});
+}
+
+TEST_F(SensorEventQueueTest, FilterEvents_ReconfigureAdditionalInfo) {
+    configureAdditionalInfo(false /* enable */);
+    const Events events = {SENSOR_TYPE_ACCELEROMETER,
+                           SENSOR_TYPE_GYROSCOPE,
+                           SENSOR_TYPE_ADDITIONAL_INFO,
+                           SENSOR_TYPE_MAGNETIC_FIELD,
+                           SENSOR_TYPE_ADDITIONAL_INFO};
+    runFilterTest(events);
+
+    // Update setting to request Additional Info
+    configureAdditionalInfo(true /* enable */);
+    runFilterTest(events);
+
+    // Update setting to stop requesting Additional Info
+    configureAdditionalInfo(true /* enable */);
+    runFilterTest(events);
+}
+
+} // namespace android
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index d089bf6..74450d7 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -60,7 +60,9 @@
         "Fence.cpp",
         "FenceTime.cpp",
         "FrameStats.cpp",
+        "Gralloc.cpp",
         "Gralloc2.cpp",
+        "Gralloc3.cpp",
         "GraphicBuffer.cpp",
         "GraphicBufferAllocator.cpp",
         "GraphicBufferMapper.cpp",
@@ -68,6 +70,7 @@
         "PixelFormat.cpp",
         "Rect.cpp",
         "Region.cpp",
+        "Size.cpp",
         "Transform.cpp",
         "UiConfig.cpp",
     ],
@@ -79,9 +82,11 @@
     shared_libs: [
         "android.frameworks.bufferhub@1.0",
         "android.hardware.graphics.allocator@2.0",
+        "android.hardware.graphics.allocator@3.0",
         "android.hardware.graphics.common@1.2",
         "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-utils",
         "libbase",
diff --git a/libs/ui/BufferHubBuffer.cpp b/libs/ui/BufferHubBuffer.cpp
index c70f188..4b3d3ba 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(dup(bufferTraits.bufferInfo->data[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());
@@ -314,14 +319,8 @@
 }
 
 bool BufferHubBuffer::IsValid() const {
-    // TODO(b/68770788): check eventFd once implemented
     return mBufferHandle.getNativeHandle() != nullptr && mId >= 0 && mClientStateMask != 0U &&
-            mMetadata.IsValid() && mBufferClient != nullptr;
-}
-
-// TODO(b/68770788): implement Poll() once we have event fd
-int BufferHubBuffer::Poll(int /* timeoutMs */) {
-    return -ENOSYS;
+            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/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/libs/ui/Gralloc.cpp
similarity index 67%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to libs/ui/Gralloc.cpp
index e6ac6bf..8e09a13 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/libs/ui/Gralloc.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#define LOG_TAG "Gralloc"
+
+#include <ui/Gralloc.h>
 
 namespace android {
-namespace mock {
 
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+GrallocMapper::~GrallocMapper() {}
 
-} // namespace mock
+GrallocAllocator::~GrallocAllocator() {}
+
 } // namespace android
diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp
index 20f27c5..2c4b5f3 100644
--- a/libs/ui/Gralloc2.cpp
+++ b/libs/ui/Gralloc2.cpp
@@ -27,9 +27,15 @@
 #include <sync/sync.h>
 #pragma clang diagnostic pop
 
-namespace android {
+using android::hardware::graphics::allocator::V2_0::IAllocator;
+using android::hardware::graphics::common::V1_1::BufferUsage;
+using android::hardware::graphics::common::V1_1::PixelFormat;
+using android::hardware::graphics::mapper::V2_0::BufferDescriptor;
+using android::hardware::graphics::mapper::V2_0::Error;
+using android::hardware::graphics::mapper::V2_0::YCbCrLayout;
+using android::hardware::graphics::mapper::V2_1::IMapper;
 
-namespace Gralloc2 {
+namespace android {
 
 namespace {
 
@@ -59,17 +65,26 @@
     return valid11UsageBits;
 }
 
+static inline IMapper::Rect sGralloc2Rect(const Rect& rect) {
+    IMapper::Rect outRect{};
+    outRect.left = rect.left;
+    outRect.top = rect.top;
+    outRect.width = rect.width();
+    outRect.height = rect.height();
+    return outRect;
+}
+
 }  // anonymous namespace
 
-void Mapper::preload() {
+void Gralloc2Mapper::preload() {
     android::hardware::preloadPassthroughService<hardware::graphics::mapper::V2_0::IMapper>();
 }
 
-Mapper::Mapper()
-{
+Gralloc2Mapper::Gralloc2Mapper() {
     mMapper = hardware::graphics::mapper::V2_0::IMapper::getService();
     if (mMapper == nullptr) {
-        LOG_ALWAYS_FATAL("gralloc-mapper is missing");
+        ALOGW("mapper 2.x is not supported");
+        return;
     }
     if (mMapper->isRemote()) {
         LOG_ALWAYS_FATAL("gralloc-mapper must be in passthrough mode");
@@ -79,30 +94,37 @@
     mMapperV2_1 = IMapper::castFrom(mMapper);
 }
 
-Gralloc2::Error Mapper::validateBufferDescriptorInfo(
-        const IMapper::BufferDescriptorInfo& descriptorInfo) const {
+bool Gralloc2Mapper::isSupported() const {
+    return mMapper != nullptr;
+}
+
+status_t Gralloc2Mapper::validateBufferDescriptorInfo(
+        IMapper::BufferDescriptorInfo* descriptorInfo) const {
     uint64_t validUsageBits = getValid10UsageBits();
     if (mMapperV2_1 != nullptr) {
         validUsageBits = validUsageBits | getValid11UsageBits();
     }
 
-    if (descriptorInfo.usage & ~validUsageBits) {
+    if (descriptorInfo->usage & ~validUsageBits) {
         ALOGE("buffer descriptor contains invalid usage bits 0x%" PRIx64,
-              descriptorInfo.usage & ~validUsageBits);
-        return Error::BAD_VALUE;
+              descriptorInfo->usage & ~validUsageBits);
+        return BAD_VALUE;
     }
-    return Error::NONE;
+    return NO_ERROR;
 }
 
-Error Mapper::createDescriptor(
-        const IMapper::BufferDescriptorInfo& descriptorInfo,
-        BufferDescriptor* outDescriptor) const
-{
-    Error error = validateBufferDescriptorInfo(descriptorInfo);
-    if (error != Error::NONE) {
-        return error;
+status_t Gralloc2Mapper::createDescriptor(void* bufferDescriptorInfo,
+                                          void* outBufferDescriptor) const {
+    IMapper::BufferDescriptorInfo* descriptorInfo =
+            static_cast<IMapper::BufferDescriptorInfo*>(bufferDescriptorInfo);
+    BufferDescriptor* outDescriptor = static_cast<BufferDescriptor*>(outBufferDescriptor);
+
+    status_t status = validateBufferDescriptorInfo(descriptorInfo);
+    if (status != NO_ERROR) {
+        return status;
     }
 
+    Error error;
     auto hidl_cb = [&](const auto& tmpError, const auto& tmpDescriptor)
                    {
                        error = tmpError;
@@ -115,24 +137,23 @@
 
     hardware::Return<void> ret;
     if (mMapperV2_1 != nullptr) {
-        ret = mMapperV2_1->createDescriptor_2_1(descriptorInfo, hidl_cb);
+        ret = mMapperV2_1->createDescriptor_2_1(*descriptorInfo, hidl_cb);
     } else {
         const hardware::graphics::mapper::V2_0::IMapper::BufferDescriptorInfo info = {
-            descriptorInfo.width,
-            descriptorInfo.height,
-            descriptorInfo.layerCount,
-            static_cast<hardware::graphics::common::V1_0::PixelFormat>(descriptorInfo.format),
-            descriptorInfo.usage,
+                descriptorInfo->width,
+                descriptorInfo->height,
+                descriptorInfo->layerCount,
+                static_cast<hardware::graphics::common::V1_0::PixelFormat>(descriptorInfo->format),
+                descriptorInfo->usage,
         };
         ret = mMapper->createDescriptor(info, hidl_cb);
     }
 
-    return (ret.isOk()) ? error : kTransactionError;
+    return static_cast<status_t>((ret.isOk()) ? error : kTransactionError);
 }
 
-Error Mapper::importBuffer(const hardware::hidl_handle& rawHandle,
-        buffer_handle_t* outBufferHandle) const
-{
+status_t Gralloc2Mapper::importBuffer(const hardware::hidl_handle& rawHandle,
+                                      buffer_handle_t* outBufferHandle) const {
     Error error;
     auto ret = mMapper->importBuffer(rawHandle,
             [&](const auto& tmpError, const auto& tmpBuffer)
@@ -145,11 +166,10 @@
                 *outBufferHandle = static_cast<buffer_handle_t>(tmpBuffer);
             });
 
-    return (ret.isOk()) ? error : kTransactionError;
+    return static_cast<status_t>((ret.isOk()) ? error : kTransactionError);
 }
 
-void Mapper::freeBuffer(buffer_handle_t bufferHandle) const
-{
+void Gralloc2Mapper::freeBuffer(buffer_handle_t bufferHandle) const {
     auto buffer = const_cast<native_handle_t*>(bufferHandle);
     auto ret = mMapper->freeBuffer(buffer);
 
@@ -158,23 +178,29 @@
             buffer, error);
 }
 
-Error Mapper::validateBufferSize(buffer_handle_t bufferHandle,
-        const IMapper::BufferDescriptorInfo& descriptorInfo,
-        uint32_t stride) const
-{
+status_t Gralloc2Mapper::validateBufferSize(buffer_handle_t bufferHandle, uint32_t width,
+                                            uint32_t height, android::PixelFormat format,
+                                            uint32_t layerCount, uint64_t usage,
+                                            uint32_t stride) const {
     if (mMapperV2_1 == nullptr) {
-        return Error::NONE;
+        return NO_ERROR;
     }
 
+    IMapper::BufferDescriptorInfo descriptorInfo = {};
+    descriptorInfo.width = width;
+    descriptorInfo.height = height;
+    descriptorInfo.layerCount = layerCount;
+    descriptorInfo.format = static_cast<hardware::graphics::common::V1_1::PixelFormat>(format);
+    descriptorInfo.usage = usage;
+
     auto buffer = const_cast<native_handle_t*>(bufferHandle);
     auto ret = mMapperV2_1->validateBufferSize(buffer, descriptorInfo, stride);
 
-    return (ret.isOk()) ? static_cast<Error>(ret) : kTransactionError;
+    return static_cast<status_t>((ret.isOk()) ? static_cast<Error>(ret) : kTransactionError);
 }
 
-void Mapper::getTransportSize(buffer_handle_t bufferHandle,
-        uint32_t* outNumFds, uint32_t* outNumInts) const
-{
+void Gralloc2Mapper::getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds,
+                                      uint32_t* outNumInts) const {
     *outNumFds = uint32_t(bufferHandle->numFds);
     *outNumInts = uint32_t(bufferHandle->numInts);
 
@@ -195,19 +221,24 @@
                 *outNumInts = tmpNumInts;
             });
 
-    if (!ret.isOk()) {
-        error = kTransactionError;
-    }
-    ALOGE_IF(error != Error::NONE, "getTransportSize(%p) failed with %d",
-            buffer, error);
+    error = (ret.isOk()) ? error : kTransactionError;
+
+    ALOGE_IF(error != Error::NONE, "getTransportSize(%p) failed with %d", buffer, error);
 }
 
-Error Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage,
-        const IMapper::Rect& accessRegion,
-        int acquireFence, void** outData) const
-{
+status_t Gralloc2Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+                              int acquireFence, void** outData, int32_t* outBytesPerPixel,
+                              int32_t* outBytesPerStride) const {
+    if (outBytesPerPixel) {
+        *outBytesPerPixel = -1;
+    }
+    if (outBytesPerStride) {
+        *outBytesPerStride = -1;
+    }
     auto buffer = const_cast<native_handle_t*>(bufferHandle);
 
+    IMapper::Rect accessRegion = sGralloc2Rect(bounds);
+
     // put acquireFence in a hidl_handle
     hardware::hidl_handle acquireFenceHandle;
     NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0);
@@ -234,15 +265,19 @@
         close(acquireFence);
     }
 
-    return (ret.isOk()) ? error : kTransactionError;
+    error = (ret.isOk()) ? error : kTransactionError;
+
+    ALOGW_IF(error != Error::NONE, "lock(%p, ...) failed: %d", bufferHandle, error);
+
+    return static_cast<status_t>(error);
 }
 
-Error Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage,
-        const IMapper::Rect& accessRegion,
-        int acquireFence, YCbCrLayout* outLayout) const
-{
+status_t Gralloc2Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+                              int acquireFence, android_ycbcr* ycbcr) const {
     auto buffer = const_cast<native_handle_t*>(bufferHandle);
 
+    IMapper::Rect accessRegion = sGralloc2Rect(bounds);
+
     // put acquireFence in a hidl_handle
     hardware::hidl_handle acquireFenceHandle;
     NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0);
@@ -252,6 +287,7 @@
         acquireFenceHandle = h;
     }
 
+    YCbCrLayout layout;
     Error error;
     auto ret = mMapper->lockYCbCr(buffer, usage, accessRegion,
             acquireFenceHandle,
@@ -262,19 +298,27 @@
                     return;
                 }
 
-                *outLayout = tmpLayout;
+                layout = tmpLayout;
             });
 
+    if (error == Error::NONE) {
+        ycbcr->y = layout.y;
+        ycbcr->cb = layout.cb;
+        ycbcr->cr = layout.cr;
+        ycbcr->ystride = static_cast<size_t>(layout.yStride);
+        ycbcr->cstride = static_cast<size_t>(layout.cStride);
+        ycbcr->chroma_step = static_cast<size_t>(layout.chromaStep);
+    }
+
     // we own acquireFence even on errors
     if (acquireFence >= 0) {
         close(acquireFence);
     }
 
-    return (ret.isOk()) ? error : kTransactionError;
+    return static_cast<status_t>((ret.isOk()) ? error : kTransactionError);
 }
 
-int Mapper::unlock(buffer_handle_t bufferHandle) const
-{
+int Gralloc2Mapper::unlock(buffer_handle_t bufferHandle) const {
     auto buffer = const_cast<native_handle_t*>(bufferHandle);
 
     int releaseFence = -1;
@@ -299,10 +343,7 @@
                 }
             });
 
-    if (!ret.isOk()) {
-        error = kTransactionError;
-    }
-
+    error = (ret.isOk()) ? error : kTransactionError;
     if (error != Error::NONE) {
         ALOGE("unlock(%p) failed with %d", buffer, error);
     }
@@ -310,17 +351,19 @@
     return releaseFence;
 }
 
-Allocator::Allocator(const Mapper& mapper)
-    : mMapper(mapper)
-{
+Gralloc2Allocator::Gralloc2Allocator(const Gralloc2Mapper& mapper) : mMapper(mapper) {
     mAllocator = IAllocator::getService();
     if (mAllocator == nullptr) {
-        LOG_ALWAYS_FATAL("gralloc-alloc is missing");
+        ALOGW("allocator 2.x is not supported");
+        return;
     }
 }
 
-std::string Allocator::dumpDebugInfo() const
-{
+bool Gralloc2Allocator::isSupported() const {
+    return mAllocator != nullptr;
+}
+
+std::string Gralloc2Allocator::dumpDebugInfo() const {
     std::string debugInfo;
 
     mAllocator->dumpDebugInfo([&](const auto& tmpDebugInfo) {
@@ -330,40 +373,51 @@
     return debugInfo;
 }
 
-Error Allocator::allocate(BufferDescriptor descriptor, uint32_t count,
-        uint32_t* outStride, buffer_handle_t* outBufferHandles) const
-{
-    Error error;
-    auto ret = mAllocator->allocate(descriptor, count,
-            [&](const auto& tmpError, const auto& tmpStride,
-                const auto& tmpBuffers) {
-                error = tmpError;
-                if (tmpError != Error::NONE) {
-                    return;
-                }
+status_t Gralloc2Allocator::allocate(uint32_t width, uint32_t height, PixelFormat format,
+                                     uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
+                                     uint32_t* outStride, buffer_handle_t* outBufferHandles) const {
+    IMapper::BufferDescriptorInfo descriptorInfo = {};
+    descriptorInfo.width = width;
+    descriptorInfo.height = height;
+    descriptorInfo.layerCount = layerCount;
+    descriptorInfo.format = static_cast<hardware::graphics::common::V1_1::PixelFormat>(format);
+    descriptorInfo.usage = usage;
 
-                // import buffers
-                for (uint32_t i = 0; i < count; i++) {
-                    error = mMapper.importBuffer(tmpBuffers[i],
-                            &outBufferHandles[i]);
-                    if (error != Error::NONE) {
-                        for (uint32_t j = 0; j < i; j++) {
-                            mMapper.freeBuffer(outBufferHandles[j]);
-                            outBufferHandles[j] = nullptr;
-                        }
-                        return;
-                    }
-                }
+    BufferDescriptor descriptor;
+    status_t error = mMapper.createDescriptor(static_cast<void*>(&descriptorInfo),
+                                              static_cast<void*>(&descriptor));
+    if (error != NO_ERROR) {
+        return error;
+    }
 
-                *outStride = tmpStride;
-            });
+    auto ret = mAllocator->allocate(descriptor, bufferCount,
+                                    [&](const auto& tmpError, const auto& tmpStride,
+                                        const auto& tmpBuffers) {
+                                        error = static_cast<status_t>(tmpError);
+                                        if (tmpError != Error::NONE) {
+                                            return;
+                                        }
+
+                                        // import buffers
+                                        for (uint32_t i = 0; i < bufferCount; i++) {
+                                            error = mMapper.importBuffer(tmpBuffers[i],
+                                                                         &outBufferHandles[i]);
+                                            if (error != NO_ERROR) {
+                                                for (uint32_t j = 0; j < i; j++) {
+                                                    mMapper.freeBuffer(outBufferHandles[j]);
+                                                    outBufferHandles[j] = nullptr;
+                                                }
+                                                return;
+                                            }
+                                        }
+
+                                        *outStride = tmpStride;
+                                    });
 
     // make sure the kernel driver sees BC_FREE_BUFFER and closes the fds now
     hardware::IPCThreadState::self()->flushCommands();
 
-    return (ret.isOk()) ? error : kTransactionError;
+    return (ret.isOk()) ? error : static_cast<status_t>(kTransactionError);
 }
 
-} // namespace Gralloc2
-
 } // namespace android
diff --git a/libs/ui/Gralloc3.cpp b/libs/ui/Gralloc3.cpp
new file mode 100644
index 0000000..acb6b01
--- /dev/null
+++ b/libs/ui/Gralloc3.cpp
@@ -0,0 +1,379 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "Gralloc3"
+
+#include <hidl/ServiceManagement.h>
+#include <hwbinder/IPCThreadState.h>
+#include <ui/Gralloc3.h>
+
+#include <inttypes.h>
+#include <log/log.h>
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wzero-length-array"
+#include <sync/sync.h>
+#pragma clang diagnostic pop
+
+using android::hardware::graphics::allocator::V3_0::IAllocator;
+using android::hardware::graphics::common::V1_1::BufferUsage;
+using android::hardware::graphics::mapper::V3_0::BufferDescriptor;
+using android::hardware::graphics::mapper::V3_0::Error;
+using android::hardware::graphics::mapper::V3_0::IMapper;
+using android::hardware::graphics::mapper::V3_0::YCbCrLayout;
+
+namespace android {
+
+namespace {
+
+static constexpr Error kTransactionError = Error::NO_RESOURCES;
+
+uint64_t getValidUsageBits() {
+    static const uint64_t validUsageBits = []() -> uint64_t {
+        uint64_t bits = 0;
+        for (const auto bit :
+             hardware::hidl_enum_range<hardware::graphics::common::V1_0::BufferUsage>()) {
+            bits = bits | bit;
+        }
+        for (const auto bit :
+             hardware::hidl_enum_range<hardware::graphics::common::V1_1::BufferUsage>()) {
+            bits = bits | bit;
+        }
+        return bits;
+    }();
+    return validUsageBits;
+}
+
+static inline IMapper::Rect sGralloc3Rect(const Rect& rect) {
+    IMapper::Rect outRect{};
+    outRect.left = rect.left;
+    outRect.top = rect.top;
+    outRect.width = rect.width();
+    outRect.height = rect.height();
+    return outRect;
+}
+static inline void sBufferDescriptorInfo(uint32_t width, uint32_t height,
+                                         android::PixelFormat format, uint32_t layerCount,
+                                         uint64_t usage,
+                                         IMapper::BufferDescriptorInfo* outDescriptorInfo) {
+    outDescriptorInfo->width = width;
+    outDescriptorInfo->height = height;
+    outDescriptorInfo->layerCount = layerCount;
+    outDescriptorInfo->format = static_cast<hardware::graphics::common::V1_1::PixelFormat>(format);
+    outDescriptorInfo->usage = usage;
+}
+
+} // anonymous namespace
+
+void Gralloc3Mapper::preload() {
+    android::hardware::preloadPassthroughService<IMapper>();
+}
+
+Gralloc3Mapper::Gralloc3Mapper() {
+    mMapper = IMapper::getService();
+    if (mMapper == nullptr) {
+        ALOGW("mapper 3.x is not supported");
+        return;
+    }
+    if (mMapper->isRemote()) {
+        LOG_ALWAYS_FATAL("gralloc-mapper must be in passthrough mode");
+    }
+}
+
+bool Gralloc3Mapper::isSupported() const {
+    return mMapper != nullptr;
+}
+
+status_t Gralloc3Mapper::validateBufferDescriptorInfo(
+        IMapper::BufferDescriptorInfo* descriptorInfo) const {
+    uint64_t validUsageBits = getValidUsageBits();
+
+    if (descriptorInfo->usage & ~validUsageBits) {
+        ALOGE("buffer descriptor contains invalid usage bits 0x%" PRIx64,
+              descriptorInfo->usage & ~validUsageBits);
+        return BAD_VALUE;
+    }
+    return NO_ERROR;
+}
+
+status_t Gralloc3Mapper::createDescriptor(void* bufferDescriptorInfo,
+                                          void* outBufferDescriptor) const {
+    IMapper::BufferDescriptorInfo* descriptorInfo =
+            static_cast<IMapper::BufferDescriptorInfo*>(bufferDescriptorInfo);
+    BufferDescriptor* outDescriptor = static_cast<BufferDescriptor*>(outBufferDescriptor);
+
+    status_t status = validateBufferDescriptorInfo(descriptorInfo);
+    if (status != NO_ERROR) {
+        return status;
+    }
+
+    Error error;
+    auto hidl_cb = [&](const auto& tmpError, const auto& tmpDescriptor) {
+        error = tmpError;
+        if (error != Error::NONE) {
+            return;
+        }
+        *outDescriptor = tmpDescriptor;
+    };
+
+    hardware::Return<void> ret = mMapper->createDescriptor(*descriptorInfo, hidl_cb);
+
+    return static_cast<status_t>((ret.isOk()) ? error : kTransactionError);
+}
+
+status_t Gralloc3Mapper::importBuffer(const hardware::hidl_handle& rawHandle,
+                                      buffer_handle_t* outBufferHandle) const {
+    Error error;
+    auto ret = mMapper->importBuffer(rawHandle, [&](const auto& tmpError, const auto& tmpBuffer) {
+        error = tmpError;
+        if (error != Error::NONE) {
+            return;
+        }
+        *outBufferHandle = static_cast<buffer_handle_t>(tmpBuffer);
+    });
+
+    return static_cast<status_t>((ret.isOk()) ? error : kTransactionError);
+}
+
+void Gralloc3Mapper::freeBuffer(buffer_handle_t bufferHandle) const {
+    auto buffer = const_cast<native_handle_t*>(bufferHandle);
+    auto ret = mMapper->freeBuffer(buffer);
+
+    auto error = (ret.isOk()) ? static_cast<Error>(ret) : kTransactionError;
+    ALOGE_IF(error != Error::NONE, "freeBuffer(%p) failed with %d", buffer, error);
+}
+
+status_t Gralloc3Mapper::validateBufferSize(buffer_handle_t bufferHandle, uint32_t width,
+                                            uint32_t height, android::PixelFormat format,
+                                            uint32_t layerCount, uint64_t usage,
+                                            uint32_t stride) const {
+    IMapper::BufferDescriptorInfo descriptorInfo;
+    sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
+
+    auto buffer = const_cast<native_handle_t*>(bufferHandle);
+    auto ret = mMapper->validateBufferSize(buffer, descriptorInfo, stride);
+
+    return static_cast<status_t>((ret.isOk()) ? static_cast<Error>(ret) : kTransactionError);
+}
+
+void Gralloc3Mapper::getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds,
+                                      uint32_t* outNumInts) const {
+    *outNumFds = uint32_t(bufferHandle->numFds);
+    *outNumInts = uint32_t(bufferHandle->numInts);
+
+    Error error;
+    auto buffer = const_cast<native_handle_t*>(bufferHandle);
+    auto ret = mMapper->getTransportSize(buffer,
+                                         [&](const auto& tmpError, const auto& tmpNumFds,
+                                             const auto& tmpNumInts) {
+                                             error = tmpError;
+                                             if (error != Error::NONE) {
+                                                 return;
+                                             }
+                                             *outNumFds = tmpNumFds;
+                                             *outNumInts = tmpNumInts;
+                                         });
+
+    error = (ret.isOk()) ? error : kTransactionError;
+
+    ALOGE_IF(error != Error::NONE, "getTransportSize(%p) failed with %d", buffer, error);
+}
+
+status_t Gralloc3Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+                              int acquireFence, void** outData, int32_t* outBytesPerPixel,
+                              int32_t* outBytesPerStride) const {
+    auto buffer = const_cast<native_handle_t*>(bufferHandle);
+
+    IMapper::Rect accessRegion = sGralloc3Rect(bounds);
+
+    // put acquireFence in a hidl_handle
+    hardware::hidl_handle acquireFenceHandle;
+    NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0);
+    if (acquireFence >= 0) {
+        auto h = native_handle_init(acquireFenceStorage, 1, 0);
+        h->data[0] = acquireFence;
+        acquireFenceHandle = h;
+    }
+
+    Error error;
+    auto ret = mMapper->lock(buffer, usage, accessRegion, acquireFenceHandle,
+                             [&](const auto& tmpError, const auto& tmpData,
+                                 const auto& tmpBytesPerPixel, const auto& tmpBytesPerStride) {
+                                 error = tmpError;
+                                 if (error != Error::NONE) {
+                                     return;
+                                 }
+                                 *outData = tmpData;
+                                 if (outBytesPerPixel) {
+                                     *outBytesPerPixel = tmpBytesPerPixel;
+                                 }
+                                 if (outBytesPerStride) {
+                                     *outBytesPerStride = tmpBytesPerStride;
+                                 }
+                             });
+
+    // we own acquireFence even on errors
+    if (acquireFence >= 0) {
+        close(acquireFence);
+    }
+
+    error = (ret.isOk()) ? error : kTransactionError;
+
+    ALOGW_IF(error != Error::NONE, "lock(%p, ...) failed: %d", bufferHandle, error);
+
+    return static_cast<status_t>(error);
+}
+
+status_t Gralloc3Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+                              int acquireFence, android_ycbcr* ycbcr) const {
+    auto buffer = const_cast<native_handle_t*>(bufferHandle);
+
+    IMapper::Rect accessRegion = sGralloc3Rect(bounds);
+
+    // put acquireFence in a hidl_handle
+    hardware::hidl_handle acquireFenceHandle;
+    NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0);
+    if (acquireFence >= 0) {
+        auto h = native_handle_init(acquireFenceStorage, 1, 0);
+        h->data[0] = acquireFence;
+        acquireFenceHandle = h;
+    }
+
+    YCbCrLayout layout;
+    Error error;
+    auto ret = mMapper->lockYCbCr(buffer, usage, accessRegion, acquireFenceHandle,
+                                  [&](const auto& tmpError, const auto& tmpLayout) {
+                                      error = tmpError;
+                                      if (error != Error::NONE) {
+                                          return;
+                                      }
+
+                                      layout = tmpLayout;
+                                  });
+
+    if (error == Error::NONE) {
+        ycbcr->y = layout.y;
+        ycbcr->cb = layout.cb;
+        ycbcr->cr = layout.cr;
+        ycbcr->ystride = static_cast<size_t>(layout.yStride);
+        ycbcr->cstride = static_cast<size_t>(layout.cStride);
+        ycbcr->chroma_step = static_cast<size_t>(layout.chromaStep);
+    }
+
+    // we own acquireFence even on errors
+    if (acquireFence >= 0) {
+        close(acquireFence);
+    }
+
+    return static_cast<status_t>((ret.isOk()) ? error : kTransactionError);
+}
+
+int Gralloc3Mapper::unlock(buffer_handle_t bufferHandle) const {
+    auto buffer = const_cast<native_handle_t*>(bufferHandle);
+
+    int releaseFence = -1;
+    Error error;
+    auto ret = mMapper->unlock(buffer, [&](const auto& tmpError, const auto& tmpReleaseFence) {
+        error = tmpError;
+        if (error != Error::NONE) {
+            return;
+        }
+
+        auto fenceHandle = tmpReleaseFence.getNativeHandle();
+        if (fenceHandle && fenceHandle->numFds == 1) {
+            int fd = dup(fenceHandle->data[0]);
+            if (fd >= 0) {
+                releaseFence = fd;
+            } else {
+                ALOGD("failed to dup unlock release fence");
+                sync_wait(fenceHandle->data[0], -1);
+            }
+        }
+    });
+
+    if (!ret.isOk()) {
+        error = kTransactionError;
+    }
+
+    if (error != Error::NONE) {
+        ALOGE("unlock(%p) failed with %d", buffer, error);
+    }
+
+    return releaseFence;
+}
+
+Gralloc3Allocator::Gralloc3Allocator(const Gralloc3Mapper& mapper) : mMapper(mapper) {
+    mAllocator = IAllocator::getService();
+    if (mAllocator == nullptr) {
+        ALOGW("allocator 3.x is not supported");
+        return;
+    }
+}
+
+bool Gralloc3Allocator::isSupported() const {
+    return mAllocator != nullptr;
+}
+
+std::string Gralloc3Allocator::dumpDebugInfo() const {
+    std::string debugInfo;
+
+    mAllocator->dumpDebugInfo([&](const auto& tmpDebugInfo) { debugInfo = tmpDebugInfo.c_str(); });
+
+    return debugInfo;
+}
+
+status_t Gralloc3Allocator::allocate(uint32_t width, uint32_t height, android::PixelFormat format,
+                                     uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
+                                     uint32_t* outStride, buffer_handle_t* outBufferHandles) const {
+    IMapper::BufferDescriptorInfo descriptorInfo;
+    sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
+
+    BufferDescriptor descriptor;
+    status_t error = mMapper.createDescriptor(static_cast<void*>(&descriptorInfo),
+                                              static_cast<void*>(&descriptor));
+    if (error != NO_ERROR) {
+        return error;
+    }
+
+    auto ret = mAllocator->allocate(descriptor, bufferCount,
+                                    [&](const auto& tmpError, const auto& tmpStride,
+                                        const auto& tmpBuffers) {
+                                        error = static_cast<status_t>(tmpError);
+                                        if (tmpError != Error::NONE) {
+                                            return;
+                                        }
+
+                                        // import buffers
+                                        for (uint32_t i = 0; i < bufferCount; i++) {
+                                            error = mMapper.importBuffer(tmpBuffers[i],
+                                                                         &outBufferHandles[i]);
+                                            if (error != NO_ERROR) {
+                                                for (uint32_t j = 0; j < i; j++) {
+                                                    mMapper.freeBuffer(outBufferHandles[j]);
+                                                    outBufferHandles[j] = nullptr;
+                                                }
+                                                return;
+                                            }
+                                        }
+                                        *outStride = tmpStride;
+                                    });
+
+    // make sure the kernel driver sees BC_FREE_BUFFER and closes the fds now
+    hardware::IPCThreadState::self()->flushCommands();
+
+    return (ret.isOk()) ? error : static_cast<status_t>(kTransactionError);
+}
+
+} // namespace android
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index f408fcb..da24cf1 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -252,7 +252,10 @@
                 width, height);
         return BAD_VALUE;
     }
-    status_t res = getBufferMapper().lock(handle, inUsage, rect, vaddr);
+    int32_t bytesPerPixel, bytesPerStride;
+
+    status_t res =
+            getBufferMapper().lock(handle, inUsage, rect, vaddr, &bytesPerPixel, &bytesPerStride);
     return res;
 }
 
@@ -306,8 +309,10 @@
                 width, height);
         return BAD_VALUE;
     }
-    status_t res = getBufferMapper().lockAsync(handle, inProducerUsage,
-            inConsumerUsage, rect, vaddr, fenceFd);
+
+    int32_t bytesPerPixel, bytesPerStride;
+    status_t res = getBufferMapper().lockAsync(handle, inProducerUsage, inConsumerUsage, rect,
+                                               vaddr, fenceFd, &bytesPerPixel, &bytesPerStride);
     return res;
 }
 
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index f56e6b9..efb5798 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -29,7 +29,9 @@
 #include <utils/Singleton.h>
 #include <utils/Trace.h>
 
+#include <ui/Gralloc.h>
 #include <ui/Gralloc2.h>
+#include <ui/Gralloc3.h>
 #include <ui/GraphicBufferMapper.h>
 
 namespace android {
@@ -43,11 +45,17 @@
 KeyedVector<buffer_handle_t,
     GraphicBufferAllocator::alloc_rec_t> GraphicBufferAllocator::sAllocList;
 
-GraphicBufferAllocator::GraphicBufferAllocator()
-  : mMapper(GraphicBufferMapper::getInstance()),
-    mAllocator(std::make_unique<Gralloc2::Allocator>(
-                mMapper.getGrallocMapper()))
-{
+GraphicBufferAllocator::GraphicBufferAllocator() : mMapper(GraphicBufferMapper::getInstance()) {
+    mAllocator = std::make_unique<const Gralloc3Allocator>(
+            reinterpret_cast<const Gralloc3Mapper&>(mMapper.getGrallocMapper()));
+    if (!mAllocator->isSupported()) {
+        mAllocator = std::make_unique<const Gralloc2Allocator>(
+                reinterpret_cast<const Gralloc2Mapper&>(mMapper.getGrallocMapper()));
+    }
+
+    if (!mAllocator->isSupported()) {
+        LOG_ALWAYS_FATAL("gralloc-allocator is missing");
+    }
 }
 
 GraphicBufferAllocator::~GraphicBufferAllocator() {}
@@ -104,15 +112,9 @@
     // TODO(b/72323293, b/72703005): Remove these invalid bits from callers
     usage &= ~static_cast<uint64_t>((1 << 10) | (1 << 13));
 
-    Gralloc2::IMapper::BufferDescriptorInfo info = {};
-    info.width = width;
-    info.height = height;
-    info.layerCount = layerCount;
-    info.format = static_cast<Gralloc2::PixelFormat>(format);
-    info.usage = usage;
-
-    Gralloc2::Error error = mAllocator->allocate(info, stride, handle);
-    if (error == Gralloc2::Error::NONE) {
+    status_t error =
+            mAllocator->allocate(width, height, format, layerCount, usage, 1, stride, handle);
+    if (error == NO_ERROR) {
         Mutex::Autolock _l(sLock);
         KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
         uint32_t bpp = bytesPerPixel(format);
diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp
index 2d8e582..9e36377 100644
--- a/libs/ui/GraphicBufferMapper.cpp
+++ b/libs/ui/GraphicBufferMapper.cpp
@@ -32,7 +32,9 @@
 #include <utils/Log.h>
 #include <utils/Trace.h>
 
+#include <ui/Gralloc.h>
 #include <ui/Gralloc2.h>
+#include <ui/Gralloc3.h>
 #include <ui/GraphicBuffer.h>
 
 #include <system/graphics.h>
@@ -43,12 +45,19 @@
 ANDROID_SINGLETON_STATIC_INSTANCE( GraphicBufferMapper )
 
 void GraphicBufferMapper::preloadHal() {
-    Gralloc2::Mapper::preload();
+    Gralloc2Mapper::preload();
+    Gralloc3Mapper::preload();
 }
 
-GraphicBufferMapper::GraphicBufferMapper()
-  : mMapper(std::make_unique<const Gralloc2::Mapper>())
-{
+GraphicBufferMapper::GraphicBufferMapper() {
+    mMapper = std::make_unique<const Gralloc3Mapper>();
+    if (!mMapper->isSupported()) {
+        mMapper = std::make_unique<const Gralloc2Mapper>();
+    }
+
+    if (!mMapper->isSupported()) {
+        LOG_ALWAYS_FATAL("gralloc-mapper is missing");
+    }
 }
 
 status_t GraphicBufferMapper::importBuffer(buffer_handle_t rawHandle,
@@ -59,22 +68,15 @@
     ATRACE_CALL();
 
     buffer_handle_t bufferHandle;
-    Gralloc2::Error error = mMapper->importBuffer(
-            hardware::hidl_handle(rawHandle), &bufferHandle);
-    if (error != Gralloc2::Error::NONE) {
+    status_t error = mMapper->importBuffer(hardware::hidl_handle(rawHandle), &bufferHandle);
+    if (error != NO_ERROR) {
         ALOGW("importBuffer(%p) failed: %d", rawHandle, error);
-        return static_cast<status_t>(error);
+        return error;
     }
 
-    Gralloc2::IMapper::BufferDescriptorInfo info = {};
-    info.width = width;
-    info.height = height;
-    info.layerCount = layerCount;
-    info.format = static_cast<Gralloc2::PixelFormat>(format);
-    info.usage = usage;
-
-    error = mMapper->validateBufferSize(bufferHandle, info, stride);
-    if (error != Gralloc2::Error::NONE) {
+    error = mMapper->validateBufferSize(bufferHandle, width, height, format, layerCount, usage,
+                                        stride);
+    if (error != NO_ERROR) {
         ALOGE("validateBufferSize(%p) failed: %d", rawHandle, error);
         freeBuffer(bufferHandle);
         return static_cast<status_t>(error);
@@ -100,19 +102,10 @@
     return NO_ERROR;
 }
 
-static inline Gralloc2::IMapper::Rect asGralloc2Rect(const Rect& rect) {
-    Gralloc2::IMapper::Rect outRect{};
-    outRect.left = rect.left;
-    outRect.top = rect.top;
-    outRect.width = rect.width();
-    outRect.height = rect.height();
-    return outRect;
-}
-
-status_t GraphicBufferMapper::lock(buffer_handle_t handle, uint32_t usage,
-        const Rect& bounds, void** vaddr)
-{
-    return lockAsync(handle, usage, bounds, vaddr, -1);
+status_t GraphicBufferMapper::lock(buffer_handle_t handle, uint32_t usage, const Rect& bounds,
+                                   void** vaddr, int32_t* outBytesPerPixel,
+                                   int32_t* outBytesPerStride) {
+    return lockAsync(handle, usage, bounds, vaddr, -1, outBytesPerPixel, outBytesPerStride);
 }
 
 status_t GraphicBufferMapper::lockYCbCr(buffer_handle_t handle, uint32_t usage,
@@ -132,27 +125,23 @@
     return error;
 }
 
-status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle,
-        uint32_t usage, const Rect& bounds, void** vaddr, int fenceFd)
-{
-    return lockAsync(handle, usage, usage, bounds, vaddr, fenceFd);
+status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, uint32_t usage, const Rect& bounds,
+                                        void** vaddr, int fenceFd, int32_t* outBytesPerPixel,
+                                        int32_t* outBytesPerStride) {
+    return lockAsync(handle, usage, usage, bounds, vaddr, fenceFd, outBytesPerPixel,
+                     outBytesPerStride);
 }
 
-status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle,
-        uint64_t producerUsage, uint64_t consumerUsage, const Rect& bounds,
-        void** vaddr, int fenceFd)
-{
+status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, uint64_t producerUsage,
+                                        uint64_t consumerUsage, const Rect& bounds, void** vaddr,
+                                        int fenceFd, int32_t* outBytesPerPixel,
+                                        int32_t* outBytesPerStride) {
     ATRACE_CALL();
 
     const uint64_t usage = static_cast<uint64_t>(
             android_convertGralloc1To0Usage(producerUsage, consumerUsage));
-    Gralloc2::Error error = mMapper->lock(handle, usage,
-            asGralloc2Rect(bounds), fenceFd, vaddr);
-
-    ALOGW_IF(error != Gralloc2::Error::NONE, "lock(%p, ...) failed: %d",
-            handle, error);
-
-    return static_cast<status_t>(error);
+    return mMapper->lock(handle, usage, bounds, fenceFd, vaddr, outBytesPerPixel,
+                         outBytesPerStride);
 }
 
 status_t GraphicBufferMapper::lockAsyncYCbCr(buffer_handle_t handle,
@@ -160,19 +149,7 @@
 {
     ATRACE_CALL();
 
-    Gralloc2::YCbCrLayout layout;
-    Gralloc2::Error error = mMapper->lock(handle, usage,
-            asGralloc2Rect(bounds), fenceFd, &layout);
-    if (error == Gralloc2::Error::NONE) {
-        ycbcr->y = layout.y;
-        ycbcr->cb = layout.cb;
-        ycbcr->cr = layout.cr;
-        ycbcr->ystride = static_cast<size_t>(layout.yStride);
-        ycbcr->cstride = static_cast<size_t>(layout.cStride);
-        ycbcr->chroma_step = static_cast<size_t>(layout.chromaStep);
-    }
-
-    return static_cast<status_t>(error);
+    return mMapper->lock(handle, usage, bounds, fenceFd, ycbcr);
 }
 
 status_t GraphicBufferMapper::unlockAsync(buffer_handle_t handle, int *fenceFd)
diff --git a/libs/ui/OWNERS b/libs/ui/OWNERS
new file mode 100644
index 0000000..97ead21
--- /dev/null
+++ b/libs/ui/OWNERS
@@ -0,0 +1,7 @@
+lpy@google.com
+marissaw@google.com
+mathias@google.com
+romainguy@google.com
+stoza@google.com
+jwcai@google.com
+tianyuj@google.com
diff --git a/cmds/dumpstate/binder/android/os/DumpstateOptions.aidl b/libs/ui/Size.cpp
similarity index 65%
copy from cmds/dumpstate/binder/android/os/DumpstateOptions.aidl
copy to libs/ui/Size.cpp
index c1a7f15..d2996d1 100644
--- a/cmds/dumpstate/binder/android/os/DumpstateOptions.aidl
+++ b/libs/ui/Size.cpp
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2018, The Android Open Source Project
+/*
+ * 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
+ *      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,
@@ -14,10 +14,11 @@
  * limitations under the License.
  */
 
-package android.os;
+#include <ui/Size.h>
 
-/**
-  * Specifies arguments for IDumpstate.
-  * {@hide}
-  */
-parcelable DumpstateOptions cpp_header "android/os/DumpstateOptions.h";
+namespace android::ui {
+
+const Size Size::INVALID{-1, -1};
+const Size Size::EMPTY{0, 0};
+
+} // namespace android::ui
diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp
index 25128ef..d13942d 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -16,6 +16,7 @@
 
 #include <math.h>
 
+#include <android-base/stringprintf.h>
 #include <cutils/compiler.h>
 #include <ui/Region.h>
 #include <ui/Transform.h>
@@ -380,44 +381,47 @@
     return (getOrientation() & ROT_INVALID) ? false : true;
 }
 
-void Transform::dump(const char* name) const
-{
-    type(); // updates the type
+void Transform::dump(std::string& out, const char* name) const {
+    using android::base::StringAppendF;
 
-    String8 flags, type;
-    const mat33& m(mMatrix);
-    uint32_t orient = mType >> 8;
+    type(); // Ensure the information in mType is up to date
 
-    if (orient&ROT_INVALID) {
-        flags.append("ROT_INVALID ");
+    const uint32_t type = mType;
+    const uint32_t orient = type >> 8;
+
+    StringAppendF(&out, "%s 0x%08x (", name, orient);
+
+    if (orient & ROT_INVALID) {
+        out.append("ROT_INVALID ");
     } else {
-        if (orient&ROT_90) {
-            flags.append("ROT_90 ");
+        if (orient & ROT_90) {
+            out.append("ROT_90 ");
         } else {
-            flags.append("ROT_0 ");
+            out.append("ROT_0 ");
         }
-        if (orient&FLIP_V)
-            flags.append("FLIP_V ");
-        if (orient&FLIP_H)
-            flags.append("FLIP_H ");
+        if (orient & FLIP_V) out.append("FLIP_V ");
+        if (orient & FLIP_H) out.append("FLIP_H ");
     }
 
-    if (!(mType&(SCALE|ROTATE|TRANSLATE)))
-        type.append("IDENTITY ");
-    if (mType&SCALE)
-        type.append("SCALE ");
-    if (mType&ROTATE)
-        type.append("ROTATE ");
-    if (mType&TRANSLATE)
-        type.append("TRANSLATE ");
+    StringAppendF(&out, ") 0x%02x (", type);
 
-    ALOGD("%s 0x%08x (%s, %s)", name, mType, flags.string(), type.string());
-    ALOGD("%.4f  %.4f  %.4f", static_cast<double>(m[0][0]), static_cast<double>(m[1][0]),
-          static_cast<double>(m[2][0]));
-    ALOGD("%.4f  %.4f  %.4f", static_cast<double>(m[0][1]), static_cast<double>(m[1][1]),
-          static_cast<double>(m[2][1]));
-    ALOGD("%.4f  %.4f  %.4f", static_cast<double>(m[0][2]), static_cast<double>(m[1][2]),
-          static_cast<double>(m[2][2]));
+    if (!(type & (SCALE | ROTATE | TRANSLATE))) out.append("IDENTITY ");
+    if (type & SCALE) out.append("SCALE ");
+    if (type & ROTATE) out.append("ROTATE ");
+    if (type & TRANSLATE) out.append("TRANSLATE ");
+
+    out.append(")\n");
+
+    for (size_t i = 0; i < 3; i++) {
+        StringAppendF(&out, "    %.4f  %.4f  %.4f\n", static_cast<double>(mMatrix[0][i]),
+                      static_cast<double>(mMatrix[1][i]), static_cast<double>(mMatrix[2][i]));
+    }
+}
+
+void Transform::dump(const char* name) const {
+    std::string out;
+    dump(out, name);
+    ALOGD("%s", out.c_str());
 }
 
 }  // namespace ui
diff --git a/libs/ui/include/ui/BufferHubBuffer.h b/libs/ui/include/ui/BufferHubBuffer.h
index 44dfa95..42d9320 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,6 +56,8 @@
         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);
@@ -94,9 +97,6 @@
     // current cycle of the usage of the buffer.
     int Release();
 
-    // Polls the fd for |timeoutMs| milliseconds (-1 for infinity).
-    int Poll(int timeoutMs);
-
     // 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
@@ -126,6 +126,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..43d900c 100644
--- a/libs/ui/include/ui/BufferHubDefs.h
+++ b/libs/ui/include/ui/BufferHubDefs.h
@@ -170,15 +170,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/Gralloc.h b/libs/ui/include/ui/Gralloc.h
new file mode 100644
index 0000000..a484bce
--- /dev/null
+++ b/libs/ui/include/ui/Gralloc.h
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_UI_GRALLOC_H
+#define ANDROID_UI_GRALLOC_H
+
+#include <string>
+
+#include <hidl/HidlSupport.h>
+#include <ui/PixelFormat.h>
+#include <ui/Rect.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+// A wrapper to IMapper
+class GrallocMapper {
+public:
+    virtual ~GrallocMapper();
+
+    virtual bool isSupported() const = 0;
+
+    virtual status_t createDescriptor(void* bufferDescriptorInfo,
+                                      void* outBufferDescriptor) const = 0;
+
+    // Import a buffer that is from another HAL, another process, or is
+    // cloned.
+    //
+    // The returned handle must be freed with freeBuffer.
+    virtual status_t importBuffer(const hardware::hidl_handle& rawHandle,
+                                  buffer_handle_t* outBufferHandle) const = 0;
+
+    virtual void freeBuffer(buffer_handle_t bufferHandle) const = 0;
+
+    virtual status_t validateBufferSize(buffer_handle_t bufferHandle, uint32_t width,
+                                        uint32_t height, android::PixelFormat format,
+                                        uint32_t layerCount, uint64_t usage,
+                                        uint32_t stride) const = 0;
+
+    virtual void getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds,
+                                  uint32_t* outNumInts) const = 0;
+
+    // The ownership of acquireFence is always transferred to the callee, even
+    // on errors.
+    virtual status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+                          int acquireFence, void** outData, int32_t* outBytesPerPixel,
+                          int32_t* outBytesPerStride) const = 0;
+
+    // The ownership of acquireFence is always transferred to the callee, even
+    // on errors.
+    virtual status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+                          int acquireFence, android_ycbcr* ycbcr) const = 0;
+
+    // unlock returns a fence sync object (or -1) and the fence sync object is
+    // owned by the caller
+    virtual int unlock(buffer_handle_t bufferHandle) const = 0;
+};
+
+// A wrapper to IAllocator
+class GrallocAllocator {
+public:
+    virtual ~GrallocAllocator();
+
+    virtual bool isSupported() const = 0;
+
+    virtual std::string dumpDebugInfo() const = 0;
+
+    /*
+     * The returned buffers are already imported and must not be imported
+     * again.  outBufferHandles must point to a space that can contain at
+     * least "bufferCount" buffer_handle_t.
+     */
+    virtual status_t allocate(uint32_t width, uint32_t height, PixelFormat format,
+                              uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
+                              uint32_t* outStride, buffer_handle_t* outBufferHandles) const = 0;
+};
+
+} // namespace android
+
+#endif // ANDROID_UI_GRALLOC_H
diff --git a/libs/ui/include/ui/Gralloc2.h b/libs/ui/include/ui/Gralloc2.h
index 5a8dbda..b23d8f7 100644
--- a/libs/ui/include/ui/Gralloc2.h
+++ b/libs/ui/include/ui/Gralloc2.h
@@ -23,119 +23,72 @@
 #include <android/hardware/graphics/common/1.1/types.h>
 #include <android/hardware/graphics/mapper/2.0/IMapper.h>
 #include <android/hardware/graphics/mapper/2.1/IMapper.h>
+#include <ui/Gralloc.h>
+#include <ui/PixelFormat.h>
+#include <ui/Rect.h>
 #include <utils/StrongPointer.h>
 
 namespace android {
 
-namespace Gralloc2 {
-
-using hardware::graphics::allocator::V2_0::IAllocator;
-using hardware::graphics::common::V1_1::BufferUsage;
-using hardware::graphics::common::V1_1::PixelFormat;
-using hardware::graphics::mapper::V2_1::IMapper;
-using hardware::graphics::mapper::V2_0::BufferDescriptor;
-using hardware::graphics::mapper::V2_0::Error;
-using hardware::graphics::mapper::V2_0::YCbCrLayout;
-
-// A wrapper to IMapper
-class Mapper {
+class Gralloc2Mapper : public GrallocMapper {
 public:
     static void preload();
 
-    Mapper();
+    Gralloc2Mapper();
 
-    Error createDescriptor(
-            const IMapper::BufferDescriptorInfo& descriptorInfo,
-            BufferDescriptor* outDescriptor) const;
+    bool isSupported() const override;
 
-    // Import a buffer that is from another HAL, another process, or is
-    // cloned.
-    //
-    // The returned handle must be freed with freeBuffer.
-    Error importBuffer(const hardware::hidl_handle& rawHandle,
-            buffer_handle_t* outBufferHandle) const;
+    status_t createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const override;
 
-    void freeBuffer(buffer_handle_t bufferHandle) const;
+    status_t importBuffer(const hardware::hidl_handle& rawHandle,
+                          buffer_handle_t* outBufferHandle) const override;
 
-    Error validateBufferSize(buffer_handle_t bufferHandle,
-            const IMapper::BufferDescriptorInfo& descriptorInfo,
-            uint32_t stride) const;
+    void freeBuffer(buffer_handle_t bufferHandle) const override;
 
-    void getTransportSize(buffer_handle_t bufferHandle,
-            uint32_t* outNumFds, uint32_t* outNumInts) const;
+    status_t validateBufferSize(buffer_handle_t bufferHandle, uint32_t width, uint32_t height,
+                                android::PixelFormat format, uint32_t layerCount, uint64_t usage,
+                                uint32_t stride) const override;
 
-    // The ownership of acquireFence is always transferred to the callee, even
-    // on errors.
-    Error lock(buffer_handle_t bufferHandle, uint64_t usage,
-            const IMapper::Rect& accessRegion,
-            int acquireFence, void** outData) const;
+    void getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds,
+                          uint32_t* outNumInts) const override;
 
-    // The ownership of acquireFence is always transferred to the callee, even
-    // on errors.
-    Error lock(buffer_handle_t bufferHandle, uint64_t usage,
-            const IMapper::Rect& accessRegion,
-            int acquireFence, YCbCrLayout* outLayout) const;
+    status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+                  int acquireFence, void** outData, int32_t* outBytesPerPixel,
+                  int32_t* outBytesPerStride) const override;
 
-    // unlock returns a fence sync object (or -1) and the fence sync object is
-    // owned by the caller
-    int unlock(buffer_handle_t bufferHandle) const;
+    status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+                  int acquireFence, android_ycbcr* ycbcr) const override;
+
+    int unlock(buffer_handle_t bufferHandle) const override;
 
 private:
     // Determines whether the passed info is compatible with the mapper.
-    Error validateBufferDescriptorInfo(
-            const IMapper::BufferDescriptorInfo& descriptorInfo) const;
+    status_t validateBufferDescriptorInfo(
+            hardware::graphics::mapper::V2_1::IMapper::BufferDescriptorInfo* descriptorInfo) const;
 
     sp<hardware::graphics::mapper::V2_0::IMapper> mMapper;
-    sp<IMapper> mMapperV2_1;
+    sp<hardware::graphics::mapper::V2_1::IMapper> mMapperV2_1;
 };
 
-// A wrapper to IAllocator
-class Allocator {
+class Gralloc2Allocator : public GrallocAllocator {
 public:
     // An allocator relies on a mapper, and that mapper must be alive at all
     // time.
-    Allocator(const Mapper& mapper);
+    Gralloc2Allocator(const Gralloc2Mapper& mapper);
 
-    std::string dumpDebugInfo() const;
+    bool isSupported() const override;
 
-    /*
-     * The returned buffers are already imported and must not be imported
-     * again.  outBufferHandles must point to a space that can contain at
-     * least "count" buffer_handle_t.
-     */
-    Error allocate(BufferDescriptor descriptor, uint32_t count,
-            uint32_t* outStride, buffer_handle_t* outBufferHandles) const;
+    std::string dumpDebugInfo() const override;
 
-    Error allocate(BufferDescriptor descriptor,
-            uint32_t* outStride, buffer_handle_t* outBufferHandle) const
-    {
-        return allocate(descriptor, 1, outStride, outBufferHandle);
-    }
-
-    Error allocate(const IMapper::BufferDescriptorInfo& descriptorInfo, uint32_t count,
-            uint32_t* outStride, buffer_handle_t* outBufferHandles) const
-    {
-        BufferDescriptor descriptor;
-        Error error = mMapper.createDescriptor(descriptorInfo, &descriptor);
-        if (error == Error::NONE) {
-            error = allocate(descriptor, count, outStride, outBufferHandles);
-        }
-        return error;
-    }
-
-    Error allocate(const IMapper::BufferDescriptorInfo& descriptorInfo,
-            uint32_t* outStride, buffer_handle_t* outBufferHandle) const
-    {
-        return allocate(descriptorInfo, 1, outStride, outBufferHandle);
-    }
+    status_t allocate(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+                      uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
+                      buffer_handle_t* outBufferHandles) const override;
 
 private:
-    const Mapper& mMapper;
-    sp<IAllocator> mAllocator;
+    const Gralloc2Mapper& mMapper;
+    sp<hardware::graphics::allocator::V2_0::IAllocator> mAllocator;
 };
 
-} // namespace Gralloc2
-
 } // namespace android
 
 #endif // ANDROID_UI_GRALLOC2_H
diff --git a/libs/ui/include/ui/Gralloc3.h b/libs/ui/include/ui/Gralloc3.h
new file mode 100644
index 0000000..b0cbcc1
--- /dev/null
+++ b/libs/ui/include/ui/Gralloc3.h
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_UI_GRALLOC3_H
+#define ANDROID_UI_GRALLOC3_H
+
+#include <string>
+
+#include <android/hardware/graphics/allocator/3.0/IAllocator.h>
+#include <android/hardware/graphics/common/1.1/types.h>
+#include <android/hardware/graphics/mapper/3.0/IMapper.h>
+#include <ui/Gralloc.h>
+#include <ui/PixelFormat.h>
+#include <ui/Rect.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+class Gralloc3Mapper : public GrallocMapper {
+public:
+    static void preload();
+
+    Gralloc3Mapper();
+
+    bool isSupported() const override;
+
+    status_t createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const override;
+
+    status_t importBuffer(const hardware::hidl_handle& rawHandle,
+                          buffer_handle_t* outBufferHandle) const override;
+
+    void freeBuffer(buffer_handle_t bufferHandle) const override;
+
+    status_t validateBufferSize(buffer_handle_t bufferHandle, uint32_t width, uint32_t height,
+                                android::PixelFormat format, uint32_t layerCount, uint64_t usage,
+                                uint32_t stride) const override;
+
+    void getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds,
+                          uint32_t* outNumInts) const override;
+
+    status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+                  int acquireFence, void** outData, int32_t* outBytesPerPixel,
+                  int32_t* outBytesPerStride) const override;
+
+    status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+                  int acquireFence, android_ycbcr* ycbcr) const override;
+
+    int unlock(buffer_handle_t bufferHandle) const override;
+
+private:
+    // Determines whether the passed info is compatible with the mapper.
+    status_t validateBufferDescriptorInfo(
+            hardware::graphics::mapper::V3_0::IMapper::BufferDescriptorInfo* descriptorInfo) const;
+
+    sp<hardware::graphics::mapper::V3_0::IMapper> mMapper;
+};
+
+class Gralloc3Allocator : public GrallocAllocator {
+public:
+    // An allocator relies on a mapper, and that mapper must be alive at all
+    // time.
+    Gralloc3Allocator(const Gralloc3Mapper& mapper);
+
+    bool isSupported() const override;
+
+    std::string dumpDebugInfo() const override;
+
+    status_t allocate(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+                      uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
+                      buffer_handle_t* outBufferHandles) const override;
+
+private:
+    const Gralloc3Mapper& mMapper;
+    sp<hardware::graphics::allocator::V3_0::IAllocator> mAllocator;
+};
+
+} // namespace android
+
+#endif // ANDROID_UI_GRALLOC3_H
diff --git a/libs/ui/include/ui/GraphicBufferAllocator.h b/libs/ui/include/ui/GraphicBufferAllocator.h
index 7e2b230..3a547b6 100644
--- a/libs/ui/include/ui/GraphicBufferAllocator.h
+++ b/libs/ui/include/ui/GraphicBufferAllocator.h
@@ -34,10 +34,7 @@
 
 namespace android {
 
-namespace Gralloc2 {
-class Allocator;
-}
-
+class GrallocAllocator;
 class GraphicBufferMapper;
 
 class GraphicBufferAllocator : public Singleton<GraphicBufferAllocator>
@@ -75,7 +72,7 @@
     ~GraphicBufferAllocator();
 
     GraphicBufferMapper& mMapper;
-    const std::unique_ptr<const Gralloc2::Allocator> mAllocator;
+    std::unique_ptr<const GrallocAllocator> mAllocator;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h
index 7cf003d..072926f 100644
--- a/libs/ui/include/ui/GraphicBufferMapper.h
+++ b/libs/ui/include/ui/GraphicBufferMapper.h
@@ -35,10 +35,7 @@
 
 // ---------------------------------------------------------------------------
 
-namespace Gralloc2 {
-class Mapper;
-}
-
+class GrallocMapper;
 class Rect;
 
 class GraphicBufferMapper : public Singleton<GraphicBufferMapper>
@@ -59,20 +56,21 @@
     void getTransportSize(buffer_handle_t handle,
             uint32_t* outTransportNumFds, uint32_t* outTransportNumInts);
 
-    status_t lock(buffer_handle_t handle,
-            uint32_t usage, const Rect& bounds, void** vaddr);
+    status_t lock(buffer_handle_t handle, uint32_t usage, const Rect& bounds, void** vaddr,
+                  int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr);
 
     status_t lockYCbCr(buffer_handle_t handle,
             uint32_t usage, const Rect& bounds, android_ycbcr *ycbcr);
 
     status_t unlock(buffer_handle_t handle);
 
-    status_t lockAsync(buffer_handle_t handle,
-            uint32_t usage, const Rect& bounds, void** vaddr, int fenceFd);
+    status_t lockAsync(buffer_handle_t handle, uint32_t usage, const Rect& bounds, void** vaddr,
+                       int fenceFd, int32_t* outBytesPerPixel = nullptr,
+                       int32_t* outBytesPerStride = nullptr);
 
-    status_t lockAsync(buffer_handle_t handle,
-            uint64_t producerUsage, uint64_t consumerUsage, const Rect& bounds,
-            void** vaddr, int fenceFd);
+    status_t lockAsync(buffer_handle_t handle, uint64_t producerUsage, uint64_t consumerUsage,
+                       const Rect& bounds, void** vaddr, int fenceFd,
+                       int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr);
 
     status_t lockAsyncYCbCr(buffer_handle_t handle,
             uint32_t usage, const Rect& bounds, android_ycbcr *ycbcr,
@@ -80,9 +78,8 @@
 
     status_t unlockAsync(buffer_handle_t handle, int *fenceFd);
 
-    const Gralloc2::Mapper& getGrallocMapper() const
-    {
-        return *mMapper;
+    const GrallocMapper& getGrallocMapper() const {
+        return reinterpret_cast<const GrallocMapper&>(*mMapper);
     }
 
 private:
@@ -90,7 +87,7 @@
 
     GraphicBufferMapper();
 
-    const std::unique_ptr<const Gralloc2::Mapper> mMapper;
+    std::unique_ptr<const GrallocMapper> mMapper;
 };
 
 // ---------------------------------------------------------------------------
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/ui/Rect.h b/libs/ui/include/ui/Rect.h
index e9da087..1768805 100644
--- a/libs/ui/include/ui/Rect.h
+++ b/libs/ui/include/ui/Rect.h
@@ -24,6 +24,7 @@
 
 #include <ui/FloatRect.h>
 #include <ui/Point.h>
+#include <ui/Size.h>
 
 #include <android/rect.h>
 
@@ -78,6 +79,13 @@
         bottom = static_cast<int32_t>(floatRect.bottom + 0.5f);
     }
 
+    inline explicit Rect(const ui::Size& size) {
+        left = 0;
+        top = 0;
+        right = size.width;
+        bottom = size.height;
+    }
+
     void makeInvalid();
 
     inline void clear() {
@@ -106,6 +114,8 @@
         return bottom - top;
     }
 
+    ui::Size getSize() const { return ui::Size(getWidth(), getHeight()); }
+
     __attribute__((no_sanitize("signed-integer-overflow")))
     inline Rect getBounds() const {
         return Rect(right - left, bottom - top);
diff --git a/libs/ui/include/ui/Size.h b/libs/ui/include/ui/Size.h
new file mode 100644
index 0000000..c39d8af
--- /dev/null
+++ b/libs/ui/include/ui/Size.h
@@ -0,0 +1,158 @@
+/*
+ * 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 <algorithm>
+#include <cstdint>
+#include <limits>
+#include <type_traits>
+#include <utility>
+
+namespace android {
+namespace ui {
+
+// Forward declare a few things.
+struct Size;
+bool operator==(const Size& lhs, const Size& rhs);
+
+/**
+ * A simple value type representing a two-dimensional size
+ */
+struct Size {
+    int32_t width;
+    int32_t height;
+
+    // Special values
+    static const Size INVALID;
+    static const Size EMPTY;
+
+    // ------------------------------------------------------------------------
+    // Construction
+    // ------------------------------------------------------------------------
+
+    Size() : Size(INVALID) {}
+    template <typename T>
+    Size(T&& w, T&& h)
+          : width(Size::clamp<int32_t, T>(std::forward<T>(w))),
+            height(Size::clamp<int32_t, T>(std::forward<T>(h))) {}
+
+    // ------------------------------------------------------------------------
+    // Accessors
+    // ------------------------------------------------------------------------
+
+    int32_t getWidth() const { return width; }
+    int32_t getHeight() const { return height; }
+
+    template <typename T>
+    void setWidth(T&& v) {
+        width = Size::clamp<int32_t, T>(std::forward<T>(v));
+    }
+    template <typename T>
+    void setHeight(T&& v) {
+        height = Size::clamp<int32_t, T>(std::forward<T>(v));
+    }
+
+    // ------------------------------------------------------------------------
+    // Assignment
+    // ------------------------------------------------------------------------
+
+    void set(const Size& size) { *this = size; }
+    template <typename T>
+    void set(T&& w, T&& h) {
+        set(Size(std::forward<T>(w), std::forward<T>(h)));
+    }
+
+    // Sets the value to INVALID
+    void makeInvalid() { set(INVALID); }
+
+    // Sets the value to EMPTY
+    void clear() { set(EMPTY); }
+
+    // ------------------------------------------------------------------------
+    // Semantic checks
+    // ------------------------------------------------------------------------
+
+    // Valid means non-negative width and height
+    bool isValid() const { return width >= 0 && height >= 0; }
+
+    // Empty means zero width and height
+    bool isEmpty() const { return *this == EMPTY; }
+
+    // ------------------------------------------------------------------------
+    // Clamp Helpers
+    // ------------------------------------------------------------------------
+
+    // Note: We use only features available in C++11 here for compatibility with
+    // external targets which include this file directly or indirectly and which
+    // themselves use C++11.
+
+    // C++11 compatible replacement for std::remove_cv_reference_t [C++20]
+    template <typename T>
+    using remove_cv_reference_t =
+            typename std::remove_cv<typename std::remove_reference<T>::type>::type;
+
+    // Takes a value of type FromType, and ensures it can be represented as a value of type ToType,
+    // clamping the input value to the output range if necessary.
+    template <typename ToType, typename FromType>
+    static Size::remove_cv_reference_t<ToType> clamp(
+            typename std::enable_if<
+                    std::numeric_limits<Size::remove_cv_reference_t<ToType>>::is_bounded &&
+                            std::numeric_limits<Size::remove_cv_reference_t<FromType>>::is_bounded,
+                    FromType&&>::type v) {
+        static constexpr auto toHighest = std::numeric_limits<remove_cv_reference_t<ToType>>::max();
+        static constexpr auto toLowest =
+                std::numeric_limits<remove_cv_reference_t<ToType>>::lowest();
+        static constexpr auto fromHighest =
+                std::numeric_limits<remove_cv_reference_t<FromType>>::max();
+        static constexpr auto fromLowest =
+                std::numeric_limits<remove_cv_reference_t<FromType>>::lowest();
+
+        // A clamp is needed if the range of FromType is not a subset of the range of ToType
+        static constexpr bool isClampNeeded = (toLowest > fromLowest) || (toHighest < fromHighest);
+
+        // If a clamp is not needed, the conversion is just a trivial cast.
+        if (!isClampNeeded) {
+            return static_cast<ToType>(v);
+        }
+
+        // Otherwise we leverage implicit conversion to safely compare values of
+        // different types, to ensure we return a value clamped to the range of
+        // ToType.
+        return v < toLowest ? toLowest : (v > toHighest ? toHighest : static_cast<ToType>(v));
+    }
+};
+
+// ------------------------------------------------------------------------
+// Comparisons
+// ------------------------------------------------------------------------
+
+inline bool operator==(const Size& lhs, const Size& rhs) {
+    return lhs.width == rhs.width && lhs.height == rhs.height;
+}
+
+inline bool operator!=(const Size& lhs, const Size& rhs) {
+    return !operator==(lhs, rhs);
+}
+
+inline bool operator<(const Size& lhs, const Size& rhs) {
+    // Orders by increasing width, then height.
+    if (lhs.width != rhs.width) return lhs.width < rhs.width;
+    return lhs.height < rhs.height;
+}
+
+} // namespace ui
+} // namespace android
diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h
index 900a5c4..dcb26cf 100644
--- a/libs/ui/include/ui/Transform.h
+++ b/libs/ui/include/ui/Transform.h
@@ -19,6 +19,7 @@
 
 #include <stdint.h>
 #include <sys/types.h>
+#include <string>
 
 #include <hardware/hardware.h>
 #include <math/vec2.h>
@@ -90,6 +91,7 @@
     Transform inverse() const;
 
     // for debugging
+    void dump(std::string& result, const char* name) const;
     void dump(const char* name) const;
 
 private:
diff --git a/libs/ui/include_vndk/ui/Size.h b/libs/ui/include_vndk/ui/Size.h
new file mode 120000
index 0000000..fd2b21b
--- /dev/null
+++ b/libs/ui/include_vndk/ui/Size.h
@@ -0,0 +1 @@
+../../include/ui/Size.h
\ No newline at end of file
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 665469e..373fa4f 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -71,3 +71,10 @@
     ],
     cflags: ["-Wall", "-Werror"],
 }
+
+cc_test {
+    name: "Size_test",
+    shared_libs: ["libui"],
+    srcs: ["Size_test.cpp"],
+    cflags: ["-Wall", "-Werror"],
+}
diff --git a/libs/ui/tests/BufferHubBuffer_test.cpp b/libs/ui/tests/BufferHubBuffer_test.cpp
index cd744cd..58965c1 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 {
 
@@ -160,6 +164,32 @@
     // Both buffer instances should be in released state currently.
     EXPECT_TRUE(IsBufferReleased(b1->buffer_state()));
     EXPECT_TRUE(IsBufferReleased(b2->buffer_state()));
+
+    // 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 +219,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;
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/Size_test.cpp b/libs/ui/tests/Size_test.cpp
new file mode 100644
index 0000000..69e1ac8
--- /dev/null
+++ b/libs/ui/tests/Size_test.cpp
@@ -0,0 +1,184 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "SizeTest"
+
+#include <cmath>
+#include <cstdlib>
+
+#include <ui/Size.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace ui {
+
+TEST(SizeTest, BasicConstructionAndEqualityComparison) {
+    Size s(123, 456);
+
+    EXPECT_EQ(123, s.width);
+    EXPECT_EQ(123, s.getWidth());
+
+    EXPECT_EQ(456, s.height);
+    EXPECT_EQ(456, s.getHeight());
+
+    EXPECT_EQ(Size(123, 456), s);
+    EXPECT_NE(Size(456, 123), s);
+}
+
+TEST(SizeTest, BasicLessThanComparison) {
+    EXPECT_TRUE(Size(0, 1) < Size(2, 3));
+    EXPECT_FALSE(Size(2, 3) < Size(0, 1));
+
+    EXPECT_TRUE(Size(0, 3) < Size(2, 1));
+    EXPECT_FALSE(Size(2, 1) < Size(0, 3));
+
+    EXPECT_TRUE(Size(0, 1) < Size(0, 3));
+    EXPECT_FALSE(Size(0, 3) < Size(0, 1));
+
+    EXPECT_FALSE(Size(1, 1) < Size(1, 1));
+}
+
+TEST(SizeTest, ValidAndEmpty) {
+    {
+        Size s;
+        EXPECT_FALSE(s.isValid());
+        EXPECT_FALSE(s.isEmpty());
+    }
+
+    {
+        Size s(-1, -1);
+        EXPECT_FALSE(s.isValid());
+        EXPECT_FALSE(s.isEmpty());
+    }
+
+    {
+        Size s(1, -1000);
+        EXPECT_FALSE(s.isValid());
+        EXPECT_FALSE(s.isEmpty());
+    }
+
+    {
+        Size s(-1000, 1);
+        EXPECT_FALSE(s.isValid());
+        EXPECT_FALSE(s.isEmpty());
+    }
+
+    {
+        Size s(-1000, -1000);
+        EXPECT_FALSE(s.isValid());
+        EXPECT_FALSE(s.isEmpty());
+    }
+
+    {
+        const auto& s = Size::INVALID;
+        EXPECT_FALSE(s.isValid());
+        EXPECT_FALSE(s.isEmpty());
+    }
+
+    {
+        Size s(123, 456);
+        s.makeInvalid();
+        EXPECT_FALSE(s.isValid());
+        EXPECT_FALSE(s.isEmpty());
+    }
+
+    {
+        Size s(0, 0);
+        EXPECT_TRUE(s.isValid());
+        EXPECT_TRUE(s.isEmpty());
+    }
+
+    {
+        const auto& s = Size::EMPTY;
+        EXPECT_TRUE(s.isValid());
+        EXPECT_TRUE(s.isEmpty());
+    }
+
+    {
+        Size s(123, 456);
+        s.clear();
+        EXPECT_TRUE(s.isValid());
+        EXPECT_TRUE(s.isEmpty());
+    }
+
+    {
+        Size s(123, 456);
+        EXPECT_TRUE(s.isValid());
+        EXPECT_FALSE(s.isEmpty());
+    }
+}
+
+TEST(SizeTest, Set) {
+    {
+        Size s;
+        s.setWidth(0);
+        EXPECT_EQ(Size(0, -1), s);
+    }
+
+    {
+        Size s;
+        s.setHeight(0);
+        EXPECT_EQ(Size(-1, 0), s);
+    }
+
+    {
+        Size s;
+        s.set(123, 456);
+        EXPECT_EQ(Size(123, 456), s);
+    }
+}
+
+template <typename T, typename U>
+void ClampTest(T input, U expected) {
+    // The constructor, set(), setWidth() and setHeight() all allow arbitrary
+    // conversions from other numeric types, and implement clamping if necessary.
+
+    EXPECT_EQ(Size(expected, expected), Size(input, input));
+
+    {
+        Size s;
+        s.set(input, input);
+        EXPECT_EQ(Size(expected, expected), s);
+    }
+
+    {
+        Size s;
+        s.setWidth(input);
+        EXPECT_EQ(expected, s.width);
+    }
+
+    {
+        Size s;
+        s.setHeight(input);
+        EXPECT_EQ(expected, s.height);
+    }
+}
+
+TEST(SizeTest, Int8RangeIsNotClamped) {
+    ClampTest(std::numeric_limits<int8_t>::max(), std::numeric_limits<int8_t>::max());
+    ClampTest(int8_t(0), int8_t(0));
+    ClampTest(std::numeric_limits<int8_t>::lowest(), std::numeric_limits<int8_t>::lowest());
+}
+
+TEST(SizeTest, FloatRangeIsClamped) {
+    ClampTest(std::numeric_limits<float>::max(), std::numeric_limits<int32_t>::max());
+    ClampTest(float(0), int32_t(0));
+    ClampTest(std::numeric_limits<float>::lowest(), std::numeric_limits<int32_t>::lowest());
+}
+
+} // namespace ui
+} // namespace android
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/include/private/dvr/buffer_hub_defs.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h
index f2c40fe..f5761d5 100644
--- a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h
+++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h
@@ -162,39 +162,6 @@
   void operator=(const BufferTraits&) = delete;
 };
 
-struct DetachedBufferRPC {
- private:
-  enum {
-    kOpDetachedBufferBase = 1000,
-
-    // Allocates a standalone DetachedBuffer not associated with any producer
-    // consumer set.
-    kOpCreate,
-
-    // Imports the given channel handle to a DetachedBuffer, taking ownership.
-    kOpImport,
-
-    // Creates a DetachedBuffer client from an existing one. The new client will
-    // share the same underlying gralloc buffer and ashmem region for metadata.
-    kOpDuplicate,
-  };
-
-  // Aliases.
-  using LocalChannelHandle = pdx::LocalChannelHandle;
-  using LocalHandle = pdx::LocalHandle;
-  using Void = pdx::rpc::Void;
-
- public:
-  PDX_REMOTE_METHOD(Create, kOpCreate,
-                    void(uint32_t width, uint32_t height, uint32_t layer_count,
-                         uint32_t format, uint64_t usage,
-                         size_t user_metadata_size));
-  PDX_REMOTE_METHOD(Import, kOpImport, BufferTraits<LocalHandle>(Void));
-  PDX_REMOTE_METHOD(Duplicate, kOpDuplicate, LocalChannelHandle(Void));
-
-  PDX_REMOTE_API(API, Create, Import, Duplicate);
-};
-
 }  // namespace dvr
 }  // namespace android
 
diff --git a/libs/vr/libvrflinger/Android.bp b/libs/vr/libvrflinger/Android.bp
index 4f8bdbf..2829353 100644
--- a/libs/vr/libvrflinger/Android.bp
+++ b/libs/vr/libvrflinger/Android.bp
@@ -47,6 +47,7 @@
     "liblog",
     "libhardware",
     "libnativewindow",
+    "libprocessgroup",
     "libutils",
     "libEGL",
     "libGLESv1_CM",
diff --git a/libs/vr/libvrflinger/vr_flinger.cpp b/libs/vr/libvrflinger/vr_flinger.cpp
index b57383a..a8a8476 100644
--- a/libs/vr/libvrflinger/vr_flinger.cpp
+++ b/libs/vr/libvrflinger/vr_flinger.cpp
@@ -12,9 +12,9 @@
 #include <binder/IServiceManager.h>
 #include <binder/ProcessState.h>
 #include <cutils/properties.h>
-#include <cutils/sched_policy.h>
 #include <log/log.h>
 #include <private/dvr/display_client.h>
+#include <processgroup/sched_policy.h>
 #include <sys/prctl.h>
 #include <sys/resource.h>
 
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/libs/EGL/egl_layers.cpp b/opengl/libs/EGL/egl_layers.cpp
index dd8fbfc..f936ac0 100644
--- a/opengl/libs/EGL/egl_layers.cpp
+++ b/opengl/libs/EGL/egl_layers.cpp
@@ -375,12 +375,13 @@
                 auto app_namespace = android::GraphicsEnv::getInstance().getAppNamespace();
                 if (app_namespace && !android::base::StartsWith(layer, kSystemLayerLibraryDir)) {
                     bool native_bridge = false;
-                    std::string error_message;
-                    handle = OpenNativeLibrary(app_namespace, layer.c_str(), &native_bridge,
-                                               &error_message);
+                    char* error_message = nullptr;
+                    handle = OpenNativeLibraryInNamespace(
+                        app_namespace, layer.c_str(), &native_bridge, &error_message);
                     if (!handle) {
                         ALOGE("Failed to load layer %s with error: %s", layer.c_str(),
-                              error_message.c_str());
+                              error_message);
+                        android::NativeLoaderFreeErrorMessage(error_message);
                         return;
                     }
 
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/BufferClient.cpp b/services/bufferhub/BufferClient.cpp
index e312011..ec7e535 100644
--- a/services/bufferhub/BufferClient.cpp
+++ b/services/bufferhub/BufferClient.cpp
@@ -41,6 +41,14 @@
 }
 
 BufferClient::~BufferClient() {
+    {
+        std::lock_guard<std::mutex> lock(mClosedMutex);
+        if (!mClosed) {
+            ALOGW("%s: client of buffer #%d destroyed without close. Closing it now.", __FUNCTION__,
+                  mBufferNode->id());
+        }
+    }
+
     close();
 }
 
diff --git a/services/bufferhub/BufferHubService.cpp b/services/bufferhub/BufferHubService.cpp
index ad49cd6..ae357cd 100644
--- a/services/bufferhub/BufferHubService.cpp
+++ b/services/bufferhub/BufferHubService.cpp
@@ -65,9 +65,9 @@
     std::lock_guard<std::mutex> lock(mClientSetMutex);
     mClientSet.emplace(client);
 
-    hidl_handle bufferInfo =
-            buildBufferInfo(node->id(), node->AddNewActiveClientsBitToMask(),
-                            node->user_metadata_size(), node->metadata().ashmem_fd());
+    hidl_handle bufferInfo = buildBufferInfo(node->id(), node->AddNewActiveClientsBitToMask(),
+                                             node->user_metadata_size(), node->eventFd().get(),
+                                             node->metadata().ashmem_fd());
     BufferTraits bufferTraits = {/*bufferDesc=*/description,
                                  /*bufferHandle=*/hidl_handle(node->buffer_handle()),
                                  /*bufferInfo=*/bufferInfo};
@@ -156,7 +156,7 @@
 
     hidl_handle bufferInfo =
             buildBufferInfo(node->id(), clientStateMask, node->user_metadata_size(),
-                            node->metadata().ashmem_fd());
+                            node->eventFd().get(), node->metadata().ashmem_fd());
     BufferTraits bufferTraits = {/*bufferDesc=*/bufferDesc,
                                  /*bufferHandle=*/hidl_handle(node->buffer_handle()),
                                  /*bufferInfo=*/bufferInfo};
@@ -206,7 +206,7 @@
     stream << std::right;
     stream << std::setw(6) << "Id";
     stream << " ";
-    stream << std::setw(9) << "Clients";
+    stream << std::setw(9) << "#Clients";
     stream << " ";
     stream << std::setw(14) << "Geometry";
     stream << " ";
@@ -216,7 +216,7 @@
     stream << " ";
     stream << std::setw(10) << "State";
     stream << " ";
-    stream << std::setw(10) << "Index";
+    stream << std::setw(8) << "Index";
     stream << std::endl;
 
     for (auto iter = clientCount.begin(); iter != clientCount.end(); ++iter) {
@@ -232,7 +232,7 @@
         stream << std::right;
         stream << std::setw(6) << /*Id=*/node->id();
         stream << " ";
-        stream << std::setw(9) << /*Clients=*/clientCount;
+        stream << std::setw(9) << /*#Clients=*/clientCount;
         stream << " ";
         if (desc.format == HAL_PIXEL_FORMAT_BLOB) {
             std::string size = std::to_string(desc.width) + " B";
@@ -251,6 +251,7 @@
         stream << " ";
         stream << "0x" << std::hex << std::setfill('0');
         stream << std::setw(8) << /*State=*/state;
+        stream << std::dec << std::setfill(' ');
         stream << " ";
         stream << std::setw(8) << /*Index=*/index;
         stream << std::endl;
@@ -281,14 +282,14 @@
     stream << std::right;
     stream << std::setw(8) << "Buffer Id";
     stream << " ";
-    stream << std::setw(6) << "Tokens";
+    stream << std::setw(7) << "#Tokens";
     stream << std::endl;
 
     for (auto iter = tokenCount.begin(); iter != tokenCount.end(); ++iter) {
         stream << std::right;
         stream << std::setw(8) << /*Buffer Id=*/iter->first;
         stream << " ";
-        stream << std::setw(6) << /*Tokens=*/iter->second;
+        stream << std::setw(7) << /*#Tokens=*/iter->second;
         stream << std::endl;
     }
 
@@ -344,16 +345,18 @@
 // 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) {
+                                              uint32_t userMetadataSize, const int eventFd,
+                                              const int metadataFd) {
     native_handle_t* infoHandle = native_handle_create(BufferHubDefs::kBufferInfoNumFds,
                                                        BufferHubDefs::kBufferInfoNumInts);
 
     infoHandle->data[0] = dup(metadataFd);
-    infoHandle->data[1] = bufferId;
+    infoHandle->data[1] = dup(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);
diff --git a/services/bufferhub/include/bufferhub/BufferHubService.h b/services/bufferhub/include/bufferhub/BufferHubService.h
index 23e664e..9015e05 100644
--- a/services/bufferhub/include/bufferhub/BufferHubService.h
+++ b/services/bufferhub/include/bufferhub/BufferHubService.h
@@ -59,7 +59,7 @@
 private:
     // Helper function to build BufferTraits.bufferInfo handle
     hidl_handle buildBufferInfo(int bufferId, uint32_t clientBitMask, uint32_t userMetadataSize,
-                                const int metadataFd);
+                                const int eventFd, const int metadataFd);
 
     // 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/displayservice/OWNERS b/services/displayservice/OWNERS
new file mode 100644
index 0000000..7a3e4c2
--- /dev/null
+++ b/services/displayservice/OWNERS
@@ -0,0 +1,2 @@
+smoreland@google.com
+lpy@google.com
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 843eb37..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
     ],
 
@@ -76,7 +82,8 @@
         "libutils",
         "libui",
         "libhardware_legacy",
-        "libutils"
+        "libstatslog",
+        "libutils",
     ],
 
     header_libs: [
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 9af189a..9702fb2 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -215,10 +215,6 @@
     return true;
 }
 
-static bool isMainDisplay(int32_t displayId) {
-    return displayId == ADISPLAY_ID_DEFAULT || displayId == ADISPLAY_ID_NONE;
-}
-
 static void dumpRegion(std::string& dump, const Region& region) {
     if (region.isEmpty()) {
         dump += "<empty>";
@@ -525,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();
@@ -540,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));
+                }
             }
         }
     }
@@ -930,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,
@@ -1297,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
@@ -1689,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);
 
@@ -1702,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;
         }
@@ -2155,7 +2153,7 @@
                     motionEntry->deviceId, motionEntry->source, motionEntry->displayId,
                     dispatchEntry->resolvedAction, motionEntry->actionButton,
                     dispatchEntry->resolvedFlags, motionEntry->edgeFlags,
-                    motionEntry->metaState, motionEntry->buttonState,
+                    motionEntry->metaState, motionEntry->buttonState, motionEntry->classification,
                     xOffset, yOffset, motionEntry->xPrecision, motionEntry->yPrecision,
                     motionEntry->downTime, motionEntry->eventTime,
                     motionEntry->pointerCount, motionEntry->pointerProperties,
@@ -2491,6 +2489,7 @@
             originalMotionEntry->flags,
             originalMotionEntry->metaState,
             originalMotionEntry->buttonState,
+            originalMotionEntry->classification,
             originalMotionEntry->edgeFlags,
             originalMotionEntry->xPrecision,
             originalMotionEntry->yPrecision,
@@ -2707,7 +2706,7 @@
         MotionEntry* newEntry = new MotionEntry(args->sequenceNum, args->eventTime,
                 args->deviceId, args->source, args->displayId, policyFlags,
                 args->action, args->actionButton, args->flags,
-                args->metaState, args->buttonState,
+                args->metaState, args->buttonState, args->classification,
                 args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime,
                 args->pointerCount, args->pointerProperties, args->pointerCoords, 0, 0);
 
@@ -2721,8 +2720,7 @@
 }
 
 bool InputDispatcher::shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args) {
-    // TODO: support sending secondary display events to input filter
-    return mInputFilterEnabled && isMainDisplay(args->displayId);
+    return mInputFilterEnabled;
 }
 
 void InputDispatcher::notifySwitch(const NotifySwitchArgs* args) {
@@ -2845,7 +2843,7 @@
                 policyFlags,
                 action, actionButton, motionEvent->getFlags(),
                 motionEvent->getMetaState(), motionEvent->getButtonState(),
-                motionEvent->getEdgeFlags(),
+                motionEvent->getClassification(), motionEvent->getEdgeFlags(),
                 motionEvent->getXPrecision(), motionEvent->getYPrecision(),
                 motionEvent->getDownTime(),
                 uint32_t(pointerCount), pointerProperties, samplePointerCoords,
@@ -2860,7 +2858,7 @@
                     motionEvent->getDisplayId(), policyFlags,
                     action, actionButton, motionEvent->getFlags(),
                     motionEvent->getMetaState(), motionEvent->getButtonState(),
-                    motionEvent->getEdgeFlags(),
+                    motionEvent->getClassification(), motionEvent->getEdgeFlags(),
                     motionEvent->getXPrecision(), motionEvent->getYPrecision(),
                     motionEvent->getDownTime(),
                     uint32_t(pointerCount), pointerProperties, samplePointerCoords,
@@ -3106,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;
@@ -3541,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";
@@ -3557,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),
@@ -4404,8 +4412,8 @@
 InputDispatcher::MotionEntry::MotionEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId,
         uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action,
         int32_t actionButton,
-        int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags,
-        float xPrecision, float yPrecision, nsecs_t downTime,
+        int32_t flags, int32_t metaState, int32_t buttonState, MotionClassification classification,
+        int32_t edgeFlags, float xPrecision, float yPrecision, nsecs_t downTime,
         uint32_t pointerCount,
         const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
         float xOffset, float yOffset) :
@@ -4413,7 +4421,8 @@
         eventTime(eventTime),
         deviceId(deviceId), source(source), displayId(displayId), action(action),
         actionButton(actionButton), flags(flags), metaState(metaState), buttonState(buttonState),
-        edgeFlags(edgeFlags), xPrecision(xPrecision), yPrecision(yPrecision),
+        classification(classification), edgeFlags(edgeFlags),
+        xPrecision(xPrecision), yPrecision(yPrecision),
         downTime(downTime), pointerCount(pointerCount) {
     for (uint32_t i = 0; i < pointerCount; i++) {
         this->pointerProperties[i].copyFrom(pointerProperties[i]);
@@ -4430,9 +4439,10 @@
 void InputDispatcher::MotionEntry::appendDescription(std::string& msg) const {
     msg += StringPrintf("MotionEvent(deviceId=%d, source=0x%08x, displayId=%" PRId32
             ", action=%s, actionButton=0x%08x, flags=0x%08x, metaState=0x%08x, buttonState=0x%08x, "
-            "edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, pointers=[",
+            "classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, pointers=[",
             deviceId, source, displayId, motionActionToString(action).c_str(), actionButton, flags,
-            metaState, buttonState, edgeFlags, xPrecision, yPrecision);
+            metaState, buttonState, motionClassificationToString(classification), edgeFlags,
+            xPrecision, yPrecision);
 
     for (uint32_t i = 0; i < pointerCount; i++) {
         if (i) {
@@ -4733,15 +4743,15 @@
     for (size_t i = 0; i < mMotionMementos.size(); i++) {
         const MotionMemento& memento = mMotionMementos.itemAt(i);
         if (shouldCancelMotion(memento, options)) {
+            const int32_t action = memento.hovering ?
+                    AMOTION_EVENT_ACTION_HOVER_EXIT : AMOTION_EVENT_ACTION_CANCEL;
             outEvents.push(new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime,
                     memento.deviceId, memento.source, memento.displayId, memento.policyFlags,
-                    memento.hovering
-                            ? AMOTION_EVENT_ACTION_HOVER_EXIT
-                            : AMOTION_EVENT_ACTION_CANCEL,
-                    memento.flags, 0, 0, 0, 0,
+                    action, 0 /*actionButton*/, memento.flags, AMETA_NONE, 0 /*buttonState*/,
+                    MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
                     memento.xPrecision, memento.yPrecision, memento.downTime,
                     memento.pointerCount, memento.pointerProperties, memento.pointerCoords,
-                    0, 0));
+                    0 /*xOffset*/, 0 /*yOffset*/));
         }
     }
 }
@@ -4908,6 +4918,7 @@
     source = 0;
     displayId = ADISPLAY_ID_NONE;
     windows.clear();
+    portalWindows.clear();
 }
 
 void InputDispatcher::TouchState::copyFrom(const TouchState& other) {
@@ -4917,6 +4928,7 @@
     source = other.source;
     displayId = other.displayId;
     windows = other.windows;
+    portalWindows = other.portalWindows;
 }
 
 void InputDispatcher::TouchState::addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle,
@@ -4945,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 49de6f3..2d8df5c 100644
--- a/services/inputflinger/InputDispatcher.h
+++ b/services/inputflinger/InputDispatcher.h
@@ -535,6 +535,7 @@
         int32_t flags;
         int32_t metaState;
         int32_t buttonState;
+        MotionClassification classification;
         int32_t edgeFlags;
         float xPrecision;
         float yPrecision;
@@ -546,8 +547,9 @@
         MotionEntry(uint32_t sequenceNum, nsecs_t eventTime,
                 int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
                 int32_t action, int32_t actionButton, int32_t flags,
-                int32_t metaState, int32_t buttonState, int32_t edgeFlags,
-                float xPrecision, float yPrecision, nsecs_t downTime, uint32_t pointerCount,
+                int32_t metaState, int32_t buttonState, MotionClassification classification,
+                int32_t edgeFlags, float xPrecision, float yPrecision,
+                nsecs_t downTime, uint32_t pointerCount,
                 const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
                 float xOffset, float yOffset);
         virtual void appendDescription(std::string& msg) const;
@@ -919,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;
@@ -1014,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();
@@ -1094,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 2b31f6e..f84aa34 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -57,6 +57,7 @@
 #include <android-base/stringprintf.h>
 #include <input/Keyboard.h>
 #include <input/VirtualKeyMap.h>
+#include <statslog.h>
 
 #define INDENT "  "
 #define INDENT2 "    "
@@ -71,18 +72,21 @@
 // --- Constants ---
 
 // Maximum number of slots supported when using the slot-based Multitouch Protocol B.
-static const size_t MAX_SLOTS = 32;
+static constexpr size_t MAX_SLOTS = 32;
 
 // Maximum amount of latency to add to touch events while waiting for data from an
 // external stylus.
-static const nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(72);
+static constexpr nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(72);
 
 // Maximum amount of time to wait on touch data before pushing out new pressure data.
-static const nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20);
+static constexpr nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20);
 
 // Artificial latency on synthetic events created from stylus data without corresponding touch
 // data.
-static const nsecs_t STYLUS_DATA_LATENCY = ms2ns(10);
+static constexpr nsecs_t STYLUS_DATA_LATENCY = ms2ns(10);
+
+// How often to report input event statistics
+static constexpr nsecs_t STATISTICS_REPORT_FREQUENCY = seconds_to_nanoseconds(5 * 60);
 
 // --- Static Functions ---
 
@@ -801,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);
 
@@ -1271,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 ---
 
@@ -2643,7 +2683,7 @@
         }
 
         // Update the PointerController if viewports changed.
-        if (mParameters.hasAssociatedDisplay) {
+        if (mParameters.mode == Parameters::MODE_POINTER) {
             getPolicy()->obtainPointerController(getDeviceId());
         }
         bumpGeneration();
@@ -2915,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) :
@@ -4287,12 +4340,25 @@
     mExternalStylusFusionTimeout = LLONG_MAX;
 }
 
+void TouchInputMapper::reportEventForStatistics(nsecs_t evdevTime) {
+    nsecs_t now = systemTime(CLOCK_MONOTONIC);
+    nsecs_t latency = now - evdevTime;
+    mStatistics.addValue(nanoseconds_to_microseconds(latency));
+    nsecs_t timeSinceLastReport = now - mStatistics.lastReportTime;
+    if (timeSinceLastReport > STATISTICS_REPORT_FREQUENCY) {
+        android::util::stats_write(android::util::TOUCH_EVENT_REPORTED,
+                mStatistics.min, mStatistics.max, mStatistics.mean(), mStatistics.stdev());
+        mStatistics.reset(now);
+    }
+}
+
 void TouchInputMapper::process(const RawEvent* rawEvent) {
     mCursorButtonAccumulator.process(rawEvent);
     mCursorScrollAccumulator.process(rawEvent);
     mTouchButtonAccumulator.process(rawEvent);
 
     if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
+        reportEventForStatistics(rawEvent->when);
         sync(rawEvent->when);
     }
 }
@@ -6494,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,
@@ -6813,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 35f3c23..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;
@@ -567,6 +569,69 @@
     }
 };
 
+/**
+ * Basic statistics information.
+ * Keep track of min, max, average, and standard deviation of the received samples.
+ * Used to report latency information about input events.
+ */
+struct LatencyStatistics {
+    float min;
+    float max;
+    // Sum of all samples
+    float sum;
+    // Sum of squares of all samples
+    float sum2;
+    // The number of samples
+    size_t count;
+    // The last time statistics were reported.
+    nsecs_t lastReportTime;
+
+    LatencyStatistics() {
+        reset(systemTime(SYSTEM_TIME_MONOTONIC));
+    }
+
+    inline void addValue(float x) {
+        if (x < min) {
+            min = x;
+        }
+        if (x > max) {
+            max = x;
+        }
+        sum += x;
+        sum2 += x * x;
+        count++;
+    }
+
+    // Get the average value. Should not be called if no samples have been added.
+    inline float mean() {
+        if (count == 0) {
+            return 0;
+        }
+        return sum / count;
+    }
+
+    // Get the standard deviation. Should not be called if no samples have been added.
+    inline float stdev() {
+        if (count == 0) {
+            return 0;
+        }
+        float average = mean();
+        return sqrt(sum2 / count - average * average);
+    }
+
+    /**
+     * Reset internal state. The variable 'when' is the time when the data collection started.
+     * Call this to start a new data collection window.
+     */
+    inline void reset(nsecs_t when) {
+        max = 0;
+        min = std::numeric_limits<float>::max();
+        sum = 0;
+        sum2 = 0;
+        count = 0;
+        lastReportTime = when;
+    }
+};
 
 /* Keeps track of the state of single-touch protocol. */
 class SingleTouchMotionAccumulator {
@@ -715,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;
@@ -869,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;
@@ -962,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;
@@ -1511,6 +1579,9 @@
     VelocityControl mWheelXVelocityControl;
     VelocityControl mWheelYVelocityControl;
 
+    // Latency statistics for touch events
+    struct LatencyStatistics mStatistics;
+
     std::optional<DisplayViewport> findViewport();
 
     void resetExternalStylus();
@@ -1580,6 +1651,8 @@
 
     static void assignPointerIds(const RawState* last, RawState* current);
 
+    void reportEventForStatistics(nsecs_t evdevTime);
+
     const char* modeToString(DeviceMode deviceMode);
 };
 
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 3edec40..e0e211f 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -48,9 +48,51 @@
 
 public:
     FakeInputDispatcherPolicy() {
+        mInputEventFiltered = false;
+        mTime = -1;
+        mAction = -1;
+        mDisplayId = -1;
+    }
+
+    void assertFilterInputEventWasCalledWithExpectedArgs(const NotifyMotionArgs* args) {
+        ASSERT_TRUE(mInputEventFiltered)
+                << "Expected filterInputEvent() to have been called.";
+
+        ASSERT_EQ(mTime, args->eventTime)
+                << "Expected time of filtered event was not matched";
+        ASSERT_EQ(mAction, args->action)
+                << "Expected action of filtered event was not matched";
+        ASSERT_EQ(mDisplayId, args->displayId)
+                << "Expected displayId of filtered event was not matched";
+
+        reset();
+    }
+
+    void assertFilterInputEventWasCalledWithExpectedArgs(const NotifyKeyArgs* args) {
+        ASSERT_TRUE(mInputEventFiltered)
+                << "Expected filterInputEvent() to have been called.";
+
+        ASSERT_EQ(mTime, args->eventTime)
+                << "Expected time of filtered event was not matched";
+        ASSERT_EQ(mAction, args->action)
+                << "Expected action of filtered event was not matched";
+        ASSERT_EQ(mDisplayId, args->displayId)
+                << "Expected displayId of filtered event was not matched";
+
+        reset();
+    }
+
+    void assertFilterInputEventWasNotCalled() {
+        ASSERT_FALSE(mInputEventFiltered)
+                << "Expected filterInputEvent() to not have been called.";
     }
 
 private:
+    bool mInputEventFiltered;
+    nsecs_t mTime;
+    int32_t mAction;
+    int32_t mDisplayId;
+
     virtual void notifyConfigurationChanged(nsecs_t) {
     }
 
@@ -70,7 +112,26 @@
         *outConfig = mConfig;
     }
 
-    virtual bool filterInputEvent(const InputEvent*, uint32_t) {
+    virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) {
+        switch (inputEvent->getType()) {
+            case AINPUT_EVENT_TYPE_KEY: {
+                const KeyEvent* keyEvent = static_cast<const KeyEvent*>(inputEvent);
+                mTime = keyEvent->getEventTime();
+                mAction = keyEvent->getAction();
+                mDisplayId = keyEvent->getDisplayId();
+                break;
+            }
+
+            case AINPUT_EVENT_TYPE_MOTION: {
+                const MotionEvent* motionEvent = static_cast<const MotionEvent*>(inputEvent);
+                mTime = motionEvent->getEventTime();
+                mAction = motionEvent->getAction();
+                mDisplayId = motionEvent->getDisplayId();
+                break;
+            }
+        }
+
+        mInputEventFiltered = true;
         return true;
     }
 
@@ -99,6 +160,13 @@
     virtual bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) {
         return false;
     }
+
+    void reset() {
+        mInputEventFiltered = false;
+        mTime = -1;
+        mAction = -1;
+        mDisplayId = -1;
+    }
 };
 
 
@@ -405,14 +473,9 @@
         mFocused = true;
     }
 
-    void assertNoEvents() {
-        uint32_t consumeSeq;
-        InputEvent* event;
-        status_t status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1,
-            &consumeSeq, &event);
-        ASSERT_NE(OK, status)
-                << mName.c_str()
-                << ": should not have received any events, so consume(..) should not return OK.";
+    void releaseChannel() {
+        InputWindowHandle::releaseChannel();
+        mDispatcher->unregisterInputChannel(mServerChannel);
     }
 protected:
     virtual bool handled() {
@@ -469,6 +532,40 @@
             INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
 }
 
+static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) {
+    nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    // Define a valid key event.
+    NotifyKeyArgs args(/* sequenceNum */ 0, currentTime, DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
+            displayId, POLICY_FLAG_PASS_TO_USER, action, /* flags */ 0,
+            AKEYCODE_A, KEY_A, AMETA_NONE, currentTime);
+
+    return args;
+}
+
+static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32_t displayId) {
+    PointerProperties pointerProperties[1];
+    PointerCoords pointerCoords[1];
+
+    pointerProperties[0].clear();
+    pointerProperties[0].id = 0;
+    pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+    pointerCoords[0].clear();
+    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 100);
+    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 200);
+
+    nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    // Define a valid motion event.
+    NotifyMotionArgs args(/* sequenceNum */ 0, currentTime, DEVICE_ID, source, displayId,
+            POLICY_FLAG_PASS_TO_USER, action, /* actionButton */ 0, /* flags */ 0,
+            AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
+            AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, pointerProperties,
+            pointerCoords, /* xPrecision */ 0, /* yPrecision */ 0, currentTime,
+            /* videoFrames */ {});
+
+    return args;
+}
+
 TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) {
     sp<FakeApplicationHandle> application = new FakeApplicationHandle();
     sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window",
@@ -567,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 */
@@ -740,4 +838,76 @@
     monitorInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
 }
 
+class InputFilterTest : public InputDispatcherTest {
+protected:
+    static constexpr int32_t SECOND_DISPLAY_ID = 1;
+
+    void testNotifyMotion(int32_t displayId, bool expectToBeFiltered) {
+        NotifyMotionArgs motionArgs;
+
+        motionArgs = generateMotionArgs(
+                AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, displayId);
+        mDispatcher->notifyMotion(&motionArgs);
+        motionArgs = generateMotionArgs(
+                AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, displayId);
+        mDispatcher->notifyMotion(&motionArgs);
+
+        if (expectToBeFiltered) {
+            mFakePolicy->assertFilterInputEventWasCalledWithExpectedArgs(&motionArgs);
+        } else {
+            mFakePolicy->assertFilterInputEventWasNotCalled();
+        }
+    }
+
+    void testNotifyKey(bool expectToBeFiltered) {
+        NotifyKeyArgs keyArgs;
+
+        keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN);
+        mDispatcher->notifyKey(&keyArgs);
+        keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP);
+        mDispatcher->notifyKey(&keyArgs);
+
+        if (expectToBeFiltered) {
+            mFakePolicy->assertFilterInputEventWasCalledWithExpectedArgs(&keyArgs);
+        } else {
+            mFakePolicy->assertFilterInputEventWasNotCalled();
+        }
+    }
+};
+
+// Test InputFilter for MotionEvent
+TEST_F(InputFilterTest, MotionEvent_InputFilter) {
+    // Since the InputFilter is disabled by default, check if touch events aren't filtered.
+    testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered*/ false);
+    testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered*/ false);
+
+    // Enable InputFilter
+    mDispatcher->setInputFilterEnabled(true);
+    // Test touch on both primary and second display, and check if both events are filtered.
+    testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered*/ true);
+    testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered*/ true);
+
+    // Disable InputFilter
+    mDispatcher->setInputFilterEnabled(false);
+    // Test touch on both primary and second display, and check if both events aren't filtered.
+    testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered*/ false);
+    testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered*/ false);
+}
+
+// Test InputFilter for KeyEvent
+TEST_F(InputFilterTest, KeyEvent_InputFilter) {
+    // Since the InputFilter is disabled by default, check if key event aren't filtered.
+    testNotifyKey(/*expectToBeFiltered*/ false);
+
+    // Enable InputFilter
+    mDispatcher->setInputFilterEnabled(true);
+    // Send a key event, and check if it is filtered.
+    testNotifyKey(/*expectToBeFiltered*/ true);
+
+    // Disable InputFilter
+    mDispatcher->setInputFilterEnabled(false);
+    // Send a key event, and check if it isn't filtered.
+    testNotifyKey(/*expectToBeFiltered*/ false);
+}
+
 } // namespace android
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/SensorRecord.h b/services/sensorservice/SensorRecord.h
index 5a35410..031744a 100644
--- a/services/sensorservice/SensorRecord.h
+++ b/services/sensorservice/SensorRecord.h
@@ -25,7 +25,7 @@
 
 class SensorService::SensorRecord {
 public:
-    SensorRecord(const sp<const SensorEventConnection>& connection);
+    explicit SensorRecord(const sp<const SensorEventConnection>& connection);
     bool addConnection(const sp<const SensorEventConnection>& connection);
     bool removeConnection(const wp<const SensorEventConnection>& connection);
     size_t getNumConnections() const { return mConnections.size(); }
diff --git a/services/sensorservice/hidl/include/sensorservicehidl/SensorManager.h b/services/sensorservice/hidl/include/sensorservicehidl/SensorManager.h
index ddcee28..8d7a05b 100644
--- a/services/sensorservice/hidl/include/sensorservicehidl/SensorManager.h
+++ b/services/sensorservice/hidl/include/sensorservicehidl/SensorManager.h
@@ -43,7 +43,7 @@
 
 struct SensorManager final : public ISensorManager {
 
-    SensorManager(JavaVM* vm);
+    explicit SensorManager(JavaVM* vm);
     ~SensorManager();
 
     // Methods from ::android::frameworks::sensorservice::V1_0::ISensorManager follow.
diff --git a/services/sensorservice/mat.h b/services/sensorservice/mat.h
index 495c14e..934ae58 100644
--- a/services/sensorservice/mat.h
+++ b/services/sensorservice/mat.h
@@ -139,13 +139,13 @@
 
     mat() { }
     mat(const mat& rhs)  : base(rhs) { }
-    mat(const base& rhs) : base(rhs) { }  // NOLINT(implicit)
+    mat(const base& rhs) : base(rhs) { }  // NOLINT(google-explicit-constructor)
 
     // -----------------------------------------------------------------------
     // conversion constructors
 
     // sets the diagonal to the value, off-diagonal to zero
-    mat(pTYPE rhs) {  // NOLINT(implicit)
+    mat(pTYPE rhs) {  // NOLINT(google-explicit-constructor)
         helpers::doAssign(*this, rhs);
     }
 
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/sensorservice/vec.h b/services/sensorservice/vec.h
index 9e5d280..c195434 100644
--- a/services/sensorservice/vec.h
+++ b/services/sensorservice/vec.h
@@ -322,12 +322,12 @@
 
     vec() { }
     vec(const vec& rhs)  : base(rhs) { }
-    vec(const base& rhs) : base(rhs) { }  // NOLINT(implicit)
+    vec(const base& rhs) : base(rhs) { }  // NOLINT(google-explicit-constructor)
 
     // -----------------------------------------------------------------------
     // conversion constructors
 
-    vec(pTYPE rhs) {  // NOLINT(implicit)
+    vec(pTYPE rhs) {  // NOLINT(google-explicit-constructor)
         for (size_t i=0 ; i<SIZE ; i++)
             base::operator[](i) = rhs;
     }
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 3dee0c0..2a70293 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -25,6 +25,7 @@
         "android.hardware.configstore@1.1",
         "android.hardware.configstore@1.2",
         "android.hardware.graphics.allocator@2.0",
+        "android.hardware.graphics.allocator@3.0",
         "android.hardware.graphics.common@1.2",
         "android.hardware.graphics.composer@2.1",
         "android.hardware.graphics.composer@2.2",
@@ -49,12 +50,15 @@
         "liblog",
 	"libnativewindow",
         "libpdx_default_transport",
+        "libprocessgroup",
         "libprotobuf-cpp-lite",
         "libsync",
         "libtimestats_proto",
         "libui",
         "libinput",
         "libutils",
+        "libutilscallstack",
+        "libSurfaceFlingerProperties",
     ],
     static_libs: [
         "libcompositionengine",
@@ -76,6 +80,7 @@
     ],
     export_shared_lib_headers: [
         "android.hardware.graphics.allocator@2.0",
+        "android.hardware.graphics.allocator@3.0",
         "android.hardware.graphics.common@1.2",
         "android.hardware.graphics.composer@2.1",
         "android.hardware.graphics.composer@2.2",
@@ -113,6 +118,7 @@
         "BufferLayerConsumer.cpp",
         "BufferQueueLayer.cpp",
         "BufferStateLayer.cpp",
+        "BufferStateLayerCache.cpp",
         "Client.cpp",
         "ColorLayer.cpp",
         "ContainerLayer.cpp",
@@ -185,6 +191,7 @@
         "android.hardware.configstore@1.0",
         "android.hardware.configstore@1.2",
         "android.hardware.graphics.allocator@2.0",
+        "android.hardware.graphics.allocator@3.0",
         "libbinder",
         "libcutils",
         "libdisplayservicehidl",
@@ -193,6 +200,7 @@
         "libinput",
         "liblayers_proto",
         "liblog",
+        "libprocessgroup",
         "libsync",
         "libtimestats_proto",
         "libutils",
@@ -214,7 +222,10 @@
     defaults: ["libsurfaceflinger_binary"],
     init_rc: ["surfaceflinger.rc"],
     srcs: [":surfaceflinger_binary_sources"],
-    shared_libs: ["libsurfaceflinger"],
+    shared_libs: [
+        "libsurfaceflinger",
+        "libSurfaceFlingerProperties",
+    ],
 }
 
 cc_library_shared {
@@ -225,6 +236,7 @@
         "libcutils",
         "libdl",
         "liblog",
+        "libprocessgroup",
     ],
     product_variables: {
         // uses jni which may not be available in PDK
@@ -239,3 +251,26 @@
     "TimeStats/timestatsproto",
     "tests",
 ]
+
+cc_library_shared {
+    name: "libSurfaceFlingerProperties",
+    srcs: [
+        "SurfaceFlingerProperties.cpp",
+        "sysprop/*.sysprop",
+    ],
+    shared_libs: [
+        "android.hardware.configstore-utils",
+        "android.hardware.configstore@1.0",
+        "android.hardware.configstore@1.1",
+        "android.hardware.configstore@1.2",
+        "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
+        "libutils",
+    ],
+    export_shared_lib_headers: [
+        "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
+    ],
+}
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 164a3a6..4eafeac 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -624,19 +624,16 @@
      * 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
 
-    // Convert to Rect so that bounds are clipped to integers.
-    const Rect win{computeCrop(Rect::INVALID_RECT)};
-    // computeCrop() returns the cropping rectangle in buffer space, so we
-    // shouldn't use getBufferSize() since that may return a rectangle specified
-    // in layer space. Otherwise we may compute incorrect texture coordinates.
-    const float bufWidth = float(mActiveBuffer->getWidth());
-    const float bufHeight = float(mActiveBuffer->getHeight());
+    Rect win = bounds;
+    const int bufferWidth = getBufferSize(s).getWidth();
+    const int bufferHeight = getBufferSize(s).getHeight();
 
-    const float left = win.left / bufWidth;
-    const float top = win.top / bufHeight;
-    const float right = win.right / bufWidth;
-    const float bottom = win.bottom / bufHeight;
+    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
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index e0d9d23..2132f59 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -48,10 +48,28 @@
 // Interface implementation for Layer
 // -----------------------------------------------------------------------
 void BufferStateLayer::onLayerDisplayed(const sp<Fence>& releaseFence) {
-    // The transaction completed callback can only be sent if the release fence from the PREVIOUS
-    // frame has fired. In practice, we should never actually wait on the previous release fence
-    // but we should store it just in case.
-    mPreviousReleaseFence = releaseFence;
+    // The previous release fence notifies the client that SurfaceFlinger is done with the previous
+    // buffer that was presented on this layer. The first transaction that came in this frame that
+    // replaced the previous buffer on this layer needs this release fence, because the fence will
+    // let the client know when that previous buffer is removed from the screen.
+    //
+    // Every other transaction on this layer does not need a release fence because no other
+    // Transactions that were set on this layer this frame are going to have their preceeding buffer
+    // removed from the display this frame.
+    //
+    // For example, if we have 3 transactions this frame. The first transaction doesn't contain a
+    // buffer so it doesn't need a previous release fence because the layer still needs the previous
+    // buffer. The second transaction contains a buffer so it needs a previous release fence because
+    // the previous buffer will be released this frame. The third transaction also contains a
+    // buffer. It replaces the buffer in the second transaction. The buffer in the second
+    // transaction will now no longer be presented so it is released immediately and the third
+    // transaction doesn't need a previous release fence.
+    for (auto& handle : mDrawingState.callbackHandles) {
+        if (handle->releasePreviousBuffer) {
+            handle->previousReleaseFence = releaseFence;
+            break;
+        }
+    }
 }
 
 void BufferStateLayer::setTransformHint(uint32_t /*orientation*/) const {
@@ -60,7 +78,10 @@
 }
 
 void BufferStateLayer::releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) {
-    return;
+    mFlinger->getTransactionCompletedThread().addPresentedCallbackHandles(
+            mDrawingState.callbackHandles);
+
+    mDrawingState.callbackHandles = {};
 }
 
 bool BufferStateLayer::shouldPresentNow(nsecs_t /*expectedPresentTime*/) const {
@@ -257,14 +278,14 @@
 
             // Notify the transaction completed thread that there is a pending latched callback
             // handle
-            mFlinger->getTransactionCompletedThread().registerPendingLatchedCallbackHandle(handle);
+            mFlinger->getTransactionCompletedThread().registerPendingCallbackHandle(handle);
 
             // Store so latched time and release fence can be set
             mCurrentState.callbackHandles.push_back(handle);
 
         } else { // If this layer will NOT need to be relatched and presented this frame
             // Notify the transaction completed thread this handle is done
-            mFlinger->getTransactionCompletedThread().addUnlatchedCallbackHandle(handle);
+            mFlinger->getTransactionCompletedThread().addUnpresentedCallbackHandle(handle);
         }
     }
 
@@ -504,9 +525,9 @@
         return BAD_VALUE;
     }
 
-    mFlinger->getTransactionCompletedThread()
-            .addLatchedCallbackHandles(getDrawingState().callbackHandles, latchTime,
-                                       mPreviousReleaseFence);
+    for (auto& handle : mDrawingState.callbackHandles) {
+        handle->latchTime = latchTime;
+    }
 
     // Handle sync fences
     if (SyncFeatures::getInstance().useNativeFenceSync() && releaseFence != Fence::NO_FENCE) {
diff --git a/services/surfaceflinger/BufferStateLayerCache.cpp b/services/surfaceflinger/BufferStateLayerCache.cpp
new file mode 100644
index 0000000..c82ad7b
--- /dev/null
+++ b/services/surfaceflinger/BufferStateLayerCache.cpp
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "BufferStateLayerCache"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "BufferStateLayerCache.h"
+
+#define MAX_CACHE_SIZE 64
+
+namespace android {
+
+int32_t BufferStateLayerCache::add(const sp<IBinder>& processToken,
+                                   const sp<GraphicBuffer>& buffer) {
+    std::lock_guard lock(mMutex);
+
+    auto& processCache = getProccessCache(processToken);
+
+    int32_t slot = findSlot(processCache);
+    if (slot < 0) {
+        return slot;
+    }
+
+    processCache[slot] = buffer;
+
+    return slot;
+}
+
+void BufferStateLayerCache::release(const sp<IBinder>& processToken, int32_t id) {
+    if (id < 0) {
+        ALOGE("invalid buffer id");
+        return;
+    }
+
+    std::lock_guard lock(mMutex);
+    auto& processCache = getProccessCache(processToken);
+
+    if (id >= processCache.size()) {
+        ALOGE("invalid buffer id");
+        return;
+    }
+    processCache[id] = nullptr;
+}
+
+sp<GraphicBuffer> BufferStateLayerCache::get(const sp<IBinder>& processToken, int32_t id) {
+    if (id < 0) {
+        ALOGE("invalid buffer id");
+        return nullptr;
+    }
+
+    std::lock_guard lock(mMutex);
+    auto& processCache = getProccessCache(processToken);
+
+    if (id >= processCache.size()) {
+        ALOGE("invalid buffer id");
+        return nullptr;
+    }
+    return processCache[id];
+}
+
+std::vector<sp<GraphicBuffer>>& BufferStateLayerCache::getProccessCache(
+        const sp<IBinder>& processToken) {
+    return mBuffers[processToken];
+}
+
+int32_t BufferStateLayerCache::findSlot(std::vector<sp<GraphicBuffer>>& processCache) {
+    int32_t slot = 0;
+
+    for (const sp<GraphicBuffer> buffer : processCache) {
+        if (!buffer) {
+            return slot;
+        }
+        slot++;
+    }
+
+    if (processCache.size() < MAX_CACHE_SIZE) {
+        processCache.push_back(nullptr);
+        return slot;
+    }
+
+    return -1;
+}
+
+}; // namespace android
diff --git a/services/surfaceflinger/BufferStateLayerCache.h b/services/surfaceflinger/BufferStateLayerCache.h
new file mode 100644
index 0000000..623f0c6
--- /dev/null
+++ b/services/surfaceflinger/BufferStateLayerCache.h
@@ -0,0 +1,55 @@
+/*
+ * 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-base/thread_annotations.h>
+#include <binder/IBinder.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/RefBase.h>
+
+#include <mutex>
+#include <unordered_map>
+#include <vector>
+
+namespace android {
+
+class BufferStateLayerCache {
+public:
+    int32_t add(const sp<IBinder>& processToken, const sp<GraphicBuffer>& buffer);
+    void release(const sp<IBinder>& processToken, int32_t id);
+
+    sp<GraphicBuffer> get(const sp<IBinder>& processToken, int32_t id);
+
+private:
+    std::mutex mMutex;
+
+    std::vector<sp<GraphicBuffer>>& getProccessCache(const sp<IBinder>& processToken)
+            REQUIRES(mMutex);
+
+    int32_t findSlot(std::vector<sp<GraphicBuffer>>& proccessCache) REQUIRES(mMutex);
+
+    struct IBinderHash {
+        std::size_t operator()(const sp<IBinder>& strongPointer) const {
+            return std::hash<IBinder*>{}(strongPointer.get());
+        }
+    };
+
+    std::unordered_map<sp<IBinder> /*caching process*/, std::vector<sp<GraphicBuffer>>, IBinderHash>
+            mBuffers GUARDED_BY(mMutex);
+};
+
+}; // namespace android
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.h b/services/surfaceflinger/ColorLayer.h
index aecde9f..d1b1697 100644
--- a/services/surfaceflinger/ColorLayer.h
+++ b/services/surfaceflinger/ColorLayer.h
@@ -39,7 +39,7 @@
     bool onPreComposition(nsecs_t /*refreshStartTime*/) override { return false; }
 
 protected:
-    FloatRect computeCrop(const Rect& /*windowbounds*/) const override { return {}; }
+    FloatRect computeCrop(const sp<const DisplayDevice>& /*display*/) const override { return {}; }
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index ec5e131..49fa84a 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -16,6 +16,8 @@
         "libgui",
         "liblayers_proto",
         "liblog",
+        "libnativewindow",
+        "libsync",
         "libtimestats_proto",
         "libui",
         "libutils",
@@ -35,6 +37,13 @@
     defaults: ["libcompositionengine_defaults"],
     srcs: [
         "src/CompositionEngine.cpp",
+        "src/Display.cpp",
+        "src/DisplayColorProfile.cpp",
+        "src/DisplaySurface.cpp",
+        "src/DumpHelpers.cpp",
+        "src/Output.cpp",
+        "src/OutputCompositionState.cpp",
+        "src/RenderSurface.cpp",
     ],
     local_include_dirs: ["include"],
     export_include_dirs: ["include"],
@@ -45,6 +54,11 @@
     defaults: ["libcompositionengine_defaults"],
     srcs: [
         "mock/CompositionEngine.cpp",
+        "mock/Display.cpp",
+        "mock/DisplayColorProfile.cpp",
+        "mock/DisplaySurface.cpp",
+        "mock/Output.cpp",
+        "mock/RenderSurface.cpp",
     ],
     static_libs: [
         "libgtest",
@@ -61,7 +75,11 @@
     defaults: ["libcompositionengine_defaults"],
     srcs: [
         "tests/CompositionEngineTest.cpp",
+        "tests/DisplayColorProfileTest.cpp",
+        "tests/DisplayTest.cpp",
         "tests/MockHWComposer.cpp",
+        "tests/OutputTest.cpp",
+        "tests/RenderSurfaceTest.cpp",
     ],
     static_libs: [
         "libcompositionengine",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
index af8515f..30cdb49 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
@@ -28,6 +28,10 @@
 
 namespace compositionengine {
 
+class Display;
+
+struct DisplayCreationArgs;
+
 /**
  * Encapsulates all the interfaces and implementation details for performing
  * display output composition.
@@ -36,6 +40,9 @@
 public:
     virtual ~CompositionEngine();
 
+    // Create a composition Display
+    virtual std::shared_ptr<Display> createDisplay(DisplayCreationArgs&&) = 0;
+
     virtual HWComposer& getHwComposer() const = 0;
     virtual void setHwComposer(std::unique_ptr<HWComposer>) = 0;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
new file mode 100644
index 0000000..dbcd3bd
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
@@ -0,0 +1,59 @@
+/*
+ * 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 <cstdint>
+#include <optional>
+
+#include "DisplayHardware/DisplayIdentification.h"
+
+#include <compositionengine/Output.h>
+
+namespace android::compositionengine {
+
+struct RenderSurfaceCreationArgs;
+struct DisplayColorProfileCreationArgs;
+
+/**
+ * A display is a composition target which may be backed by a hardware composer
+ * display device
+ */
+class Display : public virtual Output {
+public:
+    // Gets the HWC DisplayId for the display if there is one
+    virtual const std::optional<DisplayId>& getId() const = 0;
+
+    // True if the display is secure
+    virtual bool isSecure() const = 0;
+
+    // True if the display is virtual
+    virtual bool isVirtual() const = 0;
+
+    // Releases the use of the HWC display, if any
+    virtual void disconnect() = 0;
+
+    // Creates a render color mode for the display
+    virtual void createDisplayColorProfile(DisplayColorProfileCreationArgs&&) = 0;
+
+    // Creates a render surface for the display
+    virtual void createRenderSurface(RenderSurfaceCreationArgs&&) = 0;
+
+protected:
+    ~Display() = default;
+};
+
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
new file mode 100644
index 0000000..e2a0d42
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.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 <cstdint>
+
+#include <ui/GraphicTypes.h>
+
+namespace android {
+
+class HdrCapabilities;
+
+namespace compositionengine {
+
+/**
+ * Encapsulates all the state and functionality for how colors should be
+ * transformed for a display
+ */
+class DisplayColorProfile {
+public:
+    constexpr static float sDefaultMinLumiance = 0.0;
+    constexpr static float sDefaultMaxLumiance = 500.0;
+
+    virtual ~DisplayColorProfile();
+
+    // Returns true if the profile is valid. This is meant to be checked post-
+    // construction and prior to use, as not everything is set up by the
+    // constructor.
+    virtual bool isValid() const = 0;
+
+    // Returns true if the profile supports the indicated render intent
+    virtual bool hasRenderIntent(ui::RenderIntent) const = 0;
+
+    // Returns true if the profile supports the indicated dataspace
+    virtual bool hasLegacyHdrSupport(ui::Dataspace) const = 0;
+
+    // Obtains the best combination of color mode and render intent for the
+    // input values
+    virtual void getBestColorMode(ui::Dataspace dataspace, ui::RenderIntent intent,
+                                  ui::Dataspace* outDataspace, ui::ColorMode* outMode,
+                                  ui::RenderIntent* outIntent) const = 0;
+
+    // Returns true if the profile supports a wide color gamut
+    virtual bool hasWideColorGamut() const = 0;
+
+    // Returns the per-frame metadata value for this profile
+    virtual int32_t getSupportedPerFrameMetadata() const = 0;
+
+    // Returns true if HWC for this profile supports HDR10Plus
+    virtual bool hasHDR10PlusSupport() const = 0;
+
+    // Returns true if HWC for this profile supports HDR10
+    virtual bool hasHDR10Support() const = 0;
+
+    // Returns true if HWC for this profile supports HLG
+    virtual bool hasHLGSupport() const = 0;
+
+    // Returns true if HWC for this profile supports DolbyVision
+    virtual bool hasDolbyVisionSupport() const = 0;
+
+    // Gets the supported HDR capabilities for the profile
+    virtual const HdrCapabilities& getHdrCapabilities() const = 0;
+
+    // Debugging
+    virtual void dump(std::string&) const = 0;
+};
+
+} // namespace compositionengine
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfileCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfileCreationArgs.h
new file mode 100644
index 0000000..ef0f925
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfileCreationArgs.h
@@ -0,0 +1,89 @@
+/*
+ * 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 <cstdint>
+#include <unordered_map>
+#include <vector>
+
+#include <ui/GraphicTypes.h>
+#include <ui/HdrCapabilities.h>
+
+namespace android::compositionengine {
+
+/**
+ * A parameter object for creating DisplayColorProfile instances
+ */
+struct DisplayColorProfileCreationArgs {
+    using HwcColorModes = std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>>;
+
+    // True if this display supports a wide color gamut
+    bool hasWideColorGamut;
+
+    // The HDR capabilities supported by the HWC
+    HdrCapabilities hdrCapabilities;
+
+    // The per-frame metadata supported by the HWC
+    int32_t supportedPerFrameMetadata;
+
+    // The mapping of color modes and render intents supported by the HWC
+    HwcColorModes hwcColorModes;
+};
+
+/**
+ * A helper for setting up a DisplayColorProfileCreationArgs value in-line.
+ *
+ * Prefer this builder over raw structure initialization.
+ *
+ * Instead of:
+ *
+ *   DisplayColorProfileCreationArgs{false, HdrCapabilities(), 0,
+ *                                   HwcColorModes()}
+ *
+ * Prefer:
+ *
+ *  DisplayColorProfileCreationArgsBuilder().setHasWideColorGamut(false)
+ *      .setIsVirtual(false).setDisplayId(displayId).Build();
+ */
+class DisplayColorProfileCreationArgsBuilder {
+public:
+    DisplayColorProfileCreationArgs Build() { return std::move(mArgs); }
+
+    DisplayColorProfileCreationArgsBuilder& setHasWideColorGamut(bool hasWideColorGamut) {
+        mArgs.hasWideColorGamut = hasWideColorGamut;
+        return *this;
+    }
+    DisplayColorProfileCreationArgsBuilder& setHdrCapabilities(HdrCapabilities&& hdrCapabilities) {
+        mArgs.hdrCapabilities = std::move(hdrCapabilities);
+        return *this;
+    }
+    DisplayColorProfileCreationArgsBuilder& setSupportedPerFrameMetadata(
+            int32_t supportedPerFrameMetadata) {
+        mArgs.supportedPerFrameMetadata = supportedPerFrameMetadata;
+        return *this;
+    }
+    DisplayColorProfileCreationArgsBuilder& setHwcColorModes(
+            DisplayColorProfileCreationArgs::HwcColorModes&& hwcColorModes) {
+        mArgs.hwcColorModes = std::move(hwcColorModes);
+        return *this;
+    }
+
+private:
+    DisplayColorProfileCreationArgs mArgs;
+};
+
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
new file mode 100644
index 0000000..0b6b4e4
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
@@ -0,0 +1,76 @@
+/*
+ * 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 <cstdint>
+#include <optional>
+
+#include "DisplayHardware/DisplayIdentification.h"
+
+namespace android::compositionengine {
+
+class CompositionEngine;
+
+/**
+ * A parameter object for creating Display instances
+ */
+struct DisplayCreationArgs {
+    // True if this display is secure
+    bool isSecure = false;
+
+    // True if this display is a virtual display
+    bool isVirtual = false;
+
+    // Identifies the display to the HWC, if composition is supported by it
+    std::optional<DisplayId> displayId;
+};
+
+/**
+ * A helper for setting up a DisplayCreationArgs value in-line.
+ * Prefer this builder over raw structure initialization.
+ *
+ * Instead of:
+ *
+ *   DisplayCreationArgs{false, false, displayId}
+ *
+ * Prefer:
+ *
+ *  DisplayCreationArgsBuilder().setIsSecure(false).setIsVirtual(false)
+ *      .setDisplayId(displayId).build();
+ */
+class DisplayCreationArgsBuilder {
+public:
+    DisplayCreationArgs build() { return std::move(mArgs); }
+
+    DisplayCreationArgsBuilder& setIsSecure(bool isSecure) {
+        mArgs.isSecure = isSecure;
+        return *this;
+    }
+    DisplayCreationArgsBuilder& setIsVirtual(bool isVirtual) {
+        mArgs.isVirtual = isVirtual;
+        return *this;
+    }
+    DisplayCreationArgsBuilder& setDisplayId(std::optional<DisplayId> displayId) {
+        mArgs.displayId = displayId;
+        return *this;
+    }
+
+private:
+    DisplayCreationArgs mArgs;
+};
+
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/DisplayHardware/DisplaySurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
similarity index 80%
rename from services/surfaceflinger/DisplayHardware/DisplaySurface.h
rename to services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
index f744f5c..0e67acf 100644
--- a/services/surfaceflinger/DisplayHardware/DisplaySurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
@@ -14,23 +14,27 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_SF_DISPLAY_SURFACE_H
-#define ANDROID_SF_DISPLAY_SURFACE_H
+#pragma once
 
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
 #include <utils/StrongPointer.h>
 
-// ---------------------------------------------------------------------------
 namespace android {
-// ---------------------------------------------------------------------------
 
 class Fence;
 class IGraphicBufferProducer;
 class String8;
 
+namespace compositionengine {
+
+/**
+ * An abstraction for working with a display surface (buffer queue)
+ */
 class DisplaySurface : public virtual RefBase {
 public:
+    virtual ~DisplaySurface();
+
     // beginFrame is called at the beginning of the composition loop, before
     // the configuration is known. The DisplaySurface should do anything it
     // needs to do to enable HWComposer to decide how to compose the frame.
@@ -44,9 +48,9 @@
     // GLES and HWC for this frame.
     enum CompositionType {
         COMPOSITION_UNKNOWN = 0,
-        COMPOSITION_GLES    = 1,
-        COMPOSITION_HWC     = 2,
-        COMPOSITION_MIXED   = COMPOSITION_GLES | COMPOSITION_HWC
+        COMPOSITION_GLES = 1,
+        COMPOSITION_HWC = 2,
+        COMPOSITION_MIXED = COMPOSITION_GLES | COMPOSITION_HWC
     };
     virtual status_t prepareFrame(CompositionType compositionType) = 0;
 
@@ -70,15 +74,7 @@
     virtual void resizeBuffers(const uint32_t w, const uint32_t h) = 0;
 
     virtual const sp<Fence>& getClientTargetAcquireFence() const = 0;
-
-protected:
-    DisplaySurface() {}
-    virtual ~DisplaySurface() {}
 };
 
-// ---------------------------------------------------------------------------
+} // namespace compositionengine
 } // namespace android
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_SF_DISPLAY_SURFACE_H
-
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
new file mode 100644
index 0000000..69b5728
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -0,0 +1,113 @@
+/*
+ * 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 <cstdint>
+#include <string>
+
+#include <math/mat4.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Region.h>
+#include <ui/Transform.h>
+
+namespace android::compositionengine {
+
+class DisplayColorProfile;
+class RenderSurface;
+
+namespace impl {
+struct OutputCompositionState;
+} // namespace impl
+
+/**
+ * Encapsulates all the state involved with composing layers for an output
+ */
+class Output {
+public:
+    // Returns true if the output is valid. This is meant to be checked post-
+    // construction and prior to use, as not everything is set up by the
+    // constructor.
+    virtual bool isValid() const = 0;
+
+    // Enables (or disables) composition on this output
+    virtual void setCompositionEnabled(bool) = 0;
+
+    // Sets the projection state to use
+    virtual void setProjection(const ui::Transform&, int32_t orientation, const Rect& frame,
+                               const Rect& viewport, const Rect& scissor, bool needsFiltering) = 0;
+    // Sets the bounds to use
+    virtual void setBounds(const ui::Size&) = 0;
+
+    // Sets the layer stack filtering settings for this output. See
+    // belongsInOutput for full details.
+    virtual void setLayerStackFilter(uint32_t layerStackId, bool isInternal) = 0;
+
+    // Sets the color transform matrix to use
+    virtual void setColorTransform(const mat4&) = 0;
+
+    // Sets the output color mode
+    virtual void setColorMode(ui::ColorMode, ui::Dataspace, ui::RenderIntent) = 0;
+
+    // Outputs a string with a state dump
+    virtual void dump(std::string&) const = 0;
+
+    // Gets the debug name for the output
+    virtual const std::string& getName() const = 0;
+
+    // Sets a debug name for the output
+    virtual void setName(const std::string&) = 0;
+
+    // Gets the current render color mode for the output
+    virtual DisplayColorProfile* getDisplayColorProfile() const = 0;
+
+    // Gets the current render surface for the output
+    virtual RenderSurface* getRenderSurface() const = 0;
+
+    using OutputCompositionState = compositionengine::impl::OutputCompositionState;
+
+    // Gets the raw composition state data for the output
+    // TODO(lpique): Make this protected once it is only internally called.
+    virtual const OutputCompositionState& getState() const = 0;
+
+    // Allows mutable access to the raw composition state data for the output.
+    // This is meant to be used by the various functions that are part of the
+    // composition process.
+    // 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;
+
+    // Tests whether a given layerStackId belongs in this output.
+    // A layer belongs to the output if its layerStackId matches the of the output layerStackId,
+    // unless the layer should display on the primary output only and this is not the primary output
+
+    // A layer belongs to the output if its layerStackId matches. Additionally
+    // if the layer should only show in the internal (primary) display only and
+    // this output allows that.
+    virtual bool belongsInOutput(uint32_t layerStackId, bool internalOnly) const = 0;
+
+protected:
+    ~Output() = default;
+
+    virtual void setDisplayColorProfile(std::unique_ptr<DisplayColorProfile>) = 0;
+    virtual void setRenderSurface(std::unique_ptr<RenderSurface>) = 0;
+};
+
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
new file mode 100644
index 0000000..dd01b05
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
@@ -0,0 +1,104 @@
+/*
+ * 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 <cstdint>
+#include <vector>
+
+#include <ui/Fence.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Size.h>
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+class GraphicBuffer;
+
+struct CompositionInfo;
+
+namespace compositionengine {
+
+/**
+ * Encapsulates everything for composing to a render surface with RenderEngine
+ */
+class RenderSurface {
+public:
+    virtual ~RenderSurface();
+
+    // Returns true if the render surface is valid. This is meant to be checked
+    // post-construction and prior to use, as not everything is set up by the
+    // constructor.
+    virtual bool isValid() const = 0;
+
+    // Performs one-time initialization of the render surface. This is meant
+    // to be called after the validation check.
+    virtual void initialize() = 0;
+
+    // Returns the bounds of the surface
+    virtual const ui::Size& getSize() const = 0;
+
+    // Gets the latest fence to pass to the HWC to signal that the surface
+    // buffer is done rendering
+    virtual const sp<Fence>& getClientTargetAcquireFence() const = 0;
+
+    // Sets the size of the surface
+    virtual void setDisplaySize(const ui::Size&) = 0;
+
+    // Sets the dataspace used for rendering the surface
+    virtual void setBufferDataspace(ui::Dataspace) = 0;
+
+    // Configures the protected rendering on the surface
+    virtual void setProtected(bool useProtected) = 0;
+
+    // Called to signal that rendering has started. 'mustRecompose' should be
+    // true if the entire frame must be recomposed.
+    virtual status_t beginFrame(bool mustRecompose) = 0;
+
+    // Prepares the frame for rendering
+    virtual status_t prepareFrame(std::vector<CompositionInfo>& compositionData) = 0;
+
+    // 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;
+
+    // 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;
+
+    // Called after the surface has been rendering to signal the surface should
+    // be made ready for displaying
+    virtual void flip() = 0;
+
+    // Debugging - Dumps the state of the RenderSurface to a string
+    virtual void dump(std::string& result) const = 0;
+
+    // Debugging - gets the page flip count for the RenderSurface
+    virtual std::uint32_t getPageFlipCount() const = 0;
+};
+
+} // namespace compositionengine
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurfaceCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurfaceCreationArgs.h
new file mode 100644
index 0000000..a1230b3
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurfaceCreationArgs.h
@@ -0,0 +1,89 @@
+/*
+ * 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 <cstdint>
+#include <memory>
+
+#include <compositionengine/DisplaySurface.h>
+#include <utils/StrongPointer.h>
+
+struct ANativeWindow;
+
+namespace android {
+
+namespace compositionengine {
+
+class Display;
+
+/**
+ * A parameter object for creating RenderSurface instances
+ */
+struct RenderSurfaceCreationArgs {
+    // The initial width of the surface
+    int32_t displayWidth;
+
+    // The initial height of the surface
+    int32_t displayHeight;
+
+    // The ANativeWindow for the buffer queue for this surface
+    sp<ANativeWindow> nativeWindow;
+
+    // The DisplaySurface for this surface
+    sp<DisplaySurface> displaySurface;
+};
+
+/**
+ * A helper for setting up a RenderSurfaceCreationArgs value in-line.
+ * Prefer this builder over raw structure initialization.
+ *
+ * Instead of:
+ *
+ *   RenderSurfaceCreationArgs{1000, 1000, nativeWindow, displaySurface}
+ *
+ * Prefer:
+ *
+ *  RenderSurfaceCreationArgsBuilder().setDisplayWidth(1000).setDisplayHeight(1000)
+ *      .setNativeWindow(nativeWindow).setDisplaySurface(displaySurface).Build();
+ */
+class RenderSurfaceCreationArgsBuilder {
+public:
+    RenderSurfaceCreationArgs build() { return std::move(mArgs); }
+
+    RenderSurfaceCreationArgsBuilder& setDisplayWidth(int32_t displayWidth) {
+        mArgs.displayWidth = displayWidth;
+        return *this;
+    }
+    RenderSurfaceCreationArgsBuilder& setDisplayHeight(int32_t displayHeight) {
+        mArgs.displayHeight = displayHeight;
+        return *this;
+    }
+    RenderSurfaceCreationArgsBuilder& setNativeWindow(sp<ANativeWindow> nativeWindow) {
+        mArgs.nativeWindow = nativeWindow;
+        return *this;
+    }
+    RenderSurfaceCreationArgsBuilder& setDisplaySurface(sp<DisplaySurface> displaySurface) {
+        mArgs.displaySurface = displaySurface;
+        return *this;
+    }
+
+private:
+    RenderSurfaceCreationArgs mArgs;
+};
+
+} // namespace compositionengine
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
index 86d1774..e23c431 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
@@ -25,6 +25,9 @@
     CompositionEngine();
     ~CompositionEngine() override;
 
+    std::shared_ptr<compositionengine::Display> createDisplay(
+            compositionengine::DisplayCreationArgs&&) override;
+
     HWComposer& getHwComposer() const override;
     void setHwComposer(std::unique_ptr<HWComposer>) override;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
new file mode 100644
index 0000000..0e20c43
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -0,0 +1,60 @@
+/*
+ * 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 <memory>
+
+#include <compositionengine/Display.h>
+#include <compositionengine/impl/Output.h>
+
+#include "DisplayHardware/DisplayIdentification.h"
+
+namespace android::compositionengine {
+
+class CompositionEngine;
+
+struct DisplayCreationArgs;
+
+namespace impl {
+
+class Display : public compositionengine::impl::Output, public compositionengine::Display {
+public:
+    Display(const CompositionEngine&, compositionengine::DisplayCreationArgs&&);
+    virtual ~Display();
+
+    // compositionengine::Output overrides
+    void dump(std::string&) const override;
+    void setColorTransform(const mat4&) override;
+    void setColorMode(ui::ColorMode, ui::Dataspace, ui::RenderIntent) override;
+
+    // compositionengine::Display overrides
+    const std::optional<DisplayId>& getId() const override;
+    bool isSecure() const override;
+    bool isVirtual() const override;
+    void disconnect() override;
+    void createDisplayColorProfile(compositionengine::DisplayColorProfileCreationArgs&&) override;
+    void createRenderSurface(compositionengine::RenderSurfaceCreationArgs&&) override;
+
+private:
+    const bool mIsVirtual;
+    std::optional<DisplayId> mId;
+};
+
+std::shared_ptr<compositionengine::Display> createDisplay(
+        const compositionengine::CompositionEngine&, compositionengine::DisplayCreationArgs&&);
+} // namespace impl
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h
new file mode 100644
index 0000000..49c2d2c
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h
@@ -0,0 +1,95 @@
+/*
+ * 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 <cstdint>
+#include <unordered_map>
+#include <vector>
+
+#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/DisplayColorProfileCreationArgs.h>
+#include <ui/HdrCapabilities.h>
+
+namespace android::compositionengine {
+
+class CompositionEngine;
+class Display;
+
+namespace impl {
+
+class DisplayColorProfile : public compositionengine::DisplayColorProfile {
+public:
+    DisplayColorProfile(DisplayColorProfileCreationArgs&&);
+    ~DisplayColorProfile() override;
+
+    bool isValid() const override;
+
+    bool hasRenderIntent(ui::RenderIntent intent) const override;
+    bool hasLegacyHdrSupport(ui::Dataspace dataspace) const override;
+    void getBestColorMode(ui::Dataspace dataspace, ui::RenderIntent intent,
+                          ui::Dataspace* outDataspace, ui::ColorMode* outMode,
+                          ui::RenderIntent* outIntent) const override;
+
+    bool hasWideColorGamut() const override;
+    int32_t getSupportedPerFrameMetadata() const override;
+
+    // Whether h/w composer has native support for specific HDR type.
+    bool hasHDR10PlusSupport() const override;
+    bool hasHDR10Support() const override;
+    bool hasHLGSupport() const override;
+    bool hasDolbyVisionSupport() const override;
+
+    const HdrCapabilities& getHdrCapabilities() const override;
+
+    void dump(std::string&) const override;
+
+private:
+    void populateColorModes(const DisplayColorProfileCreationArgs::HwcColorModes& hwcColorModes);
+    void addColorMode(const DisplayColorProfileCreationArgs::HwcColorModes& hwcColorModes,
+                      const ui::ColorMode mode, const ui::RenderIntent intent);
+
+    // Mappings from desired Dataspace/RenderIntent to the supported
+    // Dataspace/ColorMode/RenderIntent.
+    using ColorModeKey = uint64_t;
+    struct ColorModeValue {
+        ui::Dataspace dataspace;
+        ui::ColorMode colorMode;
+        ui::RenderIntent renderIntent;
+    };
+
+    static ColorModeKey getColorModeKey(ui::Dataspace dataspace, ui::RenderIntent intent) {
+        return (static_cast<uint64_t>(dataspace) << 32) | static_cast<uint32_t>(intent);
+    }
+
+    // Need to know if display is wide-color capable or not.
+    // Initialized by SurfaceFlinger when the DisplayDevice is created.
+    // Fed to RenderEngine during composition.
+    bool mHasWideColorGamut{false};
+    int32_t mSupportedPerFrameMetadata{0};
+    bool mHasHdr10Plus{false};
+    bool mHasHdr10{false};
+    bool mHasHLG{false};
+    bool mHasDolbyVision{false};
+    HdrCapabilities mHdrCapabilities;
+    std::unordered_map<ColorModeKey, ColorModeValue> mColorModes;
+};
+
+std::unique_ptr<compositionengine::DisplayColorProfile> createDisplayColorProfile(
+        DisplayColorProfileCreationArgs&&);
+
+} // namespace impl
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h
new file mode 100644
index 0000000..782c8d7
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h
@@ -0,0 +1,62 @@
+/*
+ * 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 <cstdint>
+#include <string>
+#include <type_traits>
+
+#include <math/mat4.h>
+#include <ui/FloatRect.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+#include <ui/Transform.h>
+
+namespace android::compositionengine::impl {
+
+void dumpVal(std::string& out, const char* name, bool);
+void dumpVal(std::string& out, const char* name, const void*);
+void dumpVal(std::string& out, const char* name, int);
+void dumpVal(std::string& out, const char* name, float);
+void dumpVal(std::string& out, const char* name, uint32_t);
+void dumpHex(std::string& out, const char* name, uint64_t);
+void dumpVal(std::string& out, const char* name, const char* value);
+void dumpVal(std::string& out, const char* name, const std::string& value);
+
+// For enums with named values
+void dumpVal(std::string& out, const char* name, const char*, int);
+void dumpVal(std::string& out, const char* name, const std::string&, int);
+
+template <typename EnumType>
+void dumpVal(std::string& out, const char* name, const char* valueName, EnumType value) {
+    dumpVal(out, name, valueName, static_cast<std::underlying_type_t<EnumType>>(value));
+}
+
+template <typename EnumType>
+void dumpVal(std::string& out, const char* name, const std::string& valueName, EnumType value) {
+    dumpVal(out, name, valueName, static_cast<std::underlying_type_t<EnumType>>(value));
+}
+
+void dumpVal(std::string& out, const char* name, const FloatRect& rect);
+void dumpVal(std::string& out, const char* name, const Rect& rect);
+void dumpVal(std::string& out, const char* name, const Region& region);
+void dumpVal(std::string& out, const char* name, const ui::Transform&);
+void dumpVal(std::string& out, const char* name, const ui::Size&);
+
+void dumpVal(std::string& out, const char* name, const mat4& tr);
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
new file mode 100644
index 0000000..180c40b
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -0,0 +1,85 @@
+/*
+ * 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 <memory>
+
+#include <compositionengine/Output.h>
+#include <compositionengine/impl/OutputCompositionState.h>
+
+namespace android::compositionengine {
+
+class CompositionEngine;
+
+namespace impl {
+
+class Output : public virtual compositionengine::Output {
+public:
+    Output(const CompositionEngine&);
+    virtual ~Output();
+
+    bool isValid() const override;
+
+    void setCompositionEnabled(bool) override;
+    void setProjection(const ui::Transform&, int32_t orientation, const Rect& frame,
+                       const Rect& viewport, const Rect& scissor, bool needsFiltering) override;
+    void setBounds(const ui::Size&) override;
+    void setLayerStackFilter(uint32_t layerStackId, bool isInternal) override;
+
+    void setColorTransform(const mat4&) override;
+    void setColorMode(ui::ColorMode, ui::Dataspace, ui::RenderIntent) override;
+
+    void dump(std::string&) const override;
+
+    const std::string& getName() const override;
+    void setName(const std::string&) override;
+
+    compositionengine::DisplayColorProfile* getDisplayColorProfile() const override;
+    void setDisplayColorProfile(std::unique_ptr<compositionengine::DisplayColorProfile>) override;
+
+    compositionengine::RenderSurface* getRenderSurface() const override;
+    void setRenderSurface(std::unique_ptr<compositionengine::RenderSurface>) override;
+
+    const OutputCompositionState& getState() const override;
+    OutputCompositionState& editState() override;
+
+    Region getPhysicalSpaceDirtyRegion(bool repaintEverything) const override;
+    bool belongsInOutput(uint32_t, bool) const override;
+
+    // Testing
+    void setDisplayColorProfileForTest(std::unique_ptr<compositionengine::DisplayColorProfile>);
+    void setRenderSurfaceForTest(std::unique_ptr<compositionengine::RenderSurface>);
+
+protected:
+    const CompositionEngine& getCompositionEngine() const;
+    void dumpBase(std::string&) const;
+
+private:
+    void dirtyEntireOutput();
+
+    const CompositionEngine& mCompositionEngine;
+
+    std::string mName;
+
+    OutputCompositionState mState;
+
+    std::unique_ptr<compositionengine::DisplayColorProfile> mDisplayColorProfile;
+    std::unique_ptr<compositionengine::RenderSurface> mRenderSurface;
+};
+
+} // namespace impl
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
new file mode 100644
index 0000000..024ed45
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -0,0 +1,95 @@
+/*
+ * 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 <cstdint>
+
+#include <ui/GraphicTypes.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+#include <ui/Transform.h>
+
+namespace android {
+
+namespace compositionengine::impl {
+
+struct OutputCompositionState {
+    // If false, composition will not per performed for this display
+    bool isEnabled{false};
+
+    // If false, this output is not considered secure
+    bool isSecure{false};
+
+    // If true, this output displays layers that are internal-only
+    bool layerStackInternal{false};
+
+    // The layer stack to display on this display
+    uint32_t layerStackId{~0u};
+
+    // The physical space screen bounds
+    Rect bounds;
+
+    // The logical to physical transformation to use
+    ui::Transform transform;
+
+    // The physical orientation of the display, expressed as ui::Transform
+    // orientation flags.
+    uint32_t orientation{0};
+
+    // The logical space user visible bounds
+    Rect frame;
+
+    // The logical space user viewport rectangle
+    Rect viewport;
+
+    // The physical space scissor rectangle
+    Rect scissor;
+
+    // If true, RenderEngine filtering should be enabled
+    bool needsFiltering{false};
+
+    // The logical coordinates for the dirty region for the display.
+    // dirtyRegion is semi-persistent state. Dirty rectangles are added to it
+    // by the FE until composition happens, at which point it is cleared.
+    Region dirtyRegion;
+
+    // The logical coordinates for the undefined region for the display.
+    // The undefined region is internal to the composition engine. It is
+    // updated every time the geometry changes.
+    Region undefinedRegion;
+
+    // True if the last composition frame had visible layers
+    bool lastCompositionHadVisibleLayers{false};
+
+    // The color transform to apply
+    android_color_transform_t colorTransform{HAL_COLOR_TRANSFORM_IDENTITY};
+
+    // Current active color mode
+    ui::ColorMode colorMode{ui::ColorMode::NATIVE};
+
+    // Current active render intent
+    ui::RenderIntent renderIntent{ui::RenderIntent::COLORIMETRIC};
+
+    // Current active dstaspace
+    ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
+
+    // Debugging
+    void dump(std::string& result) const;
+};
+
+} // namespace compositionengine::impl
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
new file mode 100644
index 0000000..0489310
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
@@ -0,0 +1,94 @@
+/*
+ * 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 <memory>
+
+#include <android-base/unique_fd.h>
+#include <compositionengine/RenderSurface.h>
+#include <utils/StrongPointer.h>
+
+struct ANativeWindow;
+
+namespace android {
+
+namespace compositionengine {
+
+class CompositionEngine;
+class Display;
+class DisplaySurface;
+
+struct RenderSurfaceCreationArgs;
+
+namespace impl {
+
+class RenderSurface : public compositionengine::RenderSurface {
+public:
+    RenderSurface(const CompositionEngine&, compositionengine::Display&,
+                  compositionengine::RenderSurfaceCreationArgs&&);
+    ~RenderSurface() override;
+
+    bool isValid() const override;
+    void initialize() override;
+    const ui::Size& getSize() const override;
+
+    const sp<Fence>& getClientTargetAcquireFence() const override;
+    void setBufferDataspace(ui::Dataspace) override;
+    void setDisplaySize(const ui::Size&) override;
+    void setProtected(bool useProtected) override;
+    status_t beginFrame(bool mustRecompose) override;
+    status_t prepareFrame(std::vector<CompositionInfo>& compositionData) override;
+    sp<GraphicBuffer> dequeueBuffer() override;
+    void queueBuffer() override;
+    void onPresentDisplayCompleted() override;
+    void finishBuffer() override;
+    void setViewportAndProjection() override;
+    void flip() override;
+
+    // Debugging
+    void dump(std::string& result) const override;
+    std::uint32_t getPageFlipCount() const override;
+
+    // Testing
+    void setPageFlipCountForTest(std::uint32_t);
+    void setSizeForTest(const ui::Size&);
+    sp<GraphicBuffer>& mutableGraphicBufferForTest();
+    base::unique_fd& mutableBufferReadyForTest();
+
+private:
+    const compositionengine::CompositionEngine& mCompositionEngine;
+    const compositionengine::Display& mDisplay;
+
+    // ANativeWindow being rendered into
+    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};
+};
+
+std::unique_ptr<compositionengine::RenderSurface> createRenderSurface(
+        const compositionengine::CompositionEngine&, compositionengine::Display&,
+        compositionengine::RenderSurfaceCreationArgs&&);
+
+} // namespace impl
+} // namespace compositionengine
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
index 9ba213e..1967666 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <compositionengine/CompositionEngine.h>
+#include <compositionengine/DisplayCreationArgs.h>
 #include <gmock/gmock.h>
 #include <renderengine/RenderEngine.h>
 
@@ -29,6 +30,8 @@
     CompositionEngine();
     ~CompositionEngine() override;
 
+    MOCK_METHOD1(createDisplay, std::shared_ptr<Display>(DisplayCreationArgs&&));
+
     MOCK_CONST_METHOD0(getHwComposer, HWComposer&());
     MOCK_METHOD1(setHwComposer, void(std::unique_ptr<HWComposer>));
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
new file mode 100644
index 0000000..d763aa6
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
@@ -0,0 +1,45 @@
+/*
+ * 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 <compositionengine/Display.h>
+#include <compositionengine/DisplayColorProfileCreationArgs.h>
+#include <compositionengine/RenderSurfaceCreationArgs.h>
+#include <compositionengine/mock/Output.h>
+#include <gmock/gmock.h>
+#include <system/window.h>
+
+#include "DisplayHardware/DisplayIdentification.h"
+
+namespace android::compositionengine::mock {
+
+class Display : public compositionengine::mock::Output, public compositionengine::Display {
+public:
+    Display();
+    virtual ~Display();
+
+    MOCK_CONST_METHOD0(getId, const std::optional<DisplayId>&());
+    MOCK_CONST_METHOD0(isSecure, bool());
+    MOCK_CONST_METHOD0(isVirtual, bool());
+
+    MOCK_METHOD0(disconnect, void());
+
+    MOCK_METHOD1(createDisplayColorProfile, void(DisplayColorProfileCreationArgs&&));
+    MOCK_METHOD1(createRenderSurface, void(RenderSurfaceCreationArgs&&));
+};
+
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h
new file mode 100644
index 0000000..8056c9d
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.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 <compositionengine/DisplayColorProfile.h>
+#include <gmock/gmock.h>
+#include <ui/HdrCapabilities.h>
+
+namespace android::compositionengine::mock {
+
+class DisplayColorProfile : public compositionengine::DisplayColorProfile {
+public:
+    DisplayColorProfile();
+    ~DisplayColorProfile() override;
+
+    MOCK_CONST_METHOD0(isValid, bool());
+
+    MOCK_CONST_METHOD1(hasRenderIntent, bool(ui::RenderIntent));
+    MOCK_CONST_METHOD1(hasLegacyHdrSupport, bool(ui::Dataspace));
+    MOCK_CONST_METHOD5(getBestColorMode,
+                       void(ui::Dataspace, ui::RenderIntent, ui::Dataspace*, ui::ColorMode*,
+                            ui::RenderIntent*));
+    MOCK_CONST_METHOD0(hasWideColorGamut, bool());
+    MOCK_CONST_METHOD0(getSupportedPerFrameMetadata, int32_t());
+    MOCK_CONST_METHOD0(hasHDR10PlusSupport, bool());
+    MOCK_CONST_METHOD0(hasHDR10Support, bool());
+    MOCK_CONST_METHOD0(hasHLGSupport, bool());
+    MOCK_CONST_METHOD0(hasDolbyVisionSupport, bool());
+
+    MOCK_CONST_METHOD0(getHdrCapabilities, const HdrCapabilities&());
+
+    MOCK_CONST_METHOD1(dump, void(std::string&));
+};
+
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplaySurface.h
similarity index 81%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.h
rename to services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplaySurface.h
index d6c9aa4..31b5f95 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplaySurface.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -16,16 +16,13 @@
 
 #pragma once
 
+#include <compositionengine/DisplaySurface.h>
 #include <gmock/gmock.h>
-
 #include <utils/String8.h>
 
-#include "DisplayHardware/DisplaySurface.h"
+namespace android::compositionengine::mock {
 
-namespace android {
-namespace mock {
-
-class DisplaySurface : public android::DisplaySurface {
+class DisplaySurface : public compositionengine::DisplaySurface {
 public:
     DisplaySurface();
     ~DisplaySurface() override;
@@ -39,5 +36,4 @@
     MOCK_CONST_METHOD0(getClientTargetAcquireFence, const sp<Fence>&());
 };
 
-} // namespace mock
-} // namespace android
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
new file mode 100644
index 0000000..445f0bb
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -0,0 +1,60 @@
+/*
+ * 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 <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/Output.h>
+#include <compositionengine/RenderSurface.h>
+#include <compositionengine/impl/OutputCompositionState.h>
+#include <gmock/gmock.h>
+
+namespace android::compositionengine::mock {
+
+class Output : public virtual compositionengine::Output {
+public:
+    Output();
+    virtual ~Output();
+
+    MOCK_CONST_METHOD0(isValid, bool());
+
+    MOCK_METHOD1(setCompositionEnabled, void(bool));
+    MOCK_METHOD6(setProjection,
+                 void(const ui::Transform&, int32_t, const Rect&, const Rect&, const Rect&, bool));
+    MOCK_METHOD1(setBounds, void(const ui::Size&));
+    MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool));
+
+    MOCK_METHOD1(setColorTransform, void(const mat4&));
+    MOCK_METHOD3(setColorMode, void(ui::ColorMode, ui::Dataspace, ui::RenderIntent));
+
+    MOCK_CONST_METHOD1(dump, void(std::string&));
+    MOCK_CONST_METHOD0(getName, const std::string&());
+    MOCK_METHOD1(setName, void(const std::string&));
+
+    MOCK_CONST_METHOD0(getDisplayColorProfile, DisplayColorProfile*());
+    MOCK_METHOD1(setDisplayColorProfile, void(std::unique_ptr<DisplayColorProfile>));
+
+    MOCK_CONST_METHOD0(getRenderSurface, RenderSurface*());
+    MOCK_METHOD1(setRenderSurface, void(std::unique_ptr<RenderSurface>));
+
+    MOCK_CONST_METHOD0(getState, const OutputCompositionState&());
+    MOCK_METHOD0(editState, OutputCompositionState&());
+
+    MOCK_CONST_METHOD1(getPhysicalSpaceDirtyRegion, Region(bool));
+    MOCK_CONST_METHOD2(belongsInOutput, bool(uint32_t, bool));
+};
+
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
new file mode 100644
index 0000000..2269e57
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
@@ -0,0 +1,51 @@
+/*
+ * 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 <compositionengine/RenderSurface.h>
+#include <gmock/gmock.h>
+#include <ui/GraphicBuffer.h>
+
+#include "LayerBE.h"
+
+namespace android::compositionengine::mock {
+
+class RenderSurface : public compositionengine::RenderSurface {
+public:
+    RenderSurface();
+    ~RenderSurface() override;
+
+    MOCK_CONST_METHOD0(isValid, bool());
+    MOCK_METHOD0(initialize, void());
+    MOCK_CONST_METHOD0(getSize, const ui::Size&());
+    MOCK_CONST_METHOD0(getClientTargetAcquireFence, const sp<Fence>&());
+    MOCK_METHOD1(setDisplaySize, void(const ui::Size&));
+    MOCK_METHOD1(setProtected, void(bool));
+    MOCK_METHOD1(setBufferDataspace, void(ui::Dataspace));
+    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_METHOD0(onPresentDisplayCompleted, void());
+    MOCK_METHOD0(finishBuffer, void());
+    MOCK_METHOD0(setViewportAndProjection, void());
+    MOCK_METHOD0(flip, void());
+    MOCK_CONST_METHOD1(dump, void(std::string& result));
+    MOCK_CONST_METHOD0(getPageFlipCount, std::uint32_t());
+};
+
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/CompositionEngine/mock/Display.cpp
similarity index 60%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/CompositionEngine/mock/Display.cpp
index e6ac6bf..01cf112 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/mock/Display.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -14,14 +14,13 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include <compositionengine/mock/Display.h>
 
-namespace android {
-namespace mock {
+namespace android::compositionengine::mock {
 
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+// The Google Mock documentation recommends explicit non-header instantiations
+// for better compile time performance.
+Display::Display() = default;
+Display::~Display() = default;
 
-} // namespace mock
-} // namespace android
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/CompositionEngine/mock/DisplayColorProfile.cpp
similarity index 66%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/CompositionEngine/mock/DisplayColorProfile.cpp
index e6ac6bf..581d1f7 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/mock/DisplayColorProfile.cpp
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
- *
+ * 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
@@ -14,14 +14,12 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include <compositionengine/mock/DisplayColorProfile.h>
 
-namespace android {
-namespace mock {
+namespace android::compositionengine::mock {
 
 // Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+DisplayColorProfile::DisplayColorProfile() = default;
+DisplayColorProfile::~DisplayColorProfile() = default;
 
-} // namespace mock
-} // namespace android
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/CompositionEngine/mock/DisplaySurface.cpp
similarity index 67%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/CompositionEngine/mock/DisplaySurface.cpp
index e6ac6bf..bbbd7c1 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/mock/DisplaySurface.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -14,14 +14,13 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include "compositionengine/mock/DisplaySurface.h"
 
-namespace android {
-namespace mock {
+namespace android::compositionengine::mock {
 
-// Explicit default instantiation is recommended.
+// The Google Mock documentation recommends explicit non-header instantiations
+// for better compile time performance.
 DisplaySurface::DisplaySurface() = default;
 DisplaySurface::~DisplaySurface() = default;
 
-} // namespace mock
-} // namespace android
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/CompositionEngine/mock/Output.cpp
similarity index 60%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/CompositionEngine/mock/Output.cpp
index e6ac6bf..44df4c3 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/mock/Output.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -14,14 +14,13 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include <compositionengine/mock/Output.h>
 
-namespace android {
-namespace mock {
+namespace android::compositionengine::mock {
 
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+// The Google Mock documentation recommends explicit non-header instantiations
+// for better compile time performance.
+Output::Output() = default;
+Output::~Output() = default;
 
-} // namespace mock
-} // namespace android
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/CompositionEngine/mock/RenderSurface.cpp
similarity index 68%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/CompositionEngine/mock/RenderSurface.cpp
index e6ac6bf..fe718d6 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/mock/RenderSurface.cpp
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
- *
+ * 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
@@ -14,14 +14,12 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include <compositionengine/mock/RenderSurface.h>
 
-namespace android {
-namespace mock {
+namespace android::compositionengine::mock {
 
 // Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+RenderSurface::RenderSurface() = default;
+RenderSurface::~RenderSurface() = default;
 
-} // namespace mock
-} // namespace android
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index fbf71b5..127d729 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <compositionengine/impl/CompositionEngine.h>
+#include <compositionengine/impl/Display.h>
 #include <renderengine/RenderEngine.h>
 
 #include "DisplayHardware/HWComposer.h"
@@ -32,6 +33,11 @@
 CompositionEngine::CompositionEngine() = default;
 CompositionEngine::~CompositionEngine() = default;
 
+std::shared_ptr<compositionengine::Display> CompositionEngine::createDisplay(
+        DisplayCreationArgs&& args) {
+    return compositionengine::impl::createDisplay(*this, std::move(args));
+}
+
 HWComposer& CompositionEngine::getHwComposer() const {
     return *mHwComposer.get();
 }
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
new file mode 100644
index 0000000..f9d70e3
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -0,0 +1,122 @@
+/*
+ * 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 <android-base/stringprintf.h>
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/DisplayCreationArgs.h>
+#include <compositionengine/DisplaySurface.h>
+#include <compositionengine/impl/Display.h>
+#include <compositionengine/impl/DisplayColorProfile.h>
+#include <compositionengine/impl/DumpHelpers.h>
+#include <compositionengine/impl/RenderSurface.h>
+
+#include "DisplayHardware/HWComposer.h"
+
+namespace android::compositionengine::impl {
+
+std::shared_ptr<compositionengine::Display> createDisplay(
+        const compositionengine::CompositionEngine& compositionEngine,
+        compositionengine::DisplayCreationArgs&& args) {
+    return std::make_shared<Display>(compositionEngine, std::move(args));
+}
+
+Display::Display(const CompositionEngine& compositionEngine, DisplayCreationArgs&& args)
+      : compositionengine::impl::Output(compositionEngine),
+        mIsVirtual(args.isVirtual),
+        mId(args.displayId) {
+    editState().isSecure = args.isSecure;
+}
+
+Display::~Display() = default;
+
+const std::optional<DisplayId>& Display::getId() const {
+    return mId;
+}
+
+bool Display::isSecure() const {
+    return getState().isSecure;
+}
+
+bool Display::isVirtual() const {
+    return mIsVirtual;
+}
+
+void Display::disconnect() {
+    if (!mId) {
+        return;
+    }
+
+    auto& hwc = getCompositionEngine().getHwComposer();
+    hwc.disconnectDisplay(*mId);
+    mId.reset();
+}
+
+void Display::setColorTransform(const mat4& transform) {
+    Output::setColorTransform(transform);
+
+    auto& hwc = getCompositionEngine().getHwComposer();
+    status_t result = hwc.setColorTransform(*mId, transform);
+    ALOGE_IF(result != NO_ERROR, "Failed to set color transform on display \"%s\": %d",
+             mId ? to_string(*mId).c_str() : "", result);
+}
+
+void Display::setColorMode(ui::ColorMode mode, ui::Dataspace dataspace,
+                           ui::RenderIntent renderIntent) {
+    if (mode == getState().colorMode && dataspace == getState().dataspace &&
+        renderIntent == getState().renderIntent) {
+        return;
+    }
+
+    if (mIsVirtual) {
+        ALOGW("%s: Invalid operation on virtual display", __FUNCTION__);
+        return;
+    }
+
+    Output::setColorMode(mode, dataspace, renderIntent);
+
+    auto& hwc = getCompositionEngine().getHwComposer();
+    hwc.setActiveColorMode(*mId, mode, renderIntent);
+}
+
+void Display::dump(std::string& out) const {
+    using android::base::StringAppendF;
+
+    StringAppendF(&out, "   Composition Display State: [\"%s\"]", getName().c_str());
+
+    out.append("\n   ");
+
+    dumpVal(out, "isVirtual", mIsVirtual);
+    if (mId) {
+        dumpVal(out, "hwcId", to_string(*mId));
+    } else {
+        StringAppendF(&out, "no hwcId, ");
+    }
+
+    out.append("\n");
+
+    Output::dumpBase(out);
+}
+
+void Display::createDisplayColorProfile(DisplayColorProfileCreationArgs&& args) {
+    setDisplayColorProfile(compositionengine::impl::createDisplayColorProfile(std::move(args)));
+}
+
+void Display::createRenderSurface(RenderSurfaceCreationArgs&& args) {
+    setRenderSurface(compositionengine::impl::createRenderSurface(getCompositionEngine(), *this,
+                                                                  std::move(args)));
+}
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
new file mode 100644
index 0000000..6e6f3c0
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
@@ -0,0 +1,395 @@
+/*
+ * 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 <array>
+#include <unordered_set>
+
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/Display.h>
+#include <compositionengine/DisplayColorProfileCreationArgs.h>
+#include <compositionengine/RenderSurface.h>
+#include <compositionengine/impl/DisplayColorProfile.h>
+#include <compositionengine/impl/DumpHelpers.h>
+#include <log/log.h>
+#include <ui/DebugUtils.h>
+
+#include "DisplayHardware/HWComposer.h"
+
+namespace android::compositionengine {
+
+DisplayColorProfile::~DisplayColorProfile() = default;
+
+namespace impl {
+namespace {
+
+using ui::ColorMode;
+using ui::Dataspace;
+using ui::RenderIntent;
+
+// ordered list of known SDR color modes
+const std::array<ColorMode, 3> sSdrColorModes = {
+        ColorMode::DISPLAY_BT2020,
+        ColorMode::DISPLAY_P3,
+        ColorMode::SRGB,
+};
+
+// ordered list of known HDR color modes
+const std::array<ColorMode, 2> sHdrColorModes = {
+        ColorMode::BT2100_PQ,
+        ColorMode::BT2100_HLG,
+};
+
+// ordered list of known SDR render intents
+const std::array<RenderIntent, 2> sSdrRenderIntents = {
+        RenderIntent::ENHANCE,
+        RenderIntent::COLORIMETRIC,
+};
+
+// ordered list of known HDR render intents
+const std::array<RenderIntent, 2> sHdrRenderIntents = {
+        RenderIntent::TONE_MAP_ENHANCE,
+        RenderIntent::TONE_MAP_COLORIMETRIC,
+};
+
+// map known color mode to dataspace
+Dataspace colorModeToDataspace(ColorMode mode) {
+    switch (mode) {
+        case ColorMode::SRGB:
+            return Dataspace::V0_SRGB;
+        case ColorMode::DISPLAY_P3:
+            return Dataspace::DISPLAY_P3;
+        case ColorMode::DISPLAY_BT2020:
+            return Dataspace::DISPLAY_BT2020;
+        case ColorMode::BT2100_HLG:
+            return Dataspace::BT2020_HLG;
+        case ColorMode::BT2100_PQ:
+            return Dataspace::BT2020_PQ;
+        default:
+            return Dataspace::UNKNOWN;
+    }
+}
+
+// Return a list of candidate color modes.
+std::vector<ColorMode> getColorModeCandidates(ColorMode mode) {
+    std::vector<ColorMode> candidates;
+
+    // add mode itself
+    candidates.push_back(mode);
+
+    // check if mode is HDR
+    bool isHdr = false;
+    for (auto hdrMode : sHdrColorModes) {
+        if (hdrMode == mode) {
+            isHdr = true;
+            break;
+        }
+    }
+
+    // add other HDR candidates when mode is HDR
+    if (isHdr) {
+        for (auto hdrMode : sHdrColorModes) {
+            if (hdrMode != mode) {
+                candidates.push_back(hdrMode);
+            }
+        }
+    }
+
+    // add other SDR candidates
+    for (auto sdrMode : sSdrColorModes) {
+        if (sdrMode != mode) {
+            candidates.push_back(sdrMode);
+        }
+    }
+
+    return candidates;
+}
+
+// Return a list of candidate render intents.
+std::vector<RenderIntent> getRenderIntentCandidates(RenderIntent intent) {
+    std::vector<RenderIntent> candidates;
+
+    // add intent itself
+    candidates.push_back(intent);
+
+    // check if intent is HDR
+    bool isHdr = false;
+    for (auto hdrIntent : sHdrRenderIntents) {
+        if (hdrIntent == intent) {
+            isHdr = true;
+            break;
+        }
+    }
+
+    if (isHdr) {
+        // add other HDR candidates when intent is HDR
+        for (auto hdrIntent : sHdrRenderIntents) {
+            if (hdrIntent != intent) {
+                candidates.push_back(hdrIntent);
+            }
+        }
+    } else {
+        // add other SDR candidates when intent is SDR
+        for (auto sdrIntent : sSdrRenderIntents) {
+            if (sdrIntent != intent) {
+                candidates.push_back(sdrIntent);
+            }
+        }
+    }
+
+    return candidates;
+}
+
+// Return the best color mode supported by HWC.
+ColorMode getHwcColorMode(
+        const std::unordered_map<ColorMode, std::vector<RenderIntent>>& hwcColorModes,
+        ColorMode mode) {
+    std::vector<ColorMode> candidates = getColorModeCandidates(mode);
+    for (auto candidate : candidates) {
+        auto iter = hwcColorModes.find(candidate);
+        if (iter != hwcColorModes.end()) {
+            return candidate;
+        }
+    }
+
+    return ColorMode::NATIVE;
+}
+
+// Return the best render intent supported by HWC.
+RenderIntent getHwcRenderIntent(const std::vector<RenderIntent>& hwcIntents, RenderIntent intent) {
+    std::vector<RenderIntent> candidates = getRenderIntentCandidates(intent);
+    for (auto candidate : candidates) {
+        for (auto hwcIntent : hwcIntents) {
+            if (candidate == hwcIntent) {
+                return candidate;
+            }
+        }
+    }
+
+    return RenderIntent::COLORIMETRIC;
+}
+
+} // anonymous namespace
+
+std::unique_ptr<compositionengine::DisplayColorProfile> createDisplayColorProfile(
+        DisplayColorProfileCreationArgs&& args) {
+    return std::make_unique<DisplayColorProfile>(std::move(args));
+}
+
+DisplayColorProfile::DisplayColorProfile(DisplayColorProfileCreationArgs&& args)
+      : mHasWideColorGamut(args.hasWideColorGamut),
+        mSupportedPerFrameMetadata(args.supportedPerFrameMetadata) {
+    populateColorModes(args.hwcColorModes);
+
+    std::vector<ui::Hdr> types = args.hdrCapabilities.getSupportedHdrTypes();
+    for (ui::Hdr hdrType : types) {
+        switch (hdrType) {
+            case ui::Hdr::HDR10_PLUS:
+                mHasHdr10Plus = true;
+                break;
+            case ui::Hdr::HDR10:
+                mHasHdr10 = true;
+                break;
+            case ui::Hdr::HLG:
+                mHasHLG = true;
+                break;
+            case ui::Hdr::DOLBY_VISION:
+                mHasDolbyVision = true;
+                break;
+            default:
+                ALOGE("UNKNOWN HDR capability: %d", static_cast<int32_t>(hdrType));
+        }
+    }
+
+    float minLuminance = args.hdrCapabilities.getDesiredMinLuminance();
+    float maxLuminance = args.hdrCapabilities.getDesiredMaxLuminance();
+    float maxAverageLuminance = args.hdrCapabilities.getDesiredMaxAverageLuminance();
+
+    minLuminance = minLuminance <= 0.0 ? sDefaultMinLumiance : minLuminance;
+    maxLuminance = maxLuminance <= 0.0 ? sDefaultMaxLumiance : maxLuminance;
+    maxAverageLuminance = maxAverageLuminance <= 0.0 ? sDefaultMaxLumiance : maxAverageLuminance;
+    if (args.hasWideColorGamut) {
+        // insert HDR10/HLG as we will force client composition for HDR10/HLG
+        // layers
+        if (!hasHDR10Support()) {
+            types.push_back(ui::Hdr::HDR10);
+        }
+
+        if (!hasHLGSupport()) {
+            types.push_back(ui::Hdr::HLG);
+        }
+    }
+
+    mHdrCapabilities = HdrCapabilities(types, maxLuminance, maxAverageLuminance, minLuminance);
+}
+
+DisplayColorProfile::~DisplayColorProfile() = default;
+
+bool DisplayColorProfile::isValid() const {
+    return true;
+}
+
+bool DisplayColorProfile::hasWideColorGamut() const {
+    return mHasWideColorGamut;
+}
+
+int32_t DisplayColorProfile::getSupportedPerFrameMetadata() const {
+    return mSupportedPerFrameMetadata;
+}
+
+bool DisplayColorProfile::hasHDR10PlusSupport() const {
+    return mHasHdr10Plus;
+}
+
+bool DisplayColorProfile::hasHDR10Support() const {
+    return mHasHdr10;
+}
+
+bool DisplayColorProfile::hasHLGSupport() const {
+    return mHasHLG;
+}
+
+bool DisplayColorProfile::hasDolbyVisionSupport() const {
+    return mHasDolbyVision;
+}
+
+const HdrCapabilities& DisplayColorProfile::getHdrCapabilities() const {
+    return mHdrCapabilities;
+}
+
+void DisplayColorProfile::populateColorModes(
+        const DisplayColorProfileCreationArgs::HwcColorModes& hwcColorModes) {
+    if (!hasWideColorGamut()) {
+        return;
+    }
+
+    // collect all known SDR render intents
+    std::unordered_set<RenderIntent> sdrRenderIntents(sSdrRenderIntents.begin(),
+                                                      sSdrRenderIntents.end());
+    auto iter = hwcColorModes.find(ColorMode::SRGB);
+    if (iter != hwcColorModes.end()) {
+        for (auto intent : iter->second) {
+            sdrRenderIntents.insert(intent);
+        }
+    }
+
+    // add all known SDR combinations
+    for (auto intent : sdrRenderIntents) {
+        for (auto mode : sSdrColorModes) {
+            addColorMode(hwcColorModes, mode, intent);
+        }
+    }
+
+    // collect all known HDR render intents
+    std::unordered_set<RenderIntent> hdrRenderIntents(sHdrRenderIntents.begin(),
+                                                      sHdrRenderIntents.end());
+    iter = hwcColorModes.find(ColorMode::BT2100_PQ);
+    if (iter != hwcColorModes.end()) {
+        for (auto intent : iter->second) {
+            hdrRenderIntents.insert(intent);
+        }
+    }
+
+    // add all known HDR combinations
+    for (auto intent : sHdrRenderIntents) {
+        for (auto mode : sHdrColorModes) {
+            addColorMode(hwcColorModes, mode, intent);
+        }
+    }
+}
+
+// Map dataspace/intent to the best matched dataspace/colorMode/renderIntent
+// supported by HWC.
+void DisplayColorProfile::addColorMode(
+        const DisplayColorProfileCreationArgs::HwcColorModes& hwcColorModes, const ColorMode mode,
+        const RenderIntent intent) {
+    // find the best color mode
+    const ColorMode hwcColorMode = getHwcColorMode(hwcColorModes, mode);
+
+    // find the best render intent
+    auto iter = hwcColorModes.find(hwcColorMode);
+    const auto& hwcIntents =
+            iter != hwcColorModes.end() ? iter->second : std::vector<RenderIntent>();
+    const RenderIntent hwcIntent = getHwcRenderIntent(hwcIntents, intent);
+
+    const Dataspace dataspace = colorModeToDataspace(mode);
+    const Dataspace hwcDataspace = colorModeToDataspace(hwcColorMode);
+
+    ALOGV("DisplayColorProfile: map (%s, %s) to (%s, %s, %s)",
+          dataspaceDetails(static_cast<android_dataspace_t>(dataspace)).c_str(),
+          decodeRenderIntent(intent).c_str(),
+          dataspaceDetails(static_cast<android_dataspace_t>(hwcDataspace)).c_str(),
+          decodeColorMode(hwcColorMode).c_str(), decodeRenderIntent(hwcIntent).c_str());
+
+    mColorModes[getColorModeKey(dataspace, intent)] = {hwcDataspace, hwcColorMode, hwcIntent};
+}
+
+bool DisplayColorProfile::hasRenderIntent(RenderIntent intent) const {
+    // assume a render intent is supported when SRGB supports it; we should
+    // get rid of that assumption.
+    auto iter = mColorModes.find(getColorModeKey(Dataspace::V0_SRGB, intent));
+    return iter != mColorModes.end() && iter->second.renderIntent == intent;
+}
+
+bool DisplayColorProfile::hasLegacyHdrSupport(Dataspace dataspace) const {
+    if ((dataspace == Dataspace::BT2020_PQ && hasHDR10Support()) ||
+        (dataspace == Dataspace::BT2020_HLG && hasHLGSupport())) {
+        auto iter =
+                mColorModes.find(getColorModeKey(dataspace, RenderIntent::TONE_MAP_COLORIMETRIC));
+        return iter == mColorModes.end() || iter->second.dataspace != dataspace;
+    }
+
+    return false;
+}
+
+void DisplayColorProfile::getBestColorMode(Dataspace dataspace, RenderIntent intent,
+                                           Dataspace* outDataspace, ColorMode* outMode,
+                                           RenderIntent* outIntent) const {
+    auto iter = mColorModes.find(getColorModeKey(dataspace, intent));
+    if (iter != mColorModes.end()) {
+        *outDataspace = iter->second.dataspace;
+        *outMode = iter->second.colorMode;
+        *outIntent = iter->second.renderIntent;
+    } else {
+        // this is unexpected on a WCG display
+        if (hasWideColorGamut()) {
+            ALOGE("map unknown (%s)/(%s) to default color mode",
+                  dataspaceDetails(static_cast<android_dataspace_t>(dataspace)).c_str(),
+                  decodeRenderIntent(intent).c_str());
+        }
+
+        *outDataspace = Dataspace::UNKNOWN;
+        *outMode = ColorMode::NATIVE;
+        *outIntent = RenderIntent::COLORIMETRIC;
+    }
+}
+
+void DisplayColorProfile::dump(std::string& out) const {
+    out.append("   Composition Display Color State:");
+
+    out.append("\n   HWC Support: ");
+
+    dumpVal(out, "wideColorGamut", hasWideColorGamut());
+    dumpVal(out, "hdr10plus", hasHDR10PlusSupport());
+    dumpVal(out, "hdr10", hasHDR10Support());
+    dumpVal(out, "hlg", hasHLGSupport());
+    dumpVal(out, "dv", hasDolbyVisionSupport());
+    dumpVal(out, "metadata", getSupportedPerFrameMetadata());
+
+    out.append("\n");
+}
+
+} // namespace impl
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/CompositionEngine/src/DisplaySurface.cpp
similarity index 68%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/CompositionEngine/src/DisplaySurface.cpp
index e6ac6bf..db6d4f2 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/DisplaySurface.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -14,14 +14,10 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include <compositionengine/DisplaySurface.h>
 
-namespace android {
-namespace mock {
+namespace android::compositionengine {
 
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
 DisplaySurface::~DisplaySurface() = default;
 
-} // namespace mock
-} // namespace android
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
new file mode 100644
index 0000000..c497013
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
@@ -0,0 +1,102 @@
+/*
+ * 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 <cinttypes>
+
+#include <android-base/stringprintf.h>
+#include <compositionengine/impl/DumpHelpers.h>
+
+namespace android::compositionengine::impl {
+
+using android::base::StringAppendF;
+
+void dumpVal(std::string& out, const char* name, bool value) {
+    StringAppendF(&out, "%s=%c ", name, value ? 'T' : 'F');
+}
+
+void dumpVal(std::string& out, const char* name, const void* value) {
+    StringAppendF(&out, "%s=%p ", name, value);
+}
+
+void dumpVal(std::string& out, const char* name, int value) {
+    StringAppendF(&out, "%s=%d ", name, value);
+}
+
+void dumpVal(std::string& out, const char* name, float value) {
+    StringAppendF(&out, "%s=%f ", name, value);
+}
+
+void dumpVal(std::string& out, const char* name, uint32_t value) {
+    StringAppendF(&out, "%s=%u ", name, value);
+}
+
+void dumpHex(std::string& out, const char* name, uint64_t value) {
+    StringAppendF(&out, "%s=0x08%" PRIx64 " ", name, value);
+}
+
+void dumpVal(std::string& out, const char* name, const char* value) {
+    StringAppendF(&out, "%s=%s ", name, value);
+}
+
+void dumpVal(std::string& out, const char* name, const std::string& value) {
+    dumpVal(out, name, value.c_str());
+}
+
+void dumpVal(std::string& out, const char* name, const char* valueName, int value) {
+    StringAppendF(&out, "%s=%s (%d)", name, valueName, value);
+}
+
+void dumpVal(std::string& out, const char* name, const std::string& valueName, int value) {
+    dumpVal(out, name, valueName.c_str(), value);
+}
+
+void dumpVal(std::string& out, const char* name, const FloatRect& rect) {
+    StringAppendF(&out, "%s=[%f %f %f %f] ", name, rect.left, rect.top, rect.right, rect.bottom);
+}
+
+void dumpVal(std::string& out, const char* name, const Rect& rect) {
+    StringAppendF(&out, "%s=[%d %d %d %d] ", name, rect.left, rect.top, rect.right, rect.bottom);
+}
+
+void dumpVal(std::string& out, const char* name, const Region& region) {
+    region.dump(out, name, 0);
+}
+
+void dumpVal(std::string& out, const char* name, const ui::Transform& transform) {
+    transform.dump(out, name);
+}
+
+void dumpVal(std::string& out, const char* name, const ui::Size& size) {
+    StringAppendF(&out, "%s=[%d %d] ", name, size.width, size.height);
+}
+
+void dumpVal(std::string& out, const char* name, const mat4& tr) {
+    StringAppendF(&out,
+                  "%s=["
+                  /* clang-format off */
+                  "[%0.3f,%0.3f,%0.3f,%0.3f]"
+                  "[%0.3f,%0.3f,%0.3f,%0.3f]"
+                  "[%0.3f,%0.3f,%0.3f,%0.3f]"
+                  "[%0.3f,%0.3f,%0.3f,%0.3f]]",
+                  name,
+                  tr[0][0], tr[1][0], tr[2][0], tr[3][0],
+                  tr[0][1], tr[1][1], tr[2][1], tr[3][1],
+                  tr[0][2], tr[1][2], tr[2][2], tr[3][2],
+                  tr[0][3], tr[1][3], tr[2][3], tr[3][3]
+                  ); /* clang-format on */
+}
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
new file mode 100644
index 0000000..7fb67bd
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -0,0 +1,188 @@
+/*
+ * 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 <android-base/stringprintf.h>
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/RenderSurface.h>
+#include <compositionengine/impl/Output.h>
+#include <ui/DebugUtils.h>
+
+namespace android::compositionengine::impl {
+
+Output::Output(const CompositionEngine& compositionEngine)
+      : mCompositionEngine(compositionEngine) {}
+
+Output::~Output() = default;
+
+const CompositionEngine& Output::getCompositionEngine() const {
+    return mCompositionEngine;
+}
+
+bool Output::isValid() const {
+    return mDisplayColorProfile && mDisplayColorProfile->isValid() && mRenderSurface &&
+            mRenderSurface->isValid();
+}
+
+const std::string& Output::getName() const {
+    return mName;
+}
+
+void Output::setName(const std::string& name) {
+    mName = name;
+}
+
+void Output::setCompositionEnabled(bool enabled) {
+    if (mState.isEnabled == enabled) {
+        return;
+    }
+
+    mState.isEnabled = enabled;
+    dirtyEntireOutput();
+}
+
+void Output::setProjection(const ui::Transform& transform, int32_t orientation, const Rect& frame,
+                           const Rect& viewport, const Rect& scissor, bool needsFiltering) {
+    mState.transform = transform;
+    mState.orientation = orientation;
+    mState.scissor = scissor;
+    mState.frame = frame;
+    mState.viewport = viewport;
+    mState.needsFiltering = needsFiltering;
+
+    dirtyEntireOutput();
+}
+
+// TODO(lpique): Rename setSize() once more is moved.
+void Output::setBounds(const ui::Size& size) {
+    mRenderSurface->setDisplaySize(size);
+    // TODO(lpique): Rename mState.size once more is moved.
+    mState.bounds = Rect(mRenderSurface->getSize());
+
+    dirtyEntireOutput();
+}
+
+void Output::setLayerStackFilter(uint32_t layerStackId, bool isInternal) {
+    mState.layerStackId = layerStackId;
+    mState.layerStackInternal = isInternal;
+
+    dirtyEntireOutput();
+}
+
+void Output::setColorTransform(const mat4& transform) {
+    const bool isIdentity = (transform == mat4());
+
+    mState.colorTransform =
+            isIdentity ? HAL_COLOR_TRANSFORM_IDENTITY : HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX;
+}
+
+void Output::setColorMode(ui::ColorMode mode, ui::Dataspace dataspace,
+                          ui::RenderIntent renderIntent) {
+    mState.colorMode = mode;
+    mState.dataspace = dataspace;
+    mState.renderIntent = renderIntent;
+
+    mRenderSurface->setBufferDataspace(dataspace);
+
+    ALOGV("Set active color mode: %s (%d), active render intent: %s (%d)",
+          decodeColorMode(mode).c_str(), mode, decodeRenderIntent(renderIntent).c_str(),
+          renderIntent);
+}
+
+void Output::dump(std::string& out) const {
+    using android::base::StringAppendF;
+
+    StringAppendF(&out, "   Composition Output State: [\"%s\"]", mName.c_str());
+
+    out.append("\n   ");
+
+    dumpBase(out);
+}
+
+void Output::dumpBase(std::string& out) const {
+    mState.dump(out);
+
+    if (mDisplayColorProfile) {
+        mDisplayColorProfile->dump(out);
+    } else {
+        out.append("    No display color profile!\n");
+    }
+
+    if (mRenderSurface) {
+        mRenderSurface->dump(out);
+    } else {
+        out.append("    No render surface!\n");
+    }
+}
+
+compositionengine::DisplayColorProfile* Output::getDisplayColorProfile() const {
+    return mDisplayColorProfile.get();
+}
+
+void Output::setDisplayColorProfile(std::unique_ptr<compositionengine::DisplayColorProfile> mode) {
+    mDisplayColorProfile = std::move(mode);
+}
+
+void Output::setDisplayColorProfileForTest(
+        std::unique_ptr<compositionengine::DisplayColorProfile> mode) {
+    mDisplayColorProfile = std::move(mode);
+}
+
+compositionengine::RenderSurface* Output::getRenderSurface() const {
+    return mRenderSurface.get();
+}
+
+void Output::setRenderSurface(std::unique_ptr<compositionengine::RenderSurface> surface) {
+    mRenderSurface = std::move(surface);
+    mState.bounds = Rect(mRenderSurface->getSize());
+
+    dirtyEntireOutput();
+}
+
+void Output::setRenderSurfaceForTest(std::unique_ptr<compositionengine::RenderSurface> surface) {
+    mRenderSurface = std::move(surface);
+}
+
+const OutputCompositionState& Output::getState() const {
+    return mState;
+}
+
+OutputCompositionState& Output::editState() {
+    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);
+    }
+    return dirty;
+}
+
+bool Output::belongsInOutput(uint32_t layerStackId, bool internalOnly) const {
+    // The layerStackId's must match, and also the layer must not be internal
+    // only when not on an internal output.
+    return (layerStackId == mState.layerStackId) && (!internalOnly || mState.layerStackInternal);
+}
+
+void Output::dirtyEntireOutput() {
+    mState.dirtyRegion.set(mState.bounds);
+}
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
new file mode 100644
index 0000000..78807ff
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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 <compositionengine/impl/DumpHelpers.h>
+#include <compositionengine/impl/OutputCompositionState.h>
+
+namespace android::compositionengine::impl {
+
+void OutputCompositionState::dump(std::string& out) const {
+    dumpVal(out, "isEnabled", isEnabled);
+    dumpVal(out, "isSecure", isSecure);
+
+    dumpVal(out, "layerStack", layerStackId);
+    dumpVal(out, "layerStackInternal", layerStackInternal);
+
+    out.append("\n   ");
+
+    dumpVal(out, "transform", transform);
+
+    out.append("\n   ");
+
+    dumpVal(out, "frame", frame);
+    dumpVal(out, "viewport", viewport);
+    dumpVal(out, "scissor", scissor);
+    dumpVal(out, "needsFiltering", needsFiltering);
+
+    out.append("\n");
+
+    dumpVal(out, "colorMode", toString(colorMode), colorMode);
+    dumpVal(out, "renderIntent", toString(renderIntent), renderIntent);
+    dumpVal(out, "dataspace", toString(dataspace), dataspace);
+    dumpVal(out, "colorTransform", colorTransform);
+
+    out.append("\n");
+}
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
new file mode 100644
index 0000000..d546fc8
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -0,0 +1,271 @@
+/*
+ * 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 <android-base/stringprintf.h>
+#include <android/native_window.h>
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/Display.h>
+#include <compositionengine/DisplaySurface.h>
+#include <compositionengine/RenderSurfaceCreationArgs.h>
+#include <compositionengine/impl/DumpHelpers.h>
+#include <compositionengine/impl/RenderSurface.h>
+#include <log/log.h>
+#include <renderengine/RenderEngine.h>
+#include <sync/sync.h>
+#include <system/window.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/Rect.h>
+
+#include "DisplayHardware/HWComposer.h"
+
+namespace android::compositionengine {
+
+RenderSurface::~RenderSurface() = default;
+
+namespace impl {
+
+std::unique_ptr<compositionengine::RenderSurface> createRenderSurface(
+        const compositionengine::CompositionEngine& compositionEngine,
+        compositionengine::Display& display, compositionengine::RenderSurfaceCreationArgs&& args) {
+    return std::make_unique<RenderSurface>(compositionEngine, display, std::move(args));
+}
+
+RenderSurface::RenderSurface(const CompositionEngine& compositionEngine, Display& display,
+                             RenderSurfaceCreationArgs&& args)
+      : mCompositionEngine(compositionEngine),
+        mDisplay(display),
+        mNativeWindow(args.nativeWindow),
+        mDisplaySurface(args.displaySurface),
+        mSize(args.displayWidth, args.displayHeight) {}
+
+RenderSurface::~RenderSurface() = default;
+
+bool RenderSurface::isValid() const {
+    return mSize.isValid();
+}
+
+void RenderSurface::initialize() {
+    ANativeWindow* const window = mNativeWindow.get();
+
+    int status = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
+    ALOGE_IF(status != NO_ERROR, "Unable to connect BQ producer: %d", status);
+    status = native_window_set_buffers_format(window, HAL_PIXEL_FORMAT_RGBA_8888);
+    ALOGE_IF(status != NO_ERROR, "Unable to set BQ format to RGBA888: %d", status);
+    status = native_window_set_usage(window, GRALLOC_USAGE_HW_RENDER);
+    ALOGE_IF(status != NO_ERROR, "Unable to set BQ usage bits for GPU rendering: %d", status);
+}
+
+const ui::Size& RenderSurface::getSize() const {
+    return mSize;
+}
+
+const sp<Fence>& RenderSurface::getClientTargetAcquireFence() const {
+    return mDisplaySurface->getClientTargetAcquireFence();
+}
+
+void RenderSurface::setDisplaySize(const ui::Size& size) {
+    mDisplaySurface->resizeBuffers(size.width, size.height);
+    mSize = size;
+}
+
+void RenderSurface::setBufferDataspace(ui::Dataspace dataspace) {
+    native_window_set_buffers_data_space(mNativeWindow.get(),
+                                         static_cast<android_dataspace>(dataspace));
+}
+
+void RenderSurface::setProtected(bool useProtected) {
+    uint64_t usageFlags = GRALLOC_USAGE_HW_RENDER;
+    if (useProtected) {
+        usageFlags |= GRALLOC_USAGE_PROTECTED;
+    }
+    const int status = native_window_set_usage(mNativeWindow.get(), usageFlags);
+    ALOGE_IF(status != NO_ERROR, "Unable to set BQ usage bits for protected content: %d", status);
+}
+
+status_t RenderSurface::beginFrame(bool mustRecompose) {
+    return mDisplaySurface->beginFrame(mustRecompose);
+}
+
+status_t RenderSurface::prepareFrame(std::vector<CompositionInfo>& compositionData) {
+    auto& hwc = mCompositionEngine.getHwComposer();
+    const auto id = mDisplay.getId();
+    if (id) {
+        status_t error = hwc.prepare(*id, compositionData);
+        if (error != NO_ERROR) {
+            return error;
+        }
+    }
+
+    DisplaySurface::CompositionType compositionType;
+    const bool hasClient = hwc.hasClientComposition(id);
+    const bool hasDevice = hwc.hasDeviceComposition(id);
+    if (hasClient && hasDevice) {
+        compositionType = DisplaySurface::COMPOSITION_MIXED;
+    } else if (hasClient) {
+        compositionType = DisplaySurface::COMPOSITION_GLES;
+    } else if (hasDevice) {
+        compositionType = DisplaySurface::COMPOSITION_HWC;
+    } else {
+        // Nothing to do -- when turning the screen off we get a frame like
+        // this. Call it a HWC frame since we won't be doing any GLES work but
+        // will do a prepare/set cycle.
+        compositionType = DisplaySurface::COMPOSITION_HWC;
+    }
+    return mDisplaySurface->prepareFrame(compositionType);
+}
+
+sp<GraphicBuffer> RenderSurface::dequeueBuffer() {
+    int fd = -1;
+    ANativeWindowBuffer* buffer = nullptr;
+
+    status_t result = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buffer, &fd);
+
+    if (result != NO_ERROR) {
+        ALOGE("ANativeWindow::dequeueBuffer failed for display [%s] with error: %d",
+              mDisplay.getName().c_str(), result);
+        // Return fast here as we can't do much more - any rendering we do
+        // now will just be wrong.
+        return mGraphicBuffer;
+    }
+
+    ALOGW_IF(mGraphicBuffer != nullptr, "Clobbering a non-null pointer to a buffer [%p].",
+             mGraphicBuffer->getNativeBuffer()->handle);
+    mGraphicBuffer = GraphicBuffer::from(buffer);
+
+    // Block until the buffer is ready
+    // TODO(alecmouri): it's perhaps more appropriate to block renderengine so
+    // that the gl driver can block instead.
+    if (fd >= 0) {
+        sync_wait(fd, -1);
+        close(fd);
+    }
+
+    return mGraphicBuffer;
+}
+
+void RenderSurface::queueBuffer() {
+    auto& hwc = mCompositionEngine.getHwComposer();
+    const auto id = mDisplay.getId();
+
+    if (hwc.hasClientComposition(id) || hwc.hasFlipClientTargetRequest(id)) {
+        // hasFlipClientTargetRequest could return true even if we haven't
+        // dequeued a buffer before. Try dequeueing one if we don't have a
+        // buffer ready.
+        if (mGraphicBuffer == nullptr) {
+            ALOGI("Attempting to queue a client composited buffer without one "
+                  "previously dequeued for display [%s]. Attempting to dequeue "
+                  "a scratch buffer now",
+                  mDisplay.getName().c_str());
+            // We shouldn't deadlock here, since mGraphicBuffer == nullptr only
+            // after a successful call to queueBuffer, or if dequeueBuffer has
+            // never been called.
+            dequeueBuffer();
+        }
+
+        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));
+            if (result != NO_ERROR) {
+                ALOGE("Error when queueing buffer for display [%s]: %d", mDisplay.getName().c_str(),
+                      result);
+                // We risk blocking on dequeueBuffer if the primary display failed
+                // to queue up its buffer, so crash here.
+                if (!mDisplay.isVirtual()) {
+                    LOG_ALWAYS_FATAL("ANativeWindow::queueBuffer failed with error: %d", result);
+                } else {
+                    mNativeWindow->cancelBuffer(mNativeWindow.get(),
+                                                mGraphicBuffer->getNativeBuffer(),
+                                                dup(mBufferReady));
+                }
+            }
+
+            mBufferReady.reset();
+            mGraphicBuffer = nullptr;
+        }
+    }
+
+    status_t result = mDisplaySurface->advanceFrame();
+    if (result != NO_ERROR) {
+        ALOGE("[%s] failed pushing new frame to HWC: %d", mDisplay.getName().c_str(), result);
+    }
+}
+
+void RenderSurface::onPresentDisplayCompleted() {
+    mDisplaySurface->onFrameCommitted();
+}
+
+void RenderSurface::setViewportAndProjection() {
+    auto& renderEngine = mCompositionEngine.getRenderEngine();
+    Rect sourceCrop = Rect(mSize);
+    renderEngine.setViewportAndProjection(mSize.width, mSize.height, sourceCrop,
+                                          ui::Transform::ROT_0);
+}
+
+void RenderSurface::finishBuffer() {
+    auto& renderEngine = mCompositionEngine.getRenderEngine();
+    mBufferReady = renderEngine.flush();
+    if (mBufferReady.get() < 0) {
+        renderEngine.finish();
+    }
+}
+
+void RenderSurface::flip() {
+    mPageFlipCount++;
+}
+
+void RenderSurface::dump(std::string& out) const {
+    using android::base::StringAppendF;
+
+    out.append("   Composition RenderSurface State:");
+
+    out.append("\n   ");
+
+    dumpVal(out, "size", mSize);
+    StringAppendF(&out, "ANativeWindow=%p (format %d) ", mNativeWindow.get(),
+                  ANativeWindow_getFormat(mNativeWindow.get()));
+    dumpVal(out, "flips", mPageFlipCount);
+    out.append("\n");
+
+    String8 surfaceDump;
+    mDisplaySurface->dumpAsString(surfaceDump);
+    out.append(surfaceDump);
+}
+
+std::uint32_t RenderSurface::getPageFlipCount() const {
+    return mPageFlipCount;
+}
+
+void RenderSurface::setPageFlipCountForTest(std::uint32_t count) {
+    mPageFlipCount = count;
+}
+
+void RenderSurface::setSizeForTest(const ui::Size& size) {
+    mSize = size;
+}
+
+sp<GraphicBuffer>& RenderSurface::mutableGraphicBufferForTest() {
+    return mGraphicBuffer;
+}
+
+base::unique_fd& RenderSurface::mutableBufferReadyForTest() {
+    return mBufferReady;
+}
+
+} // namespace impl
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
new file mode 100644
index 0000000..d20fdda
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
@@ -0,0 +1,644 @@
+/*
+ * 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 <compositionengine/DisplayColorProfileCreationArgs.h>
+#include <compositionengine/impl/DisplayColorProfile.h>
+#include <compositionengine/mock/CompositionEngine.h>
+#include <gtest/gtest.h>
+
+namespace android::hardware::graphics::common::V1_1 {
+
+// Note: These operator overloads need to be defined in the same namespace as
+// the values they print.
+
+std::ostream& operator<<(std::ostream& os, const RenderIntent& value) {
+    return os << toString(value) << " (" << static_cast<std::underlying_type_t<Dataspace>>(value)
+              << ")";
+}
+
+} // namespace android::hardware::graphics::common::V1_1
+
+namespace android::hardware::graphics::common::V1_2 {
+
+// Note: These operator overloads need to be defined in the same namespace as
+// the values they print.
+
+std::ostream& operator<<(std::ostream& os, const Dataspace& value) {
+    return os << toString(value) << " (" << static_cast<std::underlying_type_t<Dataspace>>(value)
+              << ")";
+}
+
+std::ostream& operator<<(std::ostream& os, const ColorMode& value) {
+    return os << toString(value) << " (" << static_cast<std::underlying_type_t<Dataspace>>(value)
+              << ")";
+}
+
+} // namespace android::hardware::graphics::common::V1_2
+
+namespace android::compositionengine {
+namespace {
+
+using testing::_;
+using testing::Contains;
+using testing::IsEmpty;
+using testing::Ref;
+using testing::Return;
+using testing::ReturnRef;
+using testing::SizeIs;
+using testing::StrictMock;
+
+using ui::ColorMode;
+using ui::Dataspace;
+using ui::Hdr;
+using ui::RenderIntent;
+
+// This allows us to simulate a vendor-specified intent being used.
+constexpr RenderIntent VendorRenderIntent = static_cast<RenderIntent>(0x100);
+
+class DisplayColorProfileTest : public testing::Test {
+public:
+    ~DisplayColorProfileTest() override = default;
+
+    StrictMock<mock::CompositionEngine> mCompositionEngine;
+};
+
+class ProfileFactory {
+public:
+    impl::DisplayColorProfile build() const {
+        return impl::DisplayColorProfile{DisplayColorProfileCreationArgs{
+                mHasWideColorGamut,
+                HdrCapabilities(mSupportedHdrTypes, mMaxLuminance, mMaxAverageLuminance,
+                                mMinLuminance),
+                mSupportedPerFrameMetadata,
+                mSupportedColorModes,
+        }};
+    }
+
+    ProfileFactory& setHasWideColorGamut(bool value) {
+        mHasWideColorGamut = value;
+        return *this;
+    }
+
+    ProfileFactory& setPerFrameMetadata(int32_t value) {
+        mSupportedPerFrameMetadata = value;
+        return *this;
+    }
+
+    ProfileFactory& addHdrType(Hdr value) {
+        mSupportedHdrTypes.emplace_back(value);
+        return *this;
+    }
+
+    ProfileFactory& addHdrTypes(std::initializer_list<Hdr> values) {
+        for (auto value : values) {
+            mSupportedHdrTypes.emplace_back(value);
+        }
+        return *this;
+    }
+
+    ProfileFactory& setMaxLuminance(float value) {
+        mMaxLuminance = value;
+        return *this;
+    }
+
+    ProfileFactory& setMaxAverageLuminance(float value) {
+        mMaxAverageLuminance = value;
+        return *this;
+    }
+
+    ProfileFactory& setMinLuminance(float value) {
+        mMinLuminance = value;
+        return *this;
+    }
+
+    ProfileFactory& addColorModeRenderIntent(ColorMode colorMode, RenderIntent renderIntent) {
+        mSupportedColorModes[colorMode].emplace_back(renderIntent);
+        return *this;
+    }
+
+    ProfileFactory& addColorModeRenderIntents(ColorMode colorMode,
+                                              std::initializer_list<RenderIntent> renderIntents) {
+        auto& profileedRenderIntents = mSupportedColorModes[colorMode];
+        for (auto renderIntent : renderIntents) {
+            profileedRenderIntents.emplace_back(renderIntent);
+        }
+        return *this;
+    }
+
+    static impl::DisplayColorProfile createProfileWithNoColorModeSupport() {
+        return ProfileFactory().build();
+    }
+
+    static impl::DisplayColorProfile createProfileWithBT2020ColorModeSupport() {
+        return ProfileFactory()
+                .setHasWideColorGamut(true)
+                .addHdrType(Hdr::HDR10)
+                .addColorModeRenderIntent(ColorMode::DISPLAY_BT2020, VendorRenderIntent)
+                .build();
+    }
+
+    static impl::DisplayColorProfile createProfileWithSRGBColorModeSupport() {
+        return ProfileFactory()
+                .setHasWideColorGamut(true)
+                .addHdrType(Hdr::HDR10)
+                .addColorModeRenderIntent(ColorMode::SRGB, VendorRenderIntent)
+                .build();
+    }
+
+    static impl::DisplayColorProfile createProfileWithBT2100PQSupport() {
+        return ProfileFactory()
+                .setHasWideColorGamut(true)
+                .addHdrType(Hdr::HLG)
+                .addColorModeRenderIntent(ColorMode::BT2100_PQ, VendorRenderIntent)
+                .build();
+    }
+
+private:
+    bool mHasWideColorGamut = false;
+    std::vector<Hdr> mSupportedHdrTypes;
+    float mMaxLuminance = -1.f;
+    float mMaxAverageLuminance = -1.f;
+    float mMinLuminance = -1.f;
+    int32_t mSupportedPerFrameMetadata = 0;
+    std::unordered_map<ColorMode, std::vector<RenderIntent>> mSupportedColorModes;
+};
+
+/* ------------------------------------------------------------------------
+ * RenderSurface Construction
+ */
+
+TEST_F(DisplayColorProfileTest, ctorSetsHasWideColorGamutFromInputArgs) {
+    {
+        auto profile = ProfileFactory().setHasWideColorGamut(false).build();
+
+        EXPECT_FALSE(profile.hasWideColorGamut());
+    }
+
+    {
+        auto profile = ProfileFactory().setHasWideColorGamut(true).build();
+
+        EXPECT_TRUE(profile.hasWideColorGamut());
+    }
+}
+
+TEST_F(DisplayColorProfileTest, ctorSetsSupportedPerFrameMetadataFromInputArgs) {
+    {
+        auto profile = ProfileFactory().setPerFrameMetadata(0).build();
+
+        EXPECT_EQ(0, profile.getSupportedPerFrameMetadata());
+    }
+
+    {
+        impl::DisplayColorProfile profile = ProfileFactory().setPerFrameMetadata(123).build();
+
+        EXPECT_EQ(123, profile.getSupportedPerFrameMetadata());
+    }
+}
+
+TEST_F(DisplayColorProfileTest, ctorDetectsSupportedHdrTypesFromInputArgs) {
+    {
+        // The constructor will set the internal state to not indicate any
+        // profile for HDR modes if none are profileed.
+        auto profile = ProfileFactory().build();
+
+        EXPECT_FALSE(profile.hasHDR10PlusSupport());
+        EXPECT_FALSE(profile.hasHDR10Support());
+        EXPECT_FALSE(profile.hasHLGSupport());
+        EXPECT_FALSE(profile.hasDolbyVisionSupport());
+    }
+
+    {
+        // The constructor will set the intenral state to indicate HDR10Plus
+        // profile if the input arguments indicate it is profileed.
+        auto profile = ProfileFactory().addHdrType(Hdr::HDR10_PLUS).build();
+
+        EXPECT_TRUE(profile.hasHDR10PlusSupport());
+        EXPECT_FALSE(profile.hasHDR10Support());
+        EXPECT_FALSE(profile.hasHLGSupport());
+        EXPECT_FALSE(profile.hasDolbyVisionSupport());
+    }
+
+    {
+        // The constructor will set the intenral state to indicate HDR10 profile
+        // if the input arguments indicate it is profileed.
+        auto profile = ProfileFactory().addHdrType(Hdr::HDR10).build();
+
+        EXPECT_FALSE(profile.hasHDR10PlusSupport());
+        EXPECT_TRUE(profile.hasHDR10Support());
+        EXPECT_FALSE(profile.hasHLGSupport());
+        EXPECT_FALSE(profile.hasDolbyVisionSupport());
+    }
+
+    {
+        // The constructor will set the intenral state to indicate HLG profile
+        // if the input arguments indicate it is profileed.
+        auto profile = ProfileFactory().addHdrType(Hdr::HLG).build();
+
+        EXPECT_FALSE(profile.hasHDR10PlusSupport());
+        EXPECT_FALSE(profile.hasHDR10Support());
+        EXPECT_TRUE(profile.hasHLGSupport());
+        EXPECT_FALSE(profile.hasDolbyVisionSupport());
+    }
+
+    {
+        // The constructor will set the intenral state to indicate Dolbyvision profile
+        // if the input arguments indicate it is profileed.
+        auto profile = ProfileFactory().addHdrType(Hdr::DOLBY_VISION).build();
+
+        EXPECT_FALSE(profile.hasHDR10Support());
+        EXPECT_FALSE(profile.hasHLGSupport());
+        EXPECT_TRUE(profile.hasDolbyVisionSupport());
+    }
+}
+
+TEST_F(DisplayColorProfileTest, ctorUsesOrDefaultsLuminanceValuesFromInputArgs) {
+    {
+        // The constructor will use a default value for each luminance setting
+        // that is negative.
+        auto profile = ProfileFactory()
+                               .setMaxLuminance(-1.f)
+                               .setMaxAverageLuminance(-1.f)
+                               .setMinLuminance(-1.f)
+                               .build();
+
+        EXPECT_EQ(DisplayColorProfile::sDefaultMaxLumiance,
+                  profile.getHdrCapabilities().getDesiredMaxLuminance());
+        EXPECT_EQ(DisplayColorProfile::sDefaultMaxLumiance,
+                  profile.getHdrCapabilities().getDesiredMaxAverageLuminance());
+        EXPECT_EQ(DisplayColorProfile::sDefaultMinLumiance,
+                  profile.getHdrCapabilities().getDesiredMinLuminance());
+    }
+
+    {
+        // The constructor will otherwise take and use a positive value for each
+        // of the luminance settings.
+        auto profile = ProfileFactory()
+                               .setMaxLuminance(1001.f)
+                               .setMaxAverageLuminance(1002.f)
+                               .setMinLuminance(1003.f)
+                               .build();
+
+        EXPECT_EQ(1001.f, profile.getHdrCapabilities().getDesiredMaxLuminance());
+        EXPECT_EQ(1002.f, profile.getHdrCapabilities().getDesiredMaxAverageLuminance());
+        EXPECT_EQ(1003.f, profile.getHdrCapabilities().getDesiredMinLuminance());
+    }
+}
+
+TEST_F(DisplayColorProfileTest, ctorSignalsHdrSupportForAnyWideColorGamutDevice) {
+    {
+        // If the output does not profile wide color gamut, then no HDR modes
+        // will be profileed in the generated HDR capabilities.
+        auto profile = ProfileFactory().setHasWideColorGamut(false).build();
+
+        EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), IsEmpty());
+    }
+
+    {
+        // If the HWC does not show profile for certain HDR modes, then the
+        // generated HDR capabilities will indicate profile anyway.
+        auto profile = ProfileFactory().setHasWideColorGamut(true).build();
+
+        EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), SizeIs(2));
+        EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), Contains(Hdr::HDR10));
+        EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), Contains(Hdr::HLG));
+    }
+
+    {
+        // If the HWC profiles the HDR modes, then the generated capabilities
+        // still has one entry for each HDR type.
+        auto profile = ProfileFactory()
+                               .setHasWideColorGamut(true)
+                               .addHdrTypes({Hdr::HLG, Hdr::HDR10})
+                               .build();
+
+        EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), SizeIs(2));
+        EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), Contains(Hdr::HDR10));
+        EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), Contains(Hdr::HLG));
+    }
+}
+
+/* ------------------------------------------------------------------------
+ * DisplayColorProfile::hasRenderIntent
+ */
+
+TEST_F(DisplayColorProfileTest, hasRenderIntentReturnsExpectedValueWhenOutputHasNoSupport) {
+    auto profile = ProfileFactory::createProfileWithNoColorModeSupport();
+
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::COLORIMETRIC));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::ENHANCE));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_COLORIMETRIC));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_ENHANCE));
+    EXPECT_FALSE(profile.hasRenderIntent(VendorRenderIntent));
+}
+
+TEST_F(DisplayColorProfileTest, hasRenderIntentReturnsExpectedValueWhenOutputHasBT2020upport) {
+    auto profile = ProfileFactory::createProfileWithBT2020ColorModeSupport();
+
+    EXPECT_TRUE(profile.hasRenderIntent(RenderIntent::COLORIMETRIC));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::ENHANCE));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_COLORIMETRIC));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_ENHANCE));
+    EXPECT_FALSE(profile.hasRenderIntent(VendorRenderIntent));
+}
+
+TEST_F(DisplayColorProfileTest, hasRenderIntentReturnsExpectedValueWhenOutputHasSRGBSupport) {
+    auto profile = ProfileFactory::createProfileWithSRGBColorModeSupport();
+
+    EXPECT_TRUE(profile.hasRenderIntent(RenderIntent::COLORIMETRIC));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::ENHANCE));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_COLORIMETRIC));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_ENHANCE));
+    EXPECT_TRUE(profile.hasRenderIntent(VendorRenderIntent));
+}
+
+TEST_F(DisplayColorProfileTest, hasRenderIntentReturnsExpectedValueWhenOutputHasBTG2100PQSupport) {
+    auto profile = ProfileFactory::createProfileWithBT2100PQSupport();
+
+    EXPECT_TRUE(profile.hasRenderIntent(RenderIntent::COLORIMETRIC));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::ENHANCE));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_COLORIMETRIC));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_ENHANCE));
+    EXPECT_FALSE(profile.hasRenderIntent(VendorRenderIntent));
+}
+
+/* ------------------------------------------------------------------------
+ * DisplayColorProfile::hasLegacyHdrSupport
+ */
+
+TEST_F(DisplayColorProfileTest, hasLegacyHdrSupport) {
+    {
+        auto profile = ProfileFactory::createProfileWithNoColorModeSupport();
+
+        EXPECT_FALSE(profile.hasLegacyHdrSupport(Dataspace::BT2020_PQ));
+        EXPECT_FALSE(profile.hasLegacyHdrSupport(Dataspace::BT2020_HLG));
+    }
+
+    {
+        auto profile = ProfileFactory::createProfileWithBT2020ColorModeSupport();
+
+        EXPECT_TRUE(profile.hasLegacyHdrSupport(Dataspace::BT2020_PQ));
+        EXPECT_FALSE(profile.hasLegacyHdrSupport(Dataspace::BT2020_HLG));
+    }
+
+    {
+        auto profile = ProfileFactory::createProfileWithSRGBColorModeSupport();
+
+        EXPECT_TRUE(profile.hasLegacyHdrSupport(Dataspace::BT2020_PQ));
+        EXPECT_FALSE(profile.hasLegacyHdrSupport(Dataspace::BT2020_HLG));
+    }
+
+    {
+        auto profile = ProfileFactory::createProfileWithBT2100PQSupport();
+
+        EXPECT_FALSE(profile.hasLegacyHdrSupport(Dataspace::BT2020_PQ));
+        EXPECT_TRUE(profile.hasLegacyHdrSupport(Dataspace::BT2020_HLG));
+    }
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::getBestColorMode()
+ */
+
+void checkGetBestColorMode(
+        DisplayColorProfile& profile,
+        const std::array<std::tuple<Dataspace, ColorMode, RenderIntent>, 25>& expected) {
+    using ArgsType = std::tuple<Dataspace, RenderIntent>;
+
+    // These are the combinations of dataspaces and render intents that could be
+    // passed to RenderSurface::getBestColorMode()
+    const std::array<std::tuple<Dataspace, RenderIntent>, 25> kArgs = {
+            /* clang-format off */
+
+            // Non-HDR combinations
+
+            /*  0 */ ArgsType{Dataspace::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+            /*  1 */ ArgsType{Dataspace::DISPLAY_BT2020, RenderIntent::ENHANCE},
+            /*  2 */ ArgsType{Dataspace::DISPLAY_BT2020, RenderIntent::TONE_MAP_COLORIMETRIC}, // Vendor explicit setting
+            /*  3 */ ArgsType{Dataspace::DISPLAY_BT2020, RenderIntent::TONE_MAP_ENHANCE},      // Vendor explicit setting
+            /*  4 */ ArgsType{Dataspace::DISPLAY_BT2020, VendorRenderIntent},                  // Vendor explicit setting
+
+            /*  5 */ ArgsType{Dataspace::DISPLAY_P3, RenderIntent::COLORIMETRIC},
+            /*  6 */ ArgsType{Dataspace::DISPLAY_P3, RenderIntent::ENHANCE},
+            /*  7 */ ArgsType{Dataspace::DISPLAY_P3, RenderIntent::TONE_MAP_COLORIMETRIC}, // Vendor explicit setting
+            /*  8 */ ArgsType{Dataspace::DISPLAY_P3, RenderIntent::TONE_MAP_ENHANCE},      // Vendor explicit setting
+            /*  9 */ ArgsType{Dataspace::DISPLAY_P3, VendorRenderIntent},                  // Vendor explicit setting
+
+            /* 10 */ ArgsType{Dataspace::V0_SRGB, RenderIntent::COLORIMETRIC},
+            /* 11 */ ArgsType{Dataspace::V0_SRGB, RenderIntent::ENHANCE},
+            /* 12 */ ArgsType{Dataspace::V0_SRGB, RenderIntent::TONE_MAP_COLORIMETRIC}, // Vendor explicit setting
+            /* 13 */ ArgsType{Dataspace::V0_SRGB, RenderIntent::TONE_MAP_ENHANCE},      // Vendor explicit setting
+            /* 14 */ ArgsType{Dataspace::V0_SRGB, VendorRenderIntent},                  // Vendor explicit setting
+
+            // HDR combinations
+
+            /* 15 */ ArgsType{Dataspace::BT2020_PQ, RenderIntent::TONE_MAP_COLORIMETRIC},
+            /* 16 */ ArgsType{Dataspace::BT2020_PQ, RenderIntent::TONE_MAP_ENHANCE},
+            /* 17 */ ArgsType{Dataspace::BT2020_PQ, RenderIntent::COLORIMETRIC},       // Vendor explicit setting
+            /* 18 */ ArgsType{Dataspace::BT2020_PQ, RenderIntent::ENHANCE},            // Vendor explicit setting
+            /* 19 */ ArgsType{Dataspace::BT2020_PQ, VendorRenderIntent},               // Vendor explicit setting
+
+            /* 20 */ ArgsType{Dataspace::BT2020_HLG, RenderIntent::TONE_MAP_COLORIMETRIC},
+            /* 21 */ ArgsType{Dataspace::BT2020_HLG, RenderIntent::TONE_MAP_ENHANCE},
+            /* 22 */ ArgsType{Dataspace::BT2020_HLG, RenderIntent::COLORIMETRIC},       // Vendor explicit setting
+            /* 23 */ ArgsType{Dataspace::BT2020_HLG, RenderIntent::ENHANCE},            // Vendor explicit setting
+            /* 24 */ ArgsType{Dataspace::BT2020_HLG, VendorRenderIntent},               // Vendor explicit setting
+            /* clang-format on */
+    };
+
+    for (size_t i = 0; i < kArgs.size(); i++) {
+        std::tuple<Dataspace, ColorMode, RenderIntent> actual;
+        profile.getBestColorMode(std::get<0>(kArgs[i]), std::get<1>(kArgs[i]), &std::get<0>(actual),
+                                 &std::get<1>(actual), &std::get<2>(actual));
+
+        EXPECT_EQ(expected[i], actual) << " for index " << i;
+    }
+}
+
+TEST_F(DisplayColorProfileTest, getBestColorModeReturnsExpectedModesWhenOutputHasNoSupport) {
+    auto profile = ProfileFactory::createProfileWithNoColorModeSupport();
+
+    // Note: This table of expected values goes with the table of arguments
+    // used in checkGetBestColorMode.
+    using Result = std::tuple<Dataspace, ColorMode, RenderIntent>;
+    std::array<Result, 25> expectedResults = {
+            /* clang-format off */
+            /*  0 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  1 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  2 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  3 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  4 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /*  5 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  6 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  7 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  8 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  9 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* 10 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 11 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 12 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 13 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 14 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* 15 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 16 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 17 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 18 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 19 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* 20 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 21 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 22 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 23 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 24 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* clang-format on */
+    };
+
+    checkGetBestColorMode(profile, expectedResults);
+}
+
+TEST_F(DisplayColorProfileTest, getBestColorModeReturnsExpectedModesWhenOutputHasBT2020Support) {
+    auto profile = ProfileFactory::createProfileWithBT2020ColorModeSupport();
+
+    // Note: This table of expected values goes with the table of arguments
+    // used in checkGetBestColorMode.
+    using Result = std::tuple<Dataspace, ColorMode, RenderIntent>;
+    std::array<Result, 25> expectedResults = {
+            /* clang-format off */
+            /*  0 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+            /*  1 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+            /*  2 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  3 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  4 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /*  5 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+            /*  6 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+            /*  7 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  8 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  9 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* 10 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+            /* 11 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+            /* 12 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 13 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 14 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* 15 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+            /* 16 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+            /* 17 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 18 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 19 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* 20 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+            /* 21 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+            /* 22 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 23 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 24 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* clang-format on */
+    };
+
+    checkGetBestColorMode(profile, expectedResults);
+}
+
+TEST_F(DisplayColorProfileTest, getBestColorModeReturnsExpectedModesWhenOutputHasSRGBSupport) {
+    auto profile = ProfileFactory::createProfileWithSRGBColorModeSupport();
+
+    // Note: This table of expected values goes with the table of arguments
+    // used in checkGetBestColorMode.
+    using Result = std::tuple<Dataspace, ColorMode, RenderIntent>;
+    std::array<Result, 25> expectedResults = {
+            /* clang-format off */
+            /*  0 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /*  1 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /*  2 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  3 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  4 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, VendorRenderIntent},
+
+            /*  5 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /*  6 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /*  7 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  8 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  9 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, VendorRenderIntent},
+
+            /* 10 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /* 11 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /* 12 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 13 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 14 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, VendorRenderIntent},
+
+            /* 15 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /* 16 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /* 17 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 18 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 19 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* 20 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /* 21 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /* 22 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 23 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 24 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* clang-format on */
+    };
+
+    checkGetBestColorMode(profile, expectedResults);
+}
+
+TEST_F(DisplayColorProfileTest, getBestColorModeReturnsExpectedModesWhenOutputHasBT2100PQSupport) {
+    auto profile = ProfileFactory::createProfileWithBT2100PQSupport();
+
+    // Note: This table of expected values goes with the table of arguments
+    // used in checkGetBestColorMode.
+    using Result = std::tuple<Dataspace, ColorMode, RenderIntent>;
+    std::array<Result, 25> expectedResults = {
+            /* clang-format off */
+            /*  0 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  1 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  2 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  3 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  4 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /*  5 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  6 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  7 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  8 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  9 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* 19 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 11 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 12 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 13 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 14 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* 15 */ Result{Dataspace::BT2020_PQ, ColorMode::BT2100_PQ, RenderIntent::COLORIMETRIC},
+            /* 16 */ Result{Dataspace::BT2020_PQ, ColorMode::BT2100_PQ, RenderIntent::COLORIMETRIC},
+            /* 17 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 18 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 19 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* 20 */ Result{Dataspace::BT2020_PQ, ColorMode::BT2100_PQ, RenderIntent::COLORIMETRIC},
+            /* 21 */ Result{Dataspace::BT2020_PQ, ColorMode::BT2100_PQ, RenderIntent::COLORIMETRIC},
+            /* 22 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 23 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 24 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* clang-format on */
+    };
+
+    checkGetBestColorMode(profile, expectedResults);
+}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
new file mode 100644
index 0000000..8cb6936
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -0,0 +1,208 @@
+/*
+ * 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 <cmath>
+
+#include <compositionengine/DisplayColorProfileCreationArgs.h>
+#include <compositionengine/DisplayCreationArgs.h>
+#include <compositionengine/DisplaySurface.h>
+#include <compositionengine/RenderSurfaceCreationArgs.h>
+#include <compositionengine/impl/Display.h>
+#include <compositionengine/mock/CompositionEngine.h>
+#include <compositionengine/mock/RenderSurface.h>
+#include <gtest/gtest.h>
+#include <system/window.h>
+
+#include "MockHWComposer.h"
+
+namespace android::compositionengine {
+namespace {
+
+using testing::Return;
+using testing::ReturnRef;
+using testing::StrictMock;
+
+constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{42};
+
+class DisplayTest : public testing::Test {
+public:
+    ~DisplayTest() override = default;
+
+    StrictMock<android::mock::HWComposer> mHwComposer;
+    StrictMock<mock::CompositionEngine> mCompositionEngine;
+    impl::Display mDisplay{mCompositionEngine,
+                           DisplayCreationArgsBuilder().setDisplayId(DEFAULT_DISPLAY_ID).build()};
+};
+
+/* ------------------------------------------------------------------------
+ * Basic construction
+ */
+
+TEST_F(DisplayTest, canInstantiateDisplay) {
+    {
+        constexpr DisplayId display1 = DisplayId{123u};
+        auto display =
+                impl::createDisplay(mCompositionEngine,
+                                    DisplayCreationArgsBuilder().setDisplayId(display1).build());
+        EXPECT_FALSE(display->isSecure());
+        EXPECT_FALSE(display->isVirtual());
+        EXPECT_EQ(display1, display->getId());
+    }
+
+    {
+        constexpr DisplayId display2 = DisplayId{546u};
+        auto display = impl::createDisplay(mCompositionEngine,
+                                           DisplayCreationArgsBuilder()
+                                                   .setIsSecure(true)
+                                                   .setDisplayId(display2)
+                                                   .build());
+        EXPECT_TRUE(display->isSecure());
+        EXPECT_FALSE(display->isVirtual());
+        EXPECT_EQ(display2, display->getId());
+    }
+
+    {
+        constexpr DisplayId display3 = DisplayId{789u};
+        auto display = impl::createDisplay(mCompositionEngine,
+                                           DisplayCreationArgsBuilder()
+                                                   .setIsVirtual(true)
+                                                   .setDisplayId(display3)
+                                                   .build());
+        EXPECT_FALSE(display->isSecure());
+        EXPECT_TRUE(display->isVirtual());
+        EXPECT_EQ(display3, display->getId());
+    }
+}
+
+/* ------------------------------------------------------------------------
+ * Display::disconnect()
+ */
+
+TEST_F(DisplayTest, disconnectDisconnectsDisplay) {
+    EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+
+    // The first call to disconnect will disconnect the display with the HWC and
+    // set mHwcId to -1.
+    EXPECT_CALL(mHwComposer, disconnectDisplay(DEFAULT_DISPLAY_ID)).Times(1);
+    mDisplay.disconnect();
+    EXPECT_FALSE(mDisplay.getId());
+
+    // Subsequent calls will do nothing,
+    EXPECT_CALL(mHwComposer, disconnectDisplay(DEFAULT_DISPLAY_ID)).Times(0);
+    mDisplay.disconnect();
+    EXPECT_FALSE(mDisplay.getId());
+}
+
+/* ------------------------------------------------------------------------
+ * Display::setColorTransform()
+ */
+
+TEST_F(DisplayTest, setColorTransformSetsTransform) {
+    // Identity matrix sets an identity state value
+    const mat4 identity;
+
+    EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+
+    EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, identity)).Times(1);
+
+    mDisplay.setColorTransform(identity);
+
+    EXPECT_EQ(HAL_COLOR_TRANSFORM_IDENTITY, mDisplay.getState().colorTransform);
+
+    // Non-identity matrix sets a non-identity state value
+    const mat4 nonIdentity = mat4() * 2;
+
+    EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, nonIdentity)).Times(1);
+
+    mDisplay.setColorTransform(nonIdentity);
+
+    EXPECT_EQ(HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX, mDisplay.getState().colorTransform);
+}
+
+/* ------------------------------------------------------------------------
+ * Display::setColorMode()
+ */
+
+TEST_F(DisplayTest, setColorModeSetsModeUnlessNoChange) {
+    mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
+    mDisplay.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
+
+    EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+
+    // These values are expected to be the initial state.
+    ASSERT_EQ(ui::ColorMode::NATIVE, mDisplay.getState().colorMode);
+    ASSERT_EQ(ui::Dataspace::UNKNOWN, mDisplay.getState().dataspace);
+    ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay.getState().renderIntent);
+
+    // Otherwise if the values are unchanged, nothing happens
+    mDisplay.setColorMode(ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
+                          ui::RenderIntent::COLORIMETRIC);
+
+    EXPECT_EQ(ui::ColorMode::NATIVE, mDisplay.getState().colorMode);
+    EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay.getState().dataspace);
+    EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay.getState().renderIntent);
+
+    // Otherwise if the values are different, updates happen
+    EXPECT_CALL(*renderSurface, setBufferDataspace(ui::Dataspace::SRGB)).Times(1);
+    EXPECT_CALL(mHwComposer,
+                setActiveColorMode(DEFAULT_DISPLAY_ID, ui::ColorMode::BT2100_PQ,
+                                   ui::RenderIntent::TONE_MAP_COLORIMETRIC))
+            .Times(1);
+
+    mDisplay.setColorMode(ui::ColorMode::BT2100_PQ, ui::Dataspace::SRGB,
+                          ui::RenderIntent::TONE_MAP_COLORIMETRIC);
+
+    EXPECT_EQ(ui::ColorMode::BT2100_PQ, mDisplay.getState().colorMode);
+    EXPECT_EQ(ui::Dataspace::SRGB, mDisplay.getState().dataspace);
+    EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mDisplay.getState().renderIntent);
+}
+
+TEST_F(DisplayTest, setColorModeDoesNothingForVirtualDisplay) {
+    impl::Display virtualDisplay{mCompositionEngine,
+                                 DisplayCreationArgs{false, true, DEFAULT_DISPLAY_ID}};
+
+    virtualDisplay.setColorMode(ui::ColorMode::BT2100_PQ, ui::Dataspace::SRGB,
+                                ui::RenderIntent::TONE_MAP_COLORIMETRIC);
+
+    EXPECT_EQ(ui::ColorMode::NATIVE, virtualDisplay.getState().colorMode);
+    EXPECT_EQ(ui::Dataspace::UNKNOWN, virtualDisplay.getState().dataspace);
+    EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, virtualDisplay.getState().renderIntent);
+}
+
+/* ------------------------------------------------------------------------
+ * Display::createDisplayColorProfile()
+ */
+
+TEST_F(DisplayTest, createDisplayColorProfileSetsDisplayColorProfile) {
+    EXPECT_TRUE(mDisplay.getDisplayColorProfile() == nullptr);
+    mDisplay.createDisplayColorProfile(
+            DisplayColorProfileCreationArgs{false, HdrCapabilities(), 0,
+                                            DisplayColorProfileCreationArgs::HwcColorModes()});
+    EXPECT_TRUE(mDisplay.getDisplayColorProfile() != nullptr);
+}
+
+/* ------------------------------------------------------------------------
+ * Display::createRenderSurface()
+ */
+
+TEST_F(DisplayTest, createRenderSurfaceSetsRenderSurface) {
+    EXPECT_TRUE(mDisplay.getRenderSurface() == nullptr);
+    mDisplay.createRenderSurface(RenderSurfaceCreationArgs{640, 480, nullptr, nullptr});
+    EXPECT_TRUE(mDisplay.getRenderSurface() != nullptr);
+}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
new file mode 100644
index 0000000..fe12825
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -0,0 +1,291 @@
+/*
+ * 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 <cmath>
+
+#include <compositionengine/impl/Output.h>
+#include <compositionengine/mock/CompositionEngine.h>
+#include <compositionengine/mock/DisplayColorProfile.h>
+#include <compositionengine/mock/RenderSurface.h>
+#include <gtest/gtest.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+
+#include "RegionMatcher.h"
+#include "TransformMatcher.h"
+
+namespace android::compositionengine {
+namespace {
+
+using testing::Return;
+using testing::ReturnRef;
+using testing::StrictMock;
+
+class OutputTest : public testing::Test {
+public:
+    OutputTest() {
+        mOutput.setDisplayColorProfileForTest(
+                std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+        mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+    }
+    ~OutputTest() override = default;
+
+    StrictMock<mock::CompositionEngine> mCompositionEngine;
+    mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+    mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+    impl::Output mOutput{mCompositionEngine};
+};
+
+/* ------------------------------------------------------------------------
+ * Basic construction
+ */
+
+TEST_F(OutputTest, canInstantiateOutput) {
+    // The validation check checks each required component.
+    EXPECT_CALL(*mDisplayColorProfile, isValid()).WillOnce(Return(true));
+    EXPECT_CALL(*mRenderSurface, isValid()).WillOnce(Return(true));
+
+    EXPECT_TRUE(mOutput.isValid());
+
+    // If we take away the required components, it is no longer valid.
+    mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>());
+
+    EXPECT_CALL(*mDisplayColorProfile, isValid()).WillOnce(Return(true));
+
+    EXPECT_FALSE(mOutput.isValid());
+}
+
+/* ------------------------------------------------------------------------
+ * Output::setCompositionEnabled()
+ */
+
+TEST_F(OutputTest, setCompositionEnabledDoesNothingIfAlreadyEnabled) {
+    const Rect displaySize{100, 200};
+    mOutput.editState().bounds = displaySize;
+    mOutput.editState().isEnabled = true;
+
+    mOutput.setCompositionEnabled(true);
+
+    EXPECT_TRUE(mOutput.getState().isEnabled);
+    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region()));
+}
+
+TEST_F(OutputTest, setCompositionEnabledSetsEnabledAndDirtiesEntireOutput) {
+    const Rect displaySize{100, 200};
+    mOutput.editState().bounds = displaySize;
+    mOutput.editState().isEnabled = false;
+
+    mOutput.setCompositionEnabled(true);
+
+    EXPECT_TRUE(mOutput.getState().isEnabled);
+    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(displaySize)));
+}
+
+TEST_F(OutputTest, setCompositionEnabledSetsDisabledAndDirtiesEntireOutput) {
+    const Rect displaySize{100, 200};
+    mOutput.editState().bounds = displaySize;
+    mOutput.editState().isEnabled = true;
+
+    mOutput.setCompositionEnabled(false);
+
+    EXPECT_FALSE(mOutput.getState().isEnabled);
+    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(displaySize)));
+}
+
+/* ------------------------------------------------------------------------
+ * Output::setProjection()
+ */
+
+TEST_F(OutputTest, setProjectionTriviallyWorks) {
+    const ui::Transform transform{ui::Transform::ROT_180};
+    const int32_t orientation = 123;
+    const Rect frame{1, 2, 3, 4};
+    const Rect viewport{5, 6, 7, 8};
+    const Rect scissor{9, 10, 11, 12};
+    const bool needsFiltering = true;
+
+    mOutput.setProjection(transform, orientation, frame, viewport, scissor, needsFiltering);
+
+    EXPECT_THAT(mOutput.getState().transform, TransformEq(transform));
+    EXPECT_EQ(orientation, mOutput.getState().orientation);
+    EXPECT_EQ(frame, mOutput.getState().frame);
+    EXPECT_EQ(viewport, mOutput.getState().viewport);
+    EXPECT_EQ(scissor, mOutput.getState().scissor);
+    EXPECT_EQ(needsFiltering, mOutput.getState().needsFiltering);
+}
+
+/* ------------------------------------------------------------------------
+ * Output::setBounds()
+ */
+
+TEST_F(OutputTest, setBoundsSetsSizeAndDirtiesEntireOutput) {
+    const ui::Size displaySize{100, 200};
+
+    EXPECT_CALL(*mRenderSurface, setDisplaySize(displaySize)).Times(1);
+    EXPECT_CALL(*mRenderSurface, getSize()).WillOnce(ReturnRef(displaySize));
+
+    mOutput.setBounds(displaySize);
+
+    EXPECT_EQ(Rect(displaySize), mOutput.getState().bounds);
+
+    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(Rect(displaySize))));
+}
+
+/* ------------------------------------------------------------------------
+ * Output::setLayerStackFilter()
+ */
+
+TEST_F(OutputTest, setLayerStackFilterSetsFilterAndDirtiesEntireOutput) {
+    const Rect displaySize{100, 200};
+    mOutput.editState().bounds = displaySize;
+
+    const uint32_t layerStack = 123u;
+    mOutput.setLayerStackFilter(layerStack, true);
+
+    EXPECT_TRUE(mOutput.getState().layerStackInternal);
+    EXPECT_EQ(layerStack, mOutput.getState().layerStackId);
+
+    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(displaySize)));
+}
+
+/* ------------------------------------------------------------------------
+ * Output::setColorTransform
+ */
+
+TEST_F(OutputTest, setColorTransformSetsTransform) {
+    // Identity matrix sets an identity state value
+    const mat4 identity;
+
+    mOutput.setColorTransform(identity);
+
+    EXPECT_EQ(HAL_COLOR_TRANSFORM_IDENTITY, mOutput.getState().colorTransform);
+
+    // Non-identity matrix sets a non-identity state value
+    const mat4 nonIdentity = mat4() * 2;
+
+    mOutput.setColorTransform(nonIdentity);
+
+    EXPECT_EQ(HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX, mOutput.getState().colorTransform);
+}
+
+/* ------------------------------------------------------------------------
+ * Output::setColorMode
+ */
+
+TEST_F(OutputTest, setColorModeSetsModeUnlessNoChange) {
+    EXPECT_CALL(*mRenderSurface, setBufferDataspace(ui::Dataspace::SRGB)).Times(1);
+
+    mOutput.setColorMode(ui::ColorMode::BT2100_PQ, ui::Dataspace::SRGB,
+                         ui::RenderIntent::TONE_MAP_COLORIMETRIC);
+
+    EXPECT_EQ(ui::ColorMode::BT2100_PQ, mOutput.getState().colorMode);
+    EXPECT_EQ(ui::Dataspace::SRGB, mOutput.getState().dataspace);
+    EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mOutput.getState().renderIntent);
+}
+
+/* ------------------------------------------------------------------------
+ * Output::setRenderSurface()
+ */
+
+TEST_F(OutputTest, setRenderSurfaceResetsBounds) {
+    const ui::Size newDisplaySize{640, 480};
+
+    mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
+    EXPECT_CALL(*renderSurface, getSize()).WillOnce(ReturnRef(newDisplaySize));
+
+    mOutput.setRenderSurface(std::unique_ptr<RenderSurface>(renderSurface));
+
+    EXPECT_EQ(Rect(newDisplaySize), mOutput.getState().bounds);
+}
+
+/* ------------------------------------------------------------------------
+ * Output::getPhysicalSpaceDirtyRegion()
+ */
+
+TEST_F(OutputTest, getPhysicalSpaceDirtyRegionWithRepaintEverythingTrue) {
+    const Rect displaySize{100, 200};
+    mOutput.editState().bounds = displaySize;
+    mOutput.editState().dirtyRegion.set(50, 300);
+
+    {
+        Region result = mOutput.getPhysicalSpaceDirtyRegion(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)));
+    }
+}
+
+TEST_F(OutputTest, getPhysicalSpaceDirtyRegionWithRepaintEverythingFalse) {
+    const Rect displaySize{100, 200};
+    mOutput.editState().bounds = displaySize;
+    mOutput.editState().dirtyRegion.set(50, 300);
+
+    {
+        Region result = mOutput.getPhysicalSpaceDirtyRegion(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))));
+    }
+}
+
+/* ------------------------------------------------------------------------
+ * Output::belongsInOutput()
+ */
+
+TEST_F(OutputTest, belongsInOutputFiltersAsExpected) {
+    const uint32_t layerStack1 = 123u;
+    const uint32_t layerStack2 = 456u;
+
+    // If the output accepts layerStack1 and internal-only layers....
+    mOutput.setLayerStackFilter(layerStack1, true);
+
+    // Any layer with layerStack1 belongs to it, internal-only or not.
+    EXPECT_TRUE(mOutput.belongsInOutput(layerStack1, false));
+    EXPECT_TRUE(mOutput.belongsInOutput(layerStack1, true));
+    EXPECT_FALSE(mOutput.belongsInOutput(layerStack2, true));
+    EXPECT_FALSE(mOutput.belongsInOutput(layerStack2, false));
+
+    // If the output accepts layerStack21 but not internal-only layers...
+    mOutput.setLayerStackFilter(layerStack1, false);
+
+    // Only non-internal layers with layerStack1 belong to it.
+    EXPECT_TRUE(mOutput.belongsInOutput(layerStack1, false));
+    EXPECT_FALSE(mOutput.belongsInOutput(layerStack1, true));
+    EXPECT_FALSE(mOutput.belongsInOutput(layerStack2, true));
+    EXPECT_FALSE(mOutput.belongsInOutput(layerStack2, false));
+}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/RegionMatcher.h b/services/surfaceflinger/CompositionEngine/tests/RegionMatcher.h
new file mode 100644
index 0000000..5a4efa9
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/RegionMatcher.h
@@ -0,0 +1,43 @@
+/*
+ * 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>
+
+namespace {
+
+// Checks for a region match
+MATCHER_P(RegionEq, expected, "") {
+    std::string buf;
+    buf.append("Regions are not equal\n");
+    expected.dump(buf, "expected region");
+    arg.dump(buf, "actual region");
+    *result_listener << buf;
+
+    size_t expectedRectCount = 0;
+    android::Rect const* expectedRects = expected.getArray(&expectedRectCount);
+    size_t actualRectCount = 0;
+    android::Rect const* actualRects = arg.getArray(&actualRectCount);
+
+    if (expectedRectCount != actualRectCount) return false;
+    for (size_t i = 0; i < expectedRectCount; i++) {
+        if (expectedRects[i] != actualRects[i]) return false;
+    }
+    return true;
+}
+
+} // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
new file mode 100644
index 0000000..13cc663
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
@@ -0,0 +1,504 @@
+/*
+ * 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 <cstdarg>
+#include <cstdint>
+
+#include <compositionengine/RenderSurfaceCreationArgs.h>
+#include <compositionengine/impl/RenderSurface.h>
+#include <compositionengine/mock/CompositionEngine.h>
+#include <compositionengine/mock/Display.h>
+#include <compositionengine/mock/DisplaySurface.h>
+#include <gtest/gtest.h>
+#include <renderengine/mock/RenderEngine.h>
+#include <system/window.h>
+#include <ui/ANativeObjectBase.h>
+
+#include "MockHWComposer.h"
+
+namespace android::compositionengine {
+namespace {
+
+/* ------------------------------------------------------------------------
+ * MockNativeWindow
+ *
+ * An intentionally simplified Mock which implements a minimal subset of the full
+ * ANativeWindow interface.
+ */
+
+class MockNativeWindow : public ANativeObjectBase<ANativeWindow, MockNativeWindow, RefBase> {
+public:
+    MockNativeWindow() {
+        ANativeWindow::setSwapInterval = &forwardSetSwapInterval;
+        ANativeWindow::dequeueBuffer = &forwardDequeueBuffer;
+        ANativeWindow::cancelBuffer = &forwardCancelBuffer;
+        ANativeWindow::queueBuffer = &forwardQueueBuffer;
+        ANativeWindow::query = &forwardQuery;
+        ANativeWindow::perform = &forwardPerform;
+
+        ANativeWindow::dequeueBuffer_DEPRECATED = &forwardDequeueBufferDeprecated;
+        ANativeWindow::cancelBuffer_DEPRECATED = &forwardCancelBufferDeprecated;
+        ANativeWindow::lockBuffer_DEPRECATED = &forwardLockBufferDeprecated;
+        ANativeWindow::queueBuffer_DEPRECATED = &forwardQueueBufferDeprecated;
+    }
+
+    MOCK_METHOD1(setSwapInterval, int(int));
+    MOCK_METHOD2(dequeueBuffer, int(struct ANativeWindowBuffer**, int*));
+    MOCK_METHOD2(cancelBuffer, int(struct ANativeWindowBuffer*, int));
+    MOCK_METHOD2(queueBuffer, int(struct ANativeWindowBuffer*, int));
+    MOCK_CONST_METHOD2(query, int(int, int*));
+    MOCK_METHOD1(connect, int(int));
+    MOCK_METHOD1(lockBuffer_DEPRECATED, int(struct ANativeWindowBuffer*));
+    MOCK_METHOD1(setBuffersFormat, int(PixelFormat));
+    MOCK_METHOD1(setBuffersDataSpace, int(ui::Dataspace));
+    MOCK_METHOD1(setUsage, int(uint64_t));
+
+    static void unexpectedCall(...) { LOG_ALWAYS_FATAL("Unexpected ANativeWindow API call"); }
+
+    static int forwardSetSwapInterval(ANativeWindow* window, int interval) {
+        return getSelf(window)->setSwapInterval(interval);
+    }
+
+    static int forwardDequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer,
+                                    int* fenceFd) {
+        return getSelf(window)->dequeueBuffer(buffer, fenceFd);
+    }
+
+    static int forwardCancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer,
+                                   int fenceFd) {
+        return getSelf(window)->cancelBuffer(buffer, fenceFd);
+    }
+
+    static int forwardQueueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd) {
+        return getSelf(window)->queueBuffer(buffer, fenceFd);
+    }
+
+    static int forwardQuery(const ANativeWindow* window, int what, int* value) {
+        return getSelf(window)->query(what, value);
+    }
+
+    static int forwardPerform(ANativeWindow* window, int operation, ...) {
+        va_list args;
+        va_start(args, operation);
+        int result = NO_ERROR;
+        switch (operation) {
+            case NATIVE_WINDOW_API_CONNECT: {
+                int api = va_arg(args, int);
+                result = getSelf(window)->connect(api);
+                break;
+            }
+            case NATIVE_WINDOW_SET_BUFFERS_FORMAT: {
+                PixelFormat format = va_arg(args, PixelFormat);
+                result = getSelf(window)->setBuffersFormat(format);
+                break;
+            }
+            case NATIVE_WINDOW_SET_BUFFERS_DATASPACE: {
+                ui::Dataspace dataspace = static_cast<ui::Dataspace>(va_arg(args, int));
+                result = getSelf(window)->setBuffersDataSpace(dataspace);
+                break;
+            }
+            case NATIVE_WINDOW_SET_USAGE: {
+                // Note: Intentionally widens usage from 32 to 64 bits so we
+                // just have one implementation.
+                uint64_t usage = va_arg(args, uint32_t);
+                result = getSelf(window)->setUsage(usage);
+                break;
+            }
+            case NATIVE_WINDOW_SET_USAGE64: {
+                uint64_t usage = va_arg(args, uint64_t);
+                result = getSelf(window)->setUsage(usage);
+                break;
+            }
+            default:
+                LOG_ALWAYS_FATAL("Unexpected operation %d", operation);
+                break;
+        }
+
+        va_end(args);
+        return result;
+    }
+
+    static int forwardDequeueBufferDeprecated(ANativeWindow* window, ANativeWindowBuffer** buffer) {
+        int ignoredFenceFd = -1;
+        return getSelf(window)->dequeueBuffer(buffer, &ignoredFenceFd);
+    }
+
+    static int forwardCancelBufferDeprecated(ANativeWindow* window, ANativeWindowBuffer* buffer) {
+        return getSelf(window)->cancelBuffer(buffer, -1);
+    }
+
+    static int forwardLockBufferDeprecated(ANativeWindow* window, ANativeWindowBuffer* buffer) {
+        return getSelf(window)->lockBuffer_DEPRECATED(buffer);
+    }
+
+    static int forwardQueueBufferDeprecated(ANativeWindow* window, ANativeWindowBuffer* buffer) {
+        return getSelf(window)->queueBuffer(buffer, -1);
+    }
+};
+
+/* ------------------------------------------------------------------------
+ * RenderSurfaceTest
+ */
+
+constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1920;
+constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1080;
+constexpr std::optional<DisplayId> DEFAULT_DISPLAY_ID = std::make_optional(DisplayId{123u});
+const std::string DEFAULT_DISPLAY_NAME = "Mock Display";
+
+using testing::_;
+using testing::ByMove;
+using testing::DoAll;
+using testing::Ref;
+using testing::Return;
+using testing::ReturnRef;
+using testing::SetArgPointee;
+using testing::StrictMock;
+
+class RenderSurfaceTest : public testing::Test {
+public:
+    RenderSurfaceTest() {
+        EXPECT_CALL(mDisplay, getId()).WillRepeatedly(ReturnRef(DEFAULT_DISPLAY_ID));
+        EXPECT_CALL(mDisplay, getName()).WillRepeatedly(ReturnRef(DEFAULT_DISPLAY_NAME));
+        EXPECT_CALL(mCompositionEngine, getHwComposer).WillRepeatedly(ReturnRef(mHwComposer));
+        EXPECT_CALL(mCompositionEngine, getRenderEngine).WillRepeatedly(ReturnRef(mRenderEngine));
+    }
+    ~RenderSurfaceTest() override = default;
+
+    StrictMock<android::mock::HWComposer> mHwComposer;
+    StrictMock<renderengine::mock::RenderEngine> mRenderEngine;
+    StrictMock<mock::CompositionEngine> mCompositionEngine;
+    StrictMock<mock::Display> mDisplay;
+    sp<MockNativeWindow> mNativeWindow = new StrictMock<MockNativeWindow>();
+    sp<mock::DisplaySurface> mDisplaySurface = new StrictMock<mock::DisplaySurface>();
+    impl::RenderSurface mSurface{mCompositionEngine, mDisplay,
+                                 RenderSurfaceCreationArgs{DEFAULT_DISPLAY_WIDTH,
+                                                           DEFAULT_DISPLAY_HEIGHT, mNativeWindow,
+                                                           mDisplaySurface}};
+};
+
+/* ------------------------------------------------------------------------
+ * Basic construction
+ */
+
+TEST_F(RenderSurfaceTest, canInstantiate) {
+    EXPECT_TRUE(mSurface.isValid());
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::initialize()
+ */
+
+TEST_F(RenderSurfaceTest, initializeConfiguresNativeWindow) {
+    EXPECT_CALL(*mNativeWindow, connect(NATIVE_WINDOW_API_EGL)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(*mNativeWindow, setBuffersFormat(HAL_PIXEL_FORMAT_RGBA_8888))
+            .WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER)).WillOnce(Return(NO_ERROR));
+
+    mSurface.initialize();
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::getSize()
+ */
+
+TEST_F(RenderSurfaceTest, sizeReturnsConstructedSize) {
+    const ui::Size expected{DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT};
+
+    EXPECT_EQ(expected, mSurface.getSize());
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::getClientTargetAcquireFence()
+ */
+
+TEST_F(RenderSurfaceTest, getClientTargetAcquireFenceForwardsCall) {
+    sp<Fence> fence = new Fence();
+
+    EXPECT_CALL(*mDisplaySurface, getClientTargetAcquireFence()).WillOnce(ReturnRef(fence));
+
+    EXPECT_EQ(fence.get(), mSurface.getClientTargetAcquireFence().get());
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::setDisplaySize()
+ */
+
+TEST_F(RenderSurfaceTest, setDisplaySizeAppliesChange) {
+    EXPECT_CALL(*mDisplaySurface, resizeBuffers(640, 480)).Times(1);
+
+    mSurface.setDisplaySize(ui::Size(640, 480));
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::setBufferDataspace()
+ */
+
+TEST_F(RenderSurfaceTest, setBufferDataspaceAppliesChange) {
+    EXPECT_CALL(*mNativeWindow, setBuffersDataSpace(ui::Dataspace::DISPLAY_P3))
+            .WillOnce(Return(NO_ERROR));
+
+    mSurface.setBufferDataspace(ui::Dataspace::DISPLAY_P3);
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::setProtected()
+ */
+
+TEST_F(RenderSurfaceTest, setProtectedTrueEnablesProtection) {
+    EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_PROTECTED))
+            .WillOnce(Return(NO_ERROR));
+
+    mSurface.setProtected(true);
+}
+
+TEST_F(RenderSurfaceTest, setProtectedFalseDisablesProtection) {
+    EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER)).WillOnce(Return(NO_ERROR));
+
+    mSurface.setProtected(false);
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::beginFrame()
+ */
+
+TEST_F(RenderSurfaceTest, beginFrameAppliesChange) {
+    EXPECT_CALL(*mDisplaySurface, beginFrame(true)).WillOnce(Return(NO_ERROR));
+
+    EXPECT_EQ(NO_ERROR, mSurface.beginFrame(true));
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::prepareFrame()
+ */
+
+TEST_F(RenderSurfaceTest, prepareFrameTakesEarlyOutOnHwcError) {
+    std::vector<CompositionInfo> data;
+
+    EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(data)))
+            .WillOnce(Return(INVALID_OPERATION));
+
+    EXPECT_EQ(INVALID_OPERATION, mSurface.prepareFrame(data));
+}
+
+TEST_F(RenderSurfaceTest, prepareFrameHandlesMixedComposition) {
+    std::vector<CompositionInfo> data;
+    EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(data))).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+    EXPECT_CALL(mHwComposer, hasDeviceComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+
+    EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_MIXED))
+            .WillOnce(Return(INVALID_OPERATION));
+
+    EXPECT_EQ(INVALID_OPERATION, mSurface.prepareFrame(data));
+}
+
+TEST_F(RenderSurfaceTest, prepareFrameHandlesOnlyGlesComposition) {
+    std::vector<CompositionInfo> data;
+
+    EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(data))).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+    EXPECT_CALL(mHwComposer, hasDeviceComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
+
+    EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_GLES))
+            .WillOnce(Return(NO_ERROR));
+
+    EXPECT_EQ(NO_ERROR, mSurface.prepareFrame(data));
+}
+
+TEST_F(RenderSurfaceTest, prepareFrameHandlesOnlyHwcComposition) {
+    std::vector<CompositionInfo> data;
+
+    EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(data))).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
+    EXPECT_CALL(mHwComposer, hasDeviceComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+
+    EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_HWC))
+            .WillOnce(Return(NO_ERROR));
+
+    EXPECT_EQ(NO_ERROR, mSurface.prepareFrame(data));
+}
+
+TEST_F(RenderSurfaceTest, prepareFrameHandlesNoComposition) {
+    std::vector<CompositionInfo> data;
+
+    EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(data))).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
+    EXPECT_CALL(mHwComposer, hasDeviceComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
+
+    EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_HWC))
+            .WillOnce(Return(NO_ERROR));
+
+    EXPECT_EQ(NO_ERROR, mSurface.prepareFrame(data));
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::dequeueBuffer()
+ */
+
+TEST_F(RenderSurfaceTest, dequeueBufferObtainsABuffer) {
+    sp<GraphicBuffer> buffer = new GraphicBuffer();
+
+    EXPECT_CALL(*mNativeWindow, dequeueBuffer(_, _))
+            .WillOnce(
+                    DoAll(SetArgPointee<0>(buffer.get()), SetArgPointee<1>(-1), Return(NO_ERROR)));
+
+    EXPECT_EQ(buffer.get(), mSurface.dequeueBuffer().get());
+
+    EXPECT_EQ(buffer.get(), mSurface.mutableGraphicBufferForTest().get());
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::queueBuffer()
+ */
+
+TEST_F(RenderSurfaceTest, queueBufferHandlesNoClientComposition) {
+    sp<GraphicBuffer> buffer = new GraphicBuffer();
+    mSurface.mutableGraphicBufferForTest() = buffer;
+
+    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
+    EXPECT_CALL(mHwComposer, hasFlipClientTargetRequest(DEFAULT_DISPLAY_ID))
+            .WillOnce(Return(false));
+    EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
+
+    mSurface.queueBuffer();
+
+    EXPECT_EQ(buffer.get(), mSurface.mutableGraphicBufferForTest().get());
+}
+
+TEST_F(RenderSurfaceTest, queueBufferHandlesClientComposition) {
+    sp<GraphicBuffer> buffer = new GraphicBuffer();
+    mSurface.mutableGraphicBufferForTest() = buffer;
+
+    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+    EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1))
+            .WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
+
+    mSurface.queueBuffer();
+
+    EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
+}
+
+TEST_F(RenderSurfaceTest, queueBufferHandlesFlipClientTargetRequest) {
+    sp<GraphicBuffer> buffer = new GraphicBuffer();
+    mSurface.mutableGraphicBufferForTest() = buffer;
+
+    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
+    EXPECT_CALL(mHwComposer, hasFlipClientTargetRequest(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+    EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1))
+            .WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
+
+    mSurface.queueBuffer();
+
+    EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
+}
+
+TEST_F(RenderSurfaceTest, queueBufferHandlesFlipClientTargetRequestWithNoBufferYetDequeued) {
+    sp<GraphicBuffer> buffer = new GraphicBuffer();
+
+    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
+    EXPECT_CALL(mHwComposer, hasFlipClientTargetRequest(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+    EXPECT_CALL(*mNativeWindow, dequeueBuffer(_, _))
+            .WillOnce(
+                    DoAll(SetArgPointee<0>(buffer.get()), SetArgPointee<1>(-1), Return(NO_ERROR)));
+    EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1))
+            .WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
+
+    mSurface.queueBuffer();
+
+    EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
+}
+
+TEST_F(RenderSurfaceTest, queueBufferHandlesNativeWindowQueueBufferFailureOnVirtualDisplay) {
+    sp<GraphicBuffer> buffer = new GraphicBuffer();
+    mSurface.mutableGraphicBufferForTest() = buffer;
+
+    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+    EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1))
+            .WillOnce(Return(INVALID_OPERATION));
+    EXPECT_CALL(mDisplay, isVirtual()).WillOnce(Return(true));
+    EXPECT_CALL(*mNativeWindow, cancelBuffer(buffer->getNativeBuffer(), -1))
+            .WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
+
+    mSurface.queueBuffer();
+
+    EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::onPresentDisplayCompleted()
+ */
+
+TEST_F(RenderSurfaceTest, onPresentDisplayCompletedForwardsSignal) {
+    EXPECT_CALL(*mDisplaySurface, onFrameCommitted()).Times(1);
+
+    mSurface.onPresentDisplayCompleted();
+}
+
+/* ------------------------------------------------------------------------
+ * 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()
+ */
+
+TEST_F(RenderSurfaceTest, setViewportAndProjectionAppliesChang) {
+    mSurface.setSizeForTest(ui::Size(100, 200));
+
+    EXPECT_CALL(mRenderEngine,
+                setViewportAndProjection(100, 200, Rect(100, 200), ui::Transform::ROT_0))
+            .Times(1);
+
+    mSurface.setViewportAndProjection();
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::flip()
+ */
+
+TEST_F(RenderSurfaceTest, flipForwardsSignal) {
+    mSurface.setPageFlipCountForTest(500);
+
+    mSurface.flip();
+
+    EXPECT_EQ(501, mSurface.getPageFlipCount());
+}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/TransformMatcher.h b/services/surfaceflinger/CompositionEngine/tests/TransformMatcher.h
new file mode 100644
index 0000000..ea07bed
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/TransformMatcher.h
@@ -0,0 +1,46 @@
+/*
+ * 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 <string>
+
+#include <gmock/gmock.h>
+
+namespace {
+
+// Check for a transform match
+MATCHER_P(TransformEq, expected, "") {
+    std::string buf;
+    buf.append("Transforms are not equal\n");
+    expected.dump(buf, "expected transform");
+    arg.dump(buf, "actual transform");
+    *result_listener << buf;
+
+    const float TOLERANCE = 1e-3f;
+
+    for (int i = 0; i < 3; i++) {
+        for (int j = 0; j < 3; j++) {
+            if (std::fabs(expected[i][j] - arg[i][j]) > TOLERANCE) {
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
+} // namespace
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index e7b7fbe..4a13bfb 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -18,47 +18,28 @@
 #undef LOG_TAG
 #define LOG_TAG "DisplayDevice"
 
-#include "DisplayDevice.h"
-
-#include <array>
-#include <unordered_set>
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <math.h>
-
 #include <android-base/stringprintf.h>
-#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/Display.h>
+#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/DisplayColorProfileCreationArgs.h>
+#include <compositionengine/DisplayCreationArgs.h>
+#include <compositionengine/DisplaySurface.h>
+#include <compositionengine/RenderSurface.h>
+#include <compositionengine/RenderSurfaceCreationArgs.h>
+#include <compositionengine/impl/OutputCompositionState.h>
 #include <configstore/Utils.h>
-#include <cutils/properties.h>
-#include <gui/Surface.h>
-#include <hardware/gralloc.h>
-#include <renderengine/RenderEngine.h>
-#include <sync/sync.h>
+#include <log/log.h>
 #include <system/window.h>
-#include <ui/DebugUtils.h>
-#include <ui/DisplayInfo.h>
-#include <ui/PixelFormat.h>
-#include <utils/Log.h>
-#include <utils/RefBase.h>
+#include <ui/GraphicTypes.h>
 
-#include "DisplayHardware/DisplaySurface.h"
-#include "DisplayHardware/HWComposer.h"
-#include "DisplayHardware/HWC2.h"
-#include "SurfaceFlinger.h"
+#include "DisplayDevice.h"
 #include "Layer.h"
+#include "SurfaceFlinger.h"
 
 namespace android {
 
-// retrieve triple buffer setting from configstore
-using namespace android::hardware::configstore;
-using namespace android::hardware::configstore::V1_0;
 using android::base::StringAppendF;
-using android::ui::ColorMode;
-using android::ui::Dataspace;
-using android::ui::Hdr;
-using android::ui::RenderIntent;
 
 /*
  * Initialize the display to the specified values.
@@ -67,413 +48,72 @@
 
 uint32_t DisplayDevice::sPrimaryDisplayOrientation = 0;
 
-namespace {
-
-// ordered list of known SDR color modes
-const std::array<ColorMode, 3> sSdrColorModes = {
-        ColorMode::DISPLAY_BT2020,
-        ColorMode::DISPLAY_P3,
-        ColorMode::SRGB,
-};
-
-// ordered list of known HDR color modes
-const std::array<ColorMode, 2> sHdrColorModes = {
-        ColorMode::BT2100_PQ,
-        ColorMode::BT2100_HLG,
-};
-
-// ordered list of known SDR render intents
-const std::array<RenderIntent, 2> sSdrRenderIntents = {
-        RenderIntent::ENHANCE,
-        RenderIntent::COLORIMETRIC,
-};
-
-// ordered list of known HDR render intents
-const std::array<RenderIntent, 2> sHdrRenderIntents = {
-        RenderIntent::TONE_MAP_ENHANCE,
-        RenderIntent::TONE_MAP_COLORIMETRIC,
-};
-
-// map known color mode to dataspace
-Dataspace colorModeToDataspace(ColorMode mode) {
-    switch (mode) {
-        case ColorMode::SRGB:
-            return Dataspace::V0_SRGB;
-        case ColorMode::DISPLAY_P3:
-            return Dataspace::DISPLAY_P3;
-        case ColorMode::DISPLAY_BT2020:
-            return Dataspace::DISPLAY_BT2020;
-        case ColorMode::BT2100_HLG:
-            return Dataspace::BT2020_HLG;
-        case ColorMode::BT2100_PQ:
-            return Dataspace::BT2020_PQ;
-        default:
-            return Dataspace::UNKNOWN;
-    }
-}
-
-// Return a list of candidate color modes.
-std::vector<ColorMode> getColorModeCandidates(ColorMode mode) {
-    std::vector<ColorMode> candidates;
-
-    // add mode itself
-    candidates.push_back(mode);
-
-    // check if mode is HDR
-    bool isHdr = false;
-    for (auto hdrMode : sHdrColorModes) {
-        if (hdrMode == mode) {
-            isHdr = true;
-            break;
-        }
-    }
-
-    // add other HDR candidates when mode is HDR
-    if (isHdr) {
-        for (auto hdrMode : sHdrColorModes) {
-            if (hdrMode != mode) {
-                candidates.push_back(hdrMode);
-            }
-        }
-    }
-
-    // add other SDR candidates
-    for (auto sdrMode : sSdrColorModes) {
-        if (sdrMode != mode) {
-            candidates.push_back(sdrMode);
-        }
-    }
-
-    return candidates;
-}
-
-// Return a list of candidate render intents.
-std::vector<RenderIntent> getRenderIntentCandidates(RenderIntent intent) {
-    std::vector<RenderIntent> candidates;
-
-    // add intent itself
-    candidates.push_back(intent);
-
-    // check if intent is HDR
-    bool isHdr = false;
-    for (auto hdrIntent : sHdrRenderIntents) {
-        if (hdrIntent == intent) {
-            isHdr = true;
-            break;
-        }
-    }
-
-    if (isHdr) {
-        // add other HDR candidates when intent is HDR
-        for (auto hdrIntent : sHdrRenderIntents) {
-            if (hdrIntent != intent) {
-                candidates.push_back(hdrIntent);
-            }
-        }
-    } else {
-        // add other SDR candidates when intent is SDR
-        for (auto sdrIntent : sSdrRenderIntents) {
-            if (sdrIntent != intent) {
-                candidates.push_back(sdrIntent);
-            }
-        }
-    }
-
-    return candidates;
-}
-
-// Return the best color mode supported by HWC.
-ColorMode getHwcColorMode(
-        const std::unordered_map<ColorMode, std::vector<RenderIntent>>& hwcColorModes,
-        ColorMode mode) {
-    std::vector<ColorMode> candidates = getColorModeCandidates(mode);
-    for (auto candidate : candidates) {
-        auto iter = hwcColorModes.find(candidate);
-        if (iter != hwcColorModes.end()) {
-            return candidate;
-        }
-    }
-
-    return ColorMode::NATIVE;
-}
-
-// Return the best render intent supported by HWC.
-RenderIntent getHwcRenderIntent(const std::vector<RenderIntent>& hwcIntents, RenderIntent intent) {
-    std::vector<RenderIntent> candidates = getRenderIntentCandidates(intent);
-    for (auto candidate : candidates) {
-        for (auto hwcIntent : hwcIntents) {
-            if (candidate == hwcIntent) {
-                return candidate;
-            }
-        }
-    }
-
-    return RenderIntent::COLORIMETRIC;
-}
-
-} // anonymous namespace
-
 DisplayDeviceCreationArgs::DisplayDeviceCreationArgs(const sp<SurfaceFlinger>& flinger,
                                                      const wp<IBinder>& displayToken,
                                                      const std::optional<DisplayId>& displayId)
       : flinger(flinger), displayToken(displayToken), displayId(displayId) {}
 
 DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs&& args)
-      : lastCompositionHadVisibleLayers(false),
-        mFlinger(args.flinger),
+      : mFlinger(args.flinger),
         mDisplayToken(args.displayToken),
         mSequenceId(args.sequenceId),
-        mId(args.displayId),
-        mNativeWindow(args.nativeWindow),
-        mGraphicBuffer(nullptr),
-        mDisplaySurface(args.displaySurface),
         mDisplayInstallOrientation(args.displayInstallOrientation),
-        mPageFlipCount(0),
+        mCompositionDisplay{mFlinger->getCompositionEngine().createDisplay(
+                compositionengine::DisplayCreationArgs{args.isSecure, args.isVirtual,
+                                                       args.displayId})},
         mIsVirtual(args.isVirtual),
-        mIsSecure(args.isSecure),
-        mLayerStack(NO_LAYER_STACK),
         mOrientation(),
-        mViewport(Rect::INVALID_RECT),
-        mFrame(Rect::INVALID_RECT),
-        mPowerMode(args.initialPowerMode),
         mActiveConfig(0),
-        mColorTransform(HAL_COLOR_TRANSFORM_IDENTITY),
-        mHasWideColorGamut(args.hasWideColorGamut),
-        mHasHdr10Plus(false),
-        mHasHdr10(false),
-        mHasHLG(false),
-        mHasDolbyVision(false),
-        mSupportedPerFrameMetadata(args.supportedPerFrameMetadata),
         mIsPrimary(args.isPrimary) {
-    populateColorModes(args.hwcColorModes);
+    mCompositionDisplay->createRenderSurface(
+            compositionengine::RenderSurfaceCreationArgs{ANativeWindow_getWidth(
+                                                                 args.nativeWindow.get()),
+                                                         ANativeWindow_getHeight(
+                                                                 args.nativeWindow.get()),
+                                                         args.nativeWindow, args.displaySurface});
 
-    ALOGE_IF(!mNativeWindow, "No native window was set for display");
-    ALOGE_IF(!mDisplaySurface, "No display surface was set for display");
+    mCompositionDisplay->createDisplayColorProfile(
+            compositionengine::DisplayColorProfileCreationArgs{args.hasWideColorGamut,
+                                                               std::move(args.hdrCapabilities),
+                                                               args.supportedPerFrameMetadata,
+                                                               args.hwcColorModes});
 
-    std::vector<Hdr> types = args.hdrCapabilities.getSupportedHdrTypes();
-    for (Hdr hdrType : types) {
-        switch (hdrType) {
-            case Hdr::HDR10_PLUS:
-                mHasHdr10Plus = true;
-                break;
-            case Hdr::HDR10:
-                mHasHdr10 = true;
-                break;
-            case Hdr::HLG:
-                mHasHLG = true;
-                break;
-            case Hdr::DOLBY_VISION:
-                mHasDolbyVision = true;
-                break;
-            default:
-                ALOGE("UNKNOWN HDR capability: %d", static_cast<int32_t>(hdrType));
-        }
+    if (!mCompositionDisplay->isValid()) {
+        ALOGE("Composition Display did not validate!");
     }
 
-    float minLuminance = args.hdrCapabilities.getDesiredMinLuminance();
-    float maxLuminance = args.hdrCapabilities.getDesiredMaxLuminance();
-    float maxAverageLuminance = args.hdrCapabilities.getDesiredMaxAverageLuminance();
+    mCompositionDisplay->getRenderSurface()->initialize();
 
-    minLuminance = minLuminance <= 0.0 ? sDefaultMinLumiance : minLuminance;
-    maxLuminance = maxLuminance <= 0.0 ? sDefaultMaxLumiance : maxLuminance;
-    maxAverageLuminance = maxAverageLuminance <= 0.0 ? sDefaultMaxLumiance : maxAverageLuminance;
-    if (this->hasWideColorGamut()) {
-        // insert HDR10/HLG as we will force client composition for HDR10/HLG
-        // layers
-        if (!hasHDR10Support()) {
-            types.push_back(Hdr::HDR10);
-        }
-
-        if (!hasHLGSupport()) {
-            types.push_back(Hdr::HLG);
-        }
-    }
-    mHdrCapabilities = HdrCapabilities(types, maxLuminance, maxAverageLuminance, minLuminance);
-
-    ANativeWindow* const window = mNativeWindow.get();
-
-    int status = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
-    ALOGE_IF(status != NO_ERROR, "Unable to connect BQ producer: %d", status);
-    status = native_window_set_buffers_format(window, HAL_PIXEL_FORMAT_RGBA_8888);
-    ALOGE_IF(status != NO_ERROR, "Unable to set BQ format to RGBA888: %d", status);
-    status = native_window_set_usage(window, GRALLOC_USAGE_HW_RENDER);
-    ALOGE_IF(status != NO_ERROR, "Unable to set BQ usage bits for GPU rendering: %d", status);
-
-    mDisplayWidth = ANativeWindow_getWidth(window);
-    mDisplayHeight = ANativeWindow_getHeight(window);
+    setPowerMode(args.initialPowerMode);
 
     // initialize the display orientation transform.
-    setProjection(DisplayState::eOrientationDefault, mViewport, mFrame);
+    setProjection(DisplayState::eOrientationDefault, Rect::INVALID_RECT, Rect::INVALID_RECT);
 }
 
 DisplayDevice::~DisplayDevice() = default;
 
-void DisplayDevice::disconnect(HWComposer& hwc) {
-    if (mId) {
-        hwc.disconnectDisplay(*mId);
-        mId.reset();
-    }
+void DisplayDevice::disconnect() {
+    mCompositionDisplay->disconnect();
 }
 
 int DisplayDevice::getWidth() const {
-    return mDisplayWidth;
+    return mCompositionDisplay->getState().bounds.getWidth();
 }
 
 int DisplayDevice::getHeight() const {
-    return mDisplayHeight;
+    return mCompositionDisplay->getState().bounds.getHeight();
 }
 
 void DisplayDevice::setDisplayName(const std::string& displayName) {
     if (!displayName.empty()) {
         // never override the name with an empty name
         mDisplayName = displayName;
+        mCompositionDisplay->setName(displayName);
     }
 }
 
 uint32_t DisplayDevice::getPageFlipCount() const {
-    return mPageFlipCount;
-}
-
-void DisplayDevice::flip() const
-{
-    mPageFlipCount++;
-}
-
-status_t DisplayDevice::beginFrame(bool mustRecompose) const {
-    return mDisplaySurface->beginFrame(mustRecompose);
-}
-
-status_t DisplayDevice::prepareFrame(HWComposer& hwc,
-        std::vector<CompositionInfo>& compositionData) {
-    if (mId) {
-        status_t error = hwc.prepare(*mId, compositionData);
-        if (error != NO_ERROR) {
-            return error;
-        }
-    }
-
-    DisplaySurface::CompositionType compositionType;
-    bool hasClient = hwc.hasClientComposition(mId);
-    bool hasDevice = hwc.hasDeviceComposition(mId);
-    if (hasClient && hasDevice) {
-        compositionType = DisplaySurface::COMPOSITION_MIXED;
-    } else if (hasClient) {
-        compositionType = DisplaySurface::COMPOSITION_GLES;
-    } else if (hasDevice) {
-        compositionType = DisplaySurface::COMPOSITION_HWC;
-    } else {
-        // Nothing to do -- when turning the screen off we get a frame like
-        // this. Call it a HWC frame since we won't be doing any GLES work but
-        // will do a prepare/set cycle.
-        compositionType = DisplaySurface::COMPOSITION_HWC;
-    }
-    return mDisplaySurface->prepareFrame(compositionType);
-}
-
-void DisplayDevice::setProtected(bool useProtected) {
-    uint64_t usageFlags = GRALLOC_USAGE_HW_RENDER;
-    if (useProtected) {
-        usageFlags |= GRALLOC_USAGE_PROTECTED;
-    }
-    const int status = native_window_set_usage(mNativeWindow.get(), usageFlags);
-    ALOGE_IF(status != NO_ERROR, "Unable to set BQ usage bits for protected content: %d", status);
-}
-
-sp<GraphicBuffer> DisplayDevice::dequeueBuffer() {
-    int fd;
-    ANativeWindowBuffer* buffer;
-
-    status_t res = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buffer, &fd);
-
-    if (res != NO_ERROR) {
-        ALOGE("ANativeWindow::dequeueBuffer failed for display [%s] with error: %d",
-              getDisplayName().c_str(), res);
-        // Return fast here as we can't do much more - any rendering we do
-        // now will just be wrong.
-        return mGraphicBuffer;
-    }
-
-    ALOGW_IF(mGraphicBuffer != nullptr, "Clobbering a non-null pointer to a buffer [%p].",
-             mGraphicBuffer->getNativeBuffer()->handle);
-    mGraphicBuffer = GraphicBuffer::from(buffer);
-
-    // Block until the buffer is ready
-    // TODO(alecmouri): it's perhaps more appropriate to block renderengine so
-    // that the gl driver can block instead.
-    if (fd >= 0) {
-        sync_wait(fd, -1);
-        close(fd);
-    }
-
-    return mGraphicBuffer;
-}
-
-void DisplayDevice::queueBuffer(HWComposer& hwc) {
-    if (hwc.hasClientComposition(mId) || hwc.hasFlipClientTargetRequest(mId)) {
-        // hasFlipClientTargetRequest could return true even if we haven't
-        // dequeued a buffer before. Try dequeueing one if we don't have a
-        // buffer ready.
-        if (mGraphicBuffer == nullptr) {
-            ALOGI("Attempting to queue a client composited buffer without one "
-                  "previously dequeued for display [%s]. Attempting to dequeue "
-                  "a scratch buffer now",
-                  mDisplayName.c_str());
-            // We shouldn't deadlock here, since mGraphicBuffer == nullptr only
-            // after a successful call to queueBuffer, or if dequeueBuffer has
-            // never been called.
-            dequeueBuffer();
-        }
-
-        if (mGraphicBuffer == nullptr) {
-            ALOGE("No buffer is ready for display [%s]", mDisplayName.c_str());
-        } else {
-            status_t res = mNativeWindow->queueBuffer(mNativeWindow.get(),
-                                                      mGraphicBuffer->getNativeBuffer(),
-                                                      dup(mBufferReady));
-            if (res != NO_ERROR) {
-                ALOGE("Error when queueing buffer for display [%s]: %d", mDisplayName.c_str(), res);
-                // We risk blocking on dequeueBuffer if the primary display failed
-                // to queue up its buffer, so crash here.
-                if (isPrimary()) {
-                    LOG_ALWAYS_FATAL("ANativeWindow::queueBuffer failed with error: %d", res);
-                } else {
-                    mNativeWindow->cancelBuffer(mNativeWindow.get(),
-                                                mGraphicBuffer->getNativeBuffer(),
-                                                dup(mBufferReady));
-                }
-            }
-
-            mBufferReady.reset();
-            mGraphicBuffer = nullptr;
-        }
-    }
-
-    status_t result = mDisplaySurface->advanceFrame();
-    if (result != NO_ERROR) {
-        ALOGE("[%s] failed pushing new frame to HWC: %d", mDisplayName.c_str(), result);
-    }
-}
-
-void DisplayDevice::onPresentDisplayCompleted() {
-    mDisplaySurface->onFrameCommitted();
-}
-
-void DisplayDevice::setViewportAndProjection() const {
-    size_t w = mDisplayWidth;
-    size_t h = mDisplayHeight;
-    Rect sourceCrop(0, 0, w, h);
-    mFlinger->getRenderEngine().setViewportAndProjection(w, h, sourceCrop, ui::Transform::ROT_0);
-}
-
-void DisplayDevice::finishBuffer() {
-    mBufferReady = mFlinger->getRenderEngine().flush();
-    if (mBufferReady.get() < 0) {
-        mFlinger->getRenderEngine().finish();
-    }
-}
-
-const sp<Fence>& DisplayDevice::getClientTargetAcquireFence() const {
-    return mDisplaySurface->getClientTargetAcquireFence();
+    return mCompositionDisplay->getRenderSurface()->getPageFlipCount();
 }
 
 // ----------------------------------------------------------------------------
@@ -494,21 +134,10 @@
     return mLayersNeedingFences;
 }
 
-Region DisplayDevice::getDirtyRegion(bool repaintEverything) const {
-    Region dirty;
-    if (repaintEverything) {
-        dirty.set(getBounds());
-    } else {
-        const ui::Transform& planeTransform(mGlobalTransform);
-        dirty = planeTransform.transform(this->dirtyRegion);
-        dirty.andSelf(getBounds());
-    }
-    return dirty;
-}
-
 // ----------------------------------------------------------------------------
 void DisplayDevice::setPowerMode(int mode) {
     mPowerMode = mode;
+    getCompositionDisplay()->setCompositionEnabled(mPowerMode != HWC_POWER_MODE_OFF);
 }
 
 int DisplayDevice::getPowerMode()  const {
@@ -529,88 +158,37 @@
 }
 
 // ----------------------------------------------------------------------------
-void DisplayDevice::setActiveColorMode(ColorMode mode) {
-    mActiveColorMode = mode;
-}
-
-ColorMode DisplayDevice::getActiveColorMode() const {
-    return mActiveColorMode;
-}
-
-RenderIntent DisplayDevice::getActiveRenderIntent() const {
-    return mActiveRenderIntent;
-}
-
-void DisplayDevice::setActiveRenderIntent(RenderIntent renderIntent) {
-    mActiveRenderIntent = renderIntent;
-}
-
-void DisplayDevice::setColorTransform(const mat4& transform) {
-    const bool isIdentity = (transform == mat4());
-    mColorTransform =
-            isIdentity ? HAL_COLOR_TRANSFORM_IDENTITY : HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX;
-}
-
-android_color_transform_t DisplayDevice::getColorTransform() const {
-    return mColorTransform;
-}
-
-void DisplayDevice::setCompositionDataSpace(ui::Dataspace dataspace) {
-    mCompositionDataSpace = dataspace;
-    ANativeWindow* const window = mNativeWindow.get();
-    native_window_set_buffers_data_space(window, static_cast<android_dataspace>(dataspace));
-}
 
 ui::Dataspace DisplayDevice::getCompositionDataSpace() const {
-    return mCompositionDataSpace;
+    return mCompositionDisplay->getState().dataspace;
 }
 
 // ----------------------------------------------------------------------------
 
 void DisplayDevice::setLayerStack(uint32_t stack) {
-    mLayerStack = stack;
-    dirtyRegion.set(bounds());
+    mCompositionDisplay->setLayerStackFilter(stack, isPrimary());
 }
 
 // ----------------------------------------------------------------------------
 
-uint32_t DisplayDevice::getOrientationTransform() const {
-    uint32_t transform = 0;
-    switch (mOrientation) {
-        case DisplayState::eOrientationDefault:
-            transform = ui::Transform::ROT_0;
-            break;
-        case DisplayState::eOrientation90:
-            transform = ui::Transform::ROT_90;
-            break;
-        case DisplayState::eOrientation180:
-            transform = ui::Transform::ROT_180;
-            break;
-        case DisplayState::eOrientation270:
-            transform = ui::Transform::ROT_270;
-            break;
-    }
-    return transform;
-}
-
-status_t DisplayDevice::orientationToTransfrom(
-        int orientation, int w, int h, ui::Transform* tr)
-{
-    uint32_t flags = 0;
+uint32_t DisplayDevice::displayStateOrientationToTransformOrientation(int orientation) {
     switch (orientation) {
     case DisplayState::eOrientationDefault:
-        flags = ui::Transform::ROT_0;
-        break;
+        return ui::Transform::ROT_0;
     case DisplayState::eOrientation90:
-        flags = ui::Transform::ROT_90;
-        break;
+        return ui::Transform::ROT_90;
     case DisplayState::eOrientation180:
-        flags = ui::Transform::ROT_180;
-        break;
+        return ui::Transform::ROT_180;
     case DisplayState::eOrientation270:
-        flags = ui::Transform::ROT_270;
-        break;
+        return ui::Transform::ROT_270;
     default:
+        return ui::Transform::ROT_INVALID;
+    }
+}
+
+status_t DisplayDevice::orientationToTransfrom(int orientation, int w, int h, ui::Transform* tr) {
+    uint32_t flags = displayStateOrientationToTransformOrientation(orientation);
+    if (flags == ui::Transform::ROT_INVALID) {
         return BAD_VALUE;
     }
     tr->set(flags, w, h);
@@ -618,12 +196,7 @@
 }
 
 void DisplayDevice::setDisplaySize(const int newWidth, const int newHeight) {
-    dirtyRegion.set(getBounds());
-
-    mDisplaySurface->resizeBuffers(newWidth, newHeight);
-
-    mDisplayWidth = newWidth;
-    mDisplayHeight = newHeight;
+    mCompositionDisplay->setBounds(ui::Size(newWidth, newHeight));
 }
 
 void DisplayDevice::setProjection(int orientation,
@@ -631,8 +204,11 @@
     Rect viewport(newViewport);
     Rect frame(newFrame);
 
-    const int w = mDisplayWidth;
-    const int h = mDisplayHeight;
+    mOrientation = orientation;
+
+    const Rect& displayBounds = getCompositionDisplay()->getState().bounds;
+    const int w = displayBounds.width();
+    const int h = displayBounds.height();
 
     ui::Transform R;
     DisplayDevice::orientationToTransfrom(orientation, w, h, &R);
@@ -656,8 +232,6 @@
         }
     }
 
-    dirtyRegion.set(getBounds());
-
     ui::Transform TL, TP, S;
     float src_width  = viewport.width();
     float src_height = viewport.height();
@@ -676,7 +250,7 @@
     TL.set(-src_x, -src_y);
     TP.set(dst_x, dst_y);
 
-    // need to take care of primary display rotation for mGlobalTransform
+    // need to take care of primary display rotation for globalTransform
     // for case if the panel is not installed aligned with device orientation
     if (isPrimary()) {
         DisplayDevice::orientationToTransfrom(
@@ -687,38 +261,25 @@
     // The viewport and frame are both in the logical orientation.
     // Apply the logical translation, scale to physical size, apply the
     // physical translation and finally rotate to the physical orientation.
-    mGlobalTransform = R * TP * S * TL;
+    ui::Transform globalTransform = R * TP * S * TL;
 
-    const uint8_t type = mGlobalTransform.getType();
-    mNeedsFiltering = (!mGlobalTransform.preserveRects() ||
-            (type >= ui::Transform::SCALE));
+    const uint8_t type = globalTransform.getType();
+    const bool needsFiltering =
+            (!globalTransform.preserveRects() || (type >= ui::Transform::SCALE));
 
-    mScissor = mGlobalTransform.transform(viewport);
-    if (mScissor.isEmpty()) {
-        mScissor = getBounds();
+    Rect scissor = globalTransform.transform(viewport);
+    if (scissor.isEmpty()) {
+        scissor = displayBounds;
     }
 
-    mOrientation = orientation;
     if (isPrimary()) {
-        uint32_t transform = 0;
-        switch (mOrientation) {
-            case DisplayState::eOrientationDefault:
-                transform = ui::Transform::ROT_0;
-                break;
-            case DisplayState::eOrientation90:
-                transform = ui::Transform::ROT_90;
-                break;
-            case DisplayState::eOrientation180:
-                transform = ui::Transform::ROT_180;
-                break;
-            case DisplayState::eOrientation270:
-                transform = ui::Transform::ROT_270;
-                break;
-        }
-        sPrimaryDisplayOrientation = transform;
+        sPrimaryDisplayOrientation = displayStateOrientationToTransformOrientation(orientation);
     }
-    mViewport = viewport;
-    mFrame = frame;
+
+    getCompositionDisplay()->setProjection(globalTransform,
+                                           displayStateOrientationToTransformOrientation(
+                                                   orientation),
+                                           frame, viewport, scissor, needsFiltering);
 }
 
 uint32_t DisplayDevice::getPrimaryDisplayOrientationTransform() {
@@ -726,149 +287,94 @@
 }
 
 std::string DisplayDevice::getDebugName() const {
-    const auto id = mId ? to_string(*mId) + ", " : std::string();
+    const auto id = getId() ? to_string(*getId()) + ", " : std::string();
     return base::StringPrintf("DisplayDevice{%s%s%s\"%s\"}", id.c_str(),
                               isPrimary() ? "primary, " : "", isVirtual() ? "virtual, " : "",
                               mDisplayName.c_str());
 }
 
 void DisplayDevice::dump(std::string& result) const {
-    const ui::Transform& tr(mGlobalTransform);
-    ANativeWindow* const window = mNativeWindow.get();
     StringAppendF(&result, "+ %s\n", getDebugName().c_str());
-    StringAppendF(&result,
-                  "  layerStack=%u, (%4dx%4d), ANativeWindow=%p "
-                  "format=%d, orient=%2d (type=%08x), flips=%u, isSecure=%d, "
-                  "powerMode=%d, activeConfig=%d, numLayers=%zu\n",
-                  mLayerStack, mDisplayWidth, mDisplayHeight, window,
-                  ANativeWindow_getFormat(window), mOrientation, tr.getType(), getPageFlipCount(),
-                  mIsSecure, mPowerMode, mActiveConfig, mVisibleLayersSortedByZ.size());
-    StringAppendF(&result,
-                  "   v:[%d,%d,%d,%d], f:[%d,%d,%d,%d], s:[%d,%d,%d,%d],"
-                  "transform:[[%0.3f,%0.3f,%0.3f][%0.3f,%0.3f,%0.3f][%0.3f,%0.3f,%0.3f]]\n",
-                  mViewport.left, mViewport.top, mViewport.right, mViewport.bottom, mFrame.left,
-                  mFrame.top, mFrame.right, mFrame.bottom, mScissor.left, mScissor.top,
-                  mScissor.right, mScissor.bottom, tr[0][0], tr[1][0], tr[2][0], tr[0][1], tr[1][1],
-                  tr[2][1], tr[0][2], tr[1][2], tr[2][2]);
-    auto const surface = static_cast<Surface*>(window);
-    ui::Dataspace dataspace = surface->getBuffersDataSpace();
-    StringAppendF(&result,
-                  "   wideColorGamut=%d, hdr10plus =%d, hdr10=%d, colorMode=%s, dataspace: %s "
-                  "(%d)\n",
-                  mHasWideColorGamut, mHasHdr10Plus, mHasHdr10,
-                  decodeColorMode(mActiveColorMode).c_str(),
-                  dataspaceDetails(static_cast<android_dataspace>(dataspace)).c_str(), dataspace);
 
-    String8 surfaceDump;
-    mDisplaySurface->dumpAsString(surfaceDump);
-    result.append(surfaceDump.string(), surfaceDump.size());
+    result.append("   ");
+    StringAppendF(&result, "powerMode=%d, ", mPowerMode);
+    StringAppendF(&result, "activeConfig=%d, ", mActiveConfig);
+    StringAppendF(&result, "numLayers=%zu\n", mVisibleLayersSortedByZ.size());
+    getCompositionDisplay()->dump(result);
 }
 
-// Map dataspace/intent to the best matched dataspace/colorMode/renderIntent
-// supported by HWC.
-void DisplayDevice::addColorMode(
-        const std::unordered_map<ColorMode, std::vector<RenderIntent>>& hwcColorModes,
-        const ColorMode mode, const RenderIntent intent) {
-    // find the best color mode
-    const ColorMode hwcColorMode = getHwcColorMode(hwcColorModes, mode);
-
-    // find the best render intent
-    auto iter = hwcColorModes.find(hwcColorMode);
-    const auto& hwcIntents =
-            iter != hwcColorModes.end() ? iter->second : std::vector<RenderIntent>();
-    const RenderIntent hwcIntent = getHwcRenderIntent(hwcIntents, intent);
-
-    const Dataspace dataspace = colorModeToDataspace(mode);
-    const Dataspace hwcDataspace = colorModeToDataspace(hwcColorMode);
-
-    ALOGV("%s: map (%s, %s) to (%s, %s, %s)", getDebugName().c_str(),
-          dataspaceDetails(static_cast<android_dataspace_t>(dataspace)).c_str(),
-          decodeRenderIntent(intent).c_str(),
-          dataspaceDetails(static_cast<android_dataspace_t>(hwcDataspace)).c_str(),
-          decodeColorMode(hwcColorMode).c_str(), decodeRenderIntent(hwcIntent).c_str());
-
-    mColorModes[getColorModeKey(dataspace, intent)] = {hwcDataspace, hwcColorMode, hwcIntent};
+bool DisplayDevice::hasRenderIntent(ui::RenderIntent intent) const {
+    return mCompositionDisplay->getDisplayColorProfile()->hasRenderIntent(intent);
 }
 
-void DisplayDevice::populateColorModes(
-        const std::unordered_map<ColorMode, std::vector<RenderIntent>>& hwcColorModes) {
-    if (!hasWideColorGamut()) {
-        return;
-    }
+// ----------------------------------------------------------------------------
 
-    // collect all known SDR render intents
-    std::unordered_set<RenderIntent> sdrRenderIntents(sSdrRenderIntents.begin(),
-                                                      sSdrRenderIntents.end());
-    auto iter = hwcColorModes.find(ColorMode::SRGB);
-    if (iter != hwcColorModes.end()) {
-        for (auto intent : iter->second) {
-            sdrRenderIntents.insert(intent);
-        }
-    }
-
-    // add all known SDR combinations
-    for (auto intent : sdrRenderIntents) {
-        for (auto mode : sSdrColorModes) {
-            addColorMode(hwcColorModes, mode, intent);
-        }
-    }
-
-    // collect all known HDR render intents
-    std::unordered_set<RenderIntent> hdrRenderIntents(sHdrRenderIntents.begin(),
-                                                      sHdrRenderIntents.end());
-    iter = hwcColorModes.find(ColorMode::BT2100_PQ);
-    if (iter != hwcColorModes.end()) {
-        for (auto intent : iter->second) {
-            hdrRenderIntents.insert(intent);
-        }
-    }
-
-    // add all known HDR combinations
-    for (auto intent : sHdrRenderIntents) {
-        for (auto mode : sHdrColorModes) {
-            addColorMode(hwcColorModes, mode, intent);
-        }
-    }
+const std::optional<DisplayId>& DisplayDevice::getId() const {
+    return mCompositionDisplay->getId();
 }
 
-bool DisplayDevice::hasRenderIntent(RenderIntent intent) const {
-    // assume a render intent is supported when SRGB supports it; we should
-    // get rid of that assumption.
-    auto iter = mColorModes.find(getColorModeKey(Dataspace::V0_SRGB, intent));
-    return iter != mColorModes.end() && iter->second.renderIntent == intent;
+bool DisplayDevice::isSecure() const {
+    return mCompositionDisplay->isSecure();
 }
 
-bool DisplayDevice::hasLegacyHdrSupport(Dataspace dataspace) const {
-    if ((dataspace == Dataspace::BT2020_PQ && hasHDR10Support()) ||
-        (dataspace == Dataspace::BT2020_HLG && hasHLGSupport())) {
-        auto iter =
-                mColorModes.find(getColorModeKey(dataspace, RenderIntent::TONE_MAP_COLORIMETRIC));
-        return iter == mColorModes.end() || iter->second.dataspace != dataspace;
-    }
-
-    return false;
+const Rect& DisplayDevice::getBounds() const {
+    return mCompositionDisplay->getState().bounds;
 }
 
-void DisplayDevice::getBestColorMode(Dataspace dataspace, RenderIntent intent,
-                                     Dataspace* outDataspace, ColorMode* outMode,
-                                     RenderIntent* outIntent) const {
-    auto iter = mColorModes.find(getColorModeKey(dataspace, intent));
-    if (iter != mColorModes.end()) {
-        *outDataspace = iter->second.dataspace;
-        *outMode = iter->second.colorMode;
-        *outIntent = iter->second.renderIntent;
-    } else {
-        // this is unexpected on a WCG display
-        if (hasWideColorGamut()) {
-            ALOGE("map unknown (%s)/(%s) to default color mode",
-                  dataspaceDetails(static_cast<android_dataspace_t>(dataspace)).c_str(),
-                  decodeRenderIntent(intent).c_str());
-        }
+const Region& DisplayDevice::getUndefinedRegion() const {
+    return mCompositionDisplay->getState().undefinedRegion;
+}
 
-        *outDataspace = Dataspace::UNKNOWN;
-        *outMode = ColorMode::NATIVE;
-        *outIntent = RenderIntent::COLORIMETRIC;
-    }
+bool DisplayDevice::needsFiltering() const {
+    return mCompositionDisplay->getState().needsFiltering;
+}
+
+uint32_t DisplayDevice::getLayerStack() const {
+    return mCompositionDisplay->getState().layerStackId;
+}
+
+const ui::Transform& DisplayDevice::getTransform() const {
+    return mCompositionDisplay->getState().transform;
+}
+
+const Rect& DisplayDevice::getViewport() const {
+    return mCompositionDisplay->getState().viewport;
+}
+
+const Rect& DisplayDevice::getFrame() const {
+    return mCompositionDisplay->getState().frame;
+}
+
+const Rect& DisplayDevice::getScissor() const {
+    return mCompositionDisplay->getState().scissor;
+}
+
+bool DisplayDevice::hasWideColorGamut() const {
+    return mCompositionDisplay->getDisplayColorProfile()->hasWideColorGamut();
+}
+
+bool DisplayDevice::hasHDR10PlusSupport() const {
+    return mCompositionDisplay->getDisplayColorProfile()->hasHDR10PlusSupport();
+}
+
+bool DisplayDevice::hasHDR10Support() const {
+    return mCompositionDisplay->getDisplayColorProfile()->hasHDR10Support();
+}
+
+bool DisplayDevice::hasHLGSupport() const {
+    return mCompositionDisplay->getDisplayColorProfile()->hasHLGSupport();
+}
+
+bool DisplayDevice::hasDolbyVisionSupport() const {
+    return mCompositionDisplay->getDisplayColorProfile()->hasDolbyVisionSupport();
+}
+
+int DisplayDevice::getSupportedPerFrameMetadata() const {
+    return mCompositionDisplay->getDisplayColorProfile()->getSupportedPerFrameMetadata();
+}
+
+const HdrCapabilities& DisplayDevice::getHdrCapabilities() const {
+    return mCompositionDisplay->getDisplayColorProfile()->getHdrCapabilities();
 }
 
 std::atomic<int32_t> DisplayDeviceState::sNextSequenceId(1);
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 250a650..9674f0d 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -44,7 +44,6 @@
 
 namespace android {
 
-class DisplaySurface;
 class Fence;
 class HWComposer;
 class IGraphicBufferProducer;
@@ -55,35 +54,33 @@
 struct DisplayDeviceCreationArgs;
 struct DisplayInfo;
 
-class DisplayDevice : public LightRefBase<DisplayDevice>
-{
+namespace compositionengine {
+class Display;
+class DisplaySurface;
+} // namespace compositionengine
+
+class DisplayDevice : public LightRefBase<DisplayDevice> {
 public:
     constexpr static float sDefaultMinLumiance = 0.0;
     constexpr static float sDefaultMaxLumiance = 500.0;
 
-    // region in layer-stack space
-    mutable Region dirtyRegion;
-    // region in screen space
-    Region undefinedRegion;
-    bool lastCompositionHadVisibleLayers;
-
     enum {
         NO_LAYER_STACK = 0xFFFFFFFF,
     };
 
     explicit DisplayDevice(DisplayDeviceCreationArgs&& args);
-    ~DisplayDevice();
+    virtual ~DisplayDevice();
+
+    std::shared_ptr<compositionengine::Display> getCompositionDisplay() const {
+        return mCompositionDisplay;
+    }
 
     bool isVirtual() const { return mIsVirtual; }
     bool isPrimary() const { return mIsPrimary; }
 
     // isSecure indicates whether this display can be trusted to display
     // secure surfaces.
-    bool isSecure() const { return mIsSecure; }
-
-    // Flip the front and back buffers if the back buffer is "dirty".  Might
-    // be instantaneous, might involve copying the frame buffer around.
-    void flip() const;
+    bool isSecure() const;
 
     int         getWidth() const;
     int         getHeight() const;
@@ -93,44 +90,34 @@
     const Vector< sp<Layer> >& getVisibleLayersSortedByZ() const;
     void                    setLayersNeedingFences(const Vector< sp<Layer> >& layers);
     const Vector< sp<Layer> >& getLayersNeedingFences() const;
-    Region                  getDirtyRegion(bool repaintEverything) const;
 
     void                    setLayerStack(uint32_t stack);
     void                    setDisplaySize(const int newWidth, const int newHeight);
     void                    setProjection(int orientation, const Rect& viewport, const Rect& frame);
 
     int                     getOrientation() const { return mOrientation; }
-    uint32_t                getOrientationTransform() const;
     static uint32_t         getPrimaryDisplayOrientationTransform();
-    const ui::Transform&   getTransform() const { return mGlobalTransform; }
-    const Rect              getViewport() const { return mViewport; }
-    const Rect              getFrame() const { return mFrame; }
-    const Rect&             getScissor() const { return mScissor; }
-    bool                    needsFiltering() const { return mNeedsFiltering; }
+    const ui::Transform& getTransform() const;
+    const Rect& getViewport() const;
+    const Rect& getFrame() const;
+    const Rect& getScissor() const;
+    bool needsFiltering() const;
+    uint32_t getLayerStack() const;
 
-    uint32_t                getLayerStack() const { return mLayerStack; }
-
-    const std::optional<DisplayId>& getId() const { return mId; }
+    const std::optional<DisplayId>& getId() const;
     const wp<IBinder>& getDisplayToken() const { return mDisplayToken; }
     int32_t getSequenceId() const { return mSequenceId; }
 
-    int32_t getSupportedPerFrameMetadata() const { return mSupportedPerFrameMetadata; }
+    const Region& getUndefinedRegion() const;
 
-    // We pass in mustRecompose so we can keep VirtualDisplaySurface's state
-    // machine happy without actually queueing a buffer if nothing has changed
-    status_t beginFrame(bool mustRecompose) const;
-    status_t prepareFrame(HWComposer& hwc, std::vector<CompositionInfo>& compositionInfo);
+    int32_t getSupportedPerFrameMetadata() const;
 
-    bool hasWideColorGamut() const { return mHasWideColorGamut; }
+    bool hasWideColorGamut() const;
     // Whether h/w composer has native support for specific HDR type.
-    bool hasHDR10PlusSupport() const { return mHasHdr10Plus; }
-    bool hasHDR10Support() const { return mHasHdr10; }
-    bool hasHLGSupport() const { return mHasHLG; }
-    bool hasDolbyVisionSupport() const { return mHasDolbyVision; }
-
-    // Return true if the HDR dataspace is supported but
-    // there is no corresponding color mode.
-    bool hasLegacyHdrSupport(ui::Dataspace dataspace) const;
+    bool hasHDR10PlusSupport() const;
+    bool hasHDR10Support() const;
+    bool hasHLGSupport() const;
+    bool hasDolbyVisionSupport() const;
 
     // The returned HdrCapabilities is the combination of HDR capabilities from
     // hardware composer and RenderEngine. When the DisplayDevice supports wide
@@ -138,41 +125,17 @@
     // color space for both PQ and HLG HDR contents. The minimum and maximum
     // luminance will be set to sDefaultMinLumiance and sDefaultMaxLumiance
     // respectively if hardware composer doesn't return meaningful values.
-    const HdrCapabilities& getHdrCapabilities() const { return mHdrCapabilities; }
+    const HdrCapabilities& getHdrCapabilities() const;
 
     // Return true if intent is supported by the display.
     bool hasRenderIntent(ui::RenderIntent intent) const;
 
-    void getBestColorMode(ui::Dataspace dataspace, ui::RenderIntent intent,
-                          ui::Dataspace* outDataspace, ui::ColorMode* outMode,
-                          ui::RenderIntent* outIntent) const;
-
-    void setProtected(bool useProtected);
-    // Queues the drawn buffer for consumption by HWC.
-    void queueBuffer(HWComposer& hwc);
-    // Allocates a buffer as scratch space for GPU composition
-    sp<GraphicBuffer> dequeueBuffer();
-
-    // called after h/w composer has completed its set() call
-    void onPresentDisplayCompleted();
-
-    Rect getBounds() const {
-        return Rect(mDisplayWidth, mDisplayHeight);
-    }
-    inline Rect bounds() const { return getBounds(); }
+    const Rect& getBounds() const;
+    const Rect& bounds() const { return getBounds(); }
 
     void setDisplayName(const std::string& displayName);
     const std::string& getDisplayName() const { return mDisplayName; }
 
-    // Acquires a new buffer for GPU composition.
-    void readyNewBuffer();
-    // Marks the current buffer has finished, so that it can be presented and
-    // swapped out.
-    void finishBuffer();
-    void setViewportAndProjection() const;
-
-    const sp<Fence>& getClientTargetAcquireFence() const;
-
     /* ------------------------------------------------------------------------
      * Display power mode management.
      */
@@ -180,13 +143,6 @@
     void setPowerMode(int mode);
     bool isPoweredOn() const;
 
-    ui::ColorMode getActiveColorMode() const;
-    void setActiveColorMode(ui::ColorMode mode);
-    ui::RenderIntent getActiveRenderIntent() const;
-    void setActiveRenderIntent(ui::RenderIntent renderIntent);
-    android_color_transform_t getColorTransform() const;
-    void setColorTransform(const mat4& transform);
-    void setCompositionDataSpace(ui::Dataspace dataspace);
     ui::Dataspace getCompositionDataSpace() const;
 
     /* ------------------------------------------------------------------------
@@ -196,7 +152,7 @@
     void setActiveConfig(int mode);
 
     // release HWC resources (if any) for removable displays
-    void disconnect(HWComposer& hwc);
+    void disconnect();
 
     /* ------------------------------------------------------------------------
      * Debugging
@@ -206,29 +162,18 @@
     void dump(std::string& result) const;
 
 private:
+    /*
+     *  Constants, set during initialization
+     */
     const sp<SurfaceFlinger> mFlinger;
     const wp<IBinder> mDisplayToken;
     const int32_t mSequenceId;
 
-    std::optional<DisplayId> mId;
+    const int mDisplayInstallOrientation;
+    const std::shared_ptr<compositionengine::Display> mCompositionDisplay;
 
-    // ANativeWindow this display is rendering into
-    sp<ANativeWindow> mNativeWindow;
-    // Current buffer that this display can render to.
-    sp<GraphicBuffer> mGraphicBuffer;
-    sp<DisplaySurface> mDisplaySurface;
-    // File descriptor indicating that mGraphicBuffer is ready for display, i.e.
-    // that drawing to the buffer is now complete.
-    base::unique_fd mBufferReady;
-
-    int             mDisplayWidth;
-    int             mDisplayHeight;
-    const int       mDisplayInstallOrientation;
-    mutable uint32_t mPageFlipCount;
-    std::string     mDisplayName;
-
+    std::string mDisplayName;
     const bool mIsVirtual;
-    const bool mIsSecure;
 
     /*
      * Can only accessed from the main thread, these members
@@ -243,66 +188,17 @@
     /*
      * Transaction state
      */
+    static uint32_t displayStateOrientationToTransformOrientation(int orientation);
     static status_t orientationToTransfrom(int orientation,
                                            int w, int h, ui::Transform* tr);
 
-    // The identifier of the active layer stack for this display. Several displays
-    // can use the same layer stack: A z-ordered group of layers (sometimes called
-    // "surfaces"). Any given layer can only be on a single layer stack.
-    uint32_t mLayerStack;
-
     int mOrientation;
     static uint32_t sPrimaryDisplayOrientation;
-    // user-provided visible area of the layer stack
-    Rect mViewport;
-    // user-provided rectangle where mViewport gets mapped to
-    Rect mFrame;
-    // pre-computed scissor to apply to the display
-    Rect mScissor;
-    ui::Transform mGlobalTransform;
-    bool mNeedsFiltering;
+
     // Current power mode
     int mPowerMode;
     // Current active config
     int mActiveConfig;
-    // current active color mode
-    ui::ColorMode mActiveColorMode = ui::ColorMode::NATIVE;
-    // Current active render intent.
-    ui::RenderIntent mActiveRenderIntent = ui::RenderIntent::COLORIMETRIC;
-    ui::Dataspace mCompositionDataSpace = ui::Dataspace::UNKNOWN;
-    // Current color transform
-    android_color_transform_t mColorTransform;
-
-    // Need to know if display is wide-color capable or not.
-    // Initialized by SurfaceFlinger when the DisplayDevice is created.
-    // Fed to RenderEngine during composition.
-    bool mHasWideColorGamut;
-    bool mHasHdr10Plus;
-    bool mHasHdr10;
-    bool mHasHLG;
-    bool mHasDolbyVision;
-    HdrCapabilities mHdrCapabilities;
-    const int32_t mSupportedPerFrameMetadata;
-
-    // Mappings from desired Dataspace/RenderIntent to the supported
-    // Dataspace/ColorMode/RenderIntent.
-    using ColorModeKey = uint64_t;
-    struct ColorModeValue {
-        ui::Dataspace dataspace;
-        ui::ColorMode colorMode;
-        ui::RenderIntent renderIntent;
-    };
-
-    static ColorModeKey getColorModeKey(ui::Dataspace dataspace, ui::RenderIntent intent) {
-        return (static_cast<uint64_t>(dataspace) << 32) | static_cast<uint32_t>(intent);
-    }
-    void populateColorModes(
-            const std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>>& hwcColorModes);
-    void addColorMode(
-            const std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>>& hwcColorModes,
-            const ui::ColorMode mode, const ui::RenderIntent intent);
-
-    std::unordered_map<ColorModeKey, ColorModeValue> mColorModes;
 
     // TODO(b/74619554): Remove special cases for primary display.
     const bool mIsPrimary;
@@ -341,7 +237,7 @@
     bool isVirtual{false};
     bool isSecure{false};
     sp<ANativeWindow> nativeWindow;
-    sp<DisplaySurface> displaySurface;
+    sp<compositionengine::DisplaySurface> displaySurface;
     int displayInstallOrientation{DisplayState::eOrientationDefault};
     bool hasWideColorGamut{false};
     HdrCapabilities hdrCapabilities;
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index 2d91c68..8343e5a 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -40,13 +40,12 @@
 
 class BufferHandle {
 public:
-    BufferHandle(const native_handle_t* buffer)
-    {
+    explicit BufferHandle(const native_handle_t* buffer) {
         // nullptr is not a valid handle to HIDL
         mHandle = (buffer) ? buffer : native_handle_init(mStorage, 0, 0);
     }
 
-    operator const hidl_handle&() const
+    operator const hidl_handle&() const // NOLINT(google-explicit-constructor)
     {
         return mHandle;
     }
@@ -80,7 +79,7 @@
         }
     }
 
-    operator const hidl_handle&() const
+    operator const hidl_handle&() const // NOLINT(google-explicit-constructor)
     {
         return mHandle;
     }
@@ -261,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 9d0d8d9..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;
@@ -285,7 +285,7 @@
 // Composer is a wrapper to IComposer, a proxy to server-side composer.
 class Composer final : public Hwc2::Composer {
 public:
-    Composer(const std::string& serviceName);
+    explicit Composer(const std::string& serviceName);
     ~Composer() override;
 
     std::vector<IComposer::Capability> getCapabilities() override;
@@ -418,7 +418,7 @@
 private:
     class CommandWriter : public CommandWriterBase {
     public:
-        CommandWriter(uint32_t initialMaxSize);
+        explicit CommandWriter(uint32_t initialMaxSize);
         ~CommandWriter() override;
 
         void setLayerInfo(uint32_t type, uint32_t appId);
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index 2431dfd..18c524f 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -17,15 +17,15 @@
 #ifndef ANDROID_SF_FRAMEBUFFER_SURFACE_H
 #define ANDROID_SF_FRAMEBUFFER_SURFACE_H
 
-#include "DisplayIdentification.h"
-#include "DisplaySurface.h"
-#include "HWComposerBufferCache.h"
-
 #include <stdint.h>
 #include <sys/types.h>
 
+#include <compositionengine/DisplaySurface.h>
 #include <gui/ConsumerBase.h>
 
+#include "DisplayIdentification.h"
+#include "HWComposerBufferCache.h"
+
 // ---------------------------------------------------------------------------
 namespace android {
 // ---------------------------------------------------------------------------
@@ -36,8 +36,7 @@
 
 // ---------------------------------------------------------------------------
 
-class FramebufferSurface : public ConsumerBase,
-                           public DisplaySurface {
+class FramebufferSurface : public ConsumerBase, public compositionengine::DisplaySurface {
 public:
     FramebufferSurface(HWComposer& hwc, DisplayId displayId,
                        const sp<IGraphicBufferConsumer>& consumer);
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 392b608..68e7876 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -143,8 +143,8 @@
         return error;
     }
 
-    auto display = std::make_unique<Display>(
-            *mComposer.get(), mPowerAdvisor, mCapabilities, displayId, DisplayType::Virtual);
+    auto display = std::make_unique<impl::Display>(*mComposer.get(), mPowerAdvisor, mCapabilities,
+                                                   displayId, DisplayType::Virtual);
     display->setConnected(true);
     *outDisplay = display.get();
     mDisplays.emplace(displayId, std::move(display));
@@ -182,8 +182,8 @@
             return;
         }
 
-        auto newDisplay = std::make_unique<Display>(
-                *mComposer.get(), mPowerAdvisor, mCapabilities, displayId, displayType);
+        auto newDisplay = std::make_unique<impl::Display>(*mComposer.get(), mPowerAdvisor,
+                                                          mCapabilities, displayId, displayType);
         newDisplay->setConnected(true);
         mDisplays.emplace(displayId, std::move(newDisplay));
     } else if (connection == Connection::Disconnected) {
@@ -224,7 +224,36 @@
 }
 
 // Display methods
+Display::~Display() = default;
 
+Display::Config::Config(Display& display, hwc2_config_t id)
+      : mDisplay(display),
+        mId(id),
+        mWidth(-1),
+        mHeight(-1),
+        mVsyncPeriod(-1),
+        mDpiX(-1),
+        mDpiY(-1) {}
+
+Display::Config::Builder::Builder(Display& display, hwc2_config_t id)
+      : mConfig(new Config(display, id)) {}
+
+float Display::Config::Builder::getDefaultDensity() {
+    // Default density is based on TVs: 1080p displays get XHIGH density, lower-
+    // resolution displays get TV density. Maybe eventually we'll need to update
+    // it for 4k displays, though hopefully those will just report accurate DPI
+    // information to begin with. This is also used for virtual displays and
+    // older HWC implementations, so be careful about orientation.
+
+    auto longDimension = std::max(mConfig->mWidth, mConfig->mHeight);
+    if (longDimension >= 1080) {
+        return ACONFIGURATION_DENSITY_XHIGH;
+    } else {
+        return ACONFIGURATION_DENSITY_TV;
+    }
+}
+
+namespace impl {
 Display::Display(android::Hwc2::Composer& composer, android::Hwc2::PowerAdvisor& advisor,
                  const std::unordered_set<Capability>& capabilities, hwc2_display_t id,
                  DisplayType type)
@@ -272,35 +301,7 @@
     }
 }
 
-Display::Config::Config(Display& display, hwc2_config_t id)
-  : mDisplay(display),
-    mId(id),
-    mWidth(-1),
-    mHeight(-1),
-    mVsyncPeriod(-1),
-    mDpiX(-1),
-    mDpiY(-1) {}
-
-Display::Config::Builder::Builder(Display& display, hwc2_config_t id)
-  : mConfig(new Config(display, id)) {}
-
-float Display::Config::Builder::getDefaultDensity() {
-    // Default density is based on TVs: 1080p displays get XHIGH density, lower-
-    // resolution displays get TV density. Maybe eventually we'll need to update
-    // it for 4k displays, though hopefully those will just report accurate DPI
-    // information to begin with. This is also used for virtual displays and
-    // older HWC implementations, so be careful about orientation.
-
-    auto longDimension = std::max(mConfig->mWidth, mConfig->mHeight);
-    if (longDimension >= 1080) {
-        return ACONFIGURATION_DENSITY_XHIGH;
-    } else {
-        return ACONFIGURATION_DENSITY_TV;
-    }
-}
-
 // Required by HWC2 display
-
 Error Display::acceptChanges()
 {
     auto intError = mComposer.acceptDisplayChanges(mId);
@@ -794,6 +795,7 @@
 
     return mLayers.at(id).get();
 }
+} // namespace impl
 
 // Layer methods
 
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index f5cb97e..c1f481a 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -130,16 +130,11 @@
 };
 
 // Convenience C++ class to access hwc2_device_t Display functions directly.
-class Display
-{
+class Display {
 public:
-    Display(android::Hwc2::Composer& composer, android::Hwc2::PowerAdvisor& advisor,
-            const std::unordered_set<Capability>& capabilities,
-            hwc2_display_t id, DisplayType type);
-    ~Display();
+    virtual ~Display();
 
-    class Config
-    {
+    class Config {
     public:
         class Builder
         {
@@ -207,78 +202,136 @@
         float mDpiY;
     };
 
-    // Required by HWC2
+    virtual hwc2_display_t getId() const = 0;
+    virtual bool isConnected() const = 0;
+    virtual void setConnected(bool connected) = 0; // For use by Device only
+    virtual const std::unordered_set<DisplayCapability>& getCapabilities() const = 0;
 
-    [[clang::warn_unused_result]] Error acceptChanges();
-    [[clang::warn_unused_result]] Error createLayer(Layer** outLayer);
-    [[clang::warn_unused_result]] Error destroyLayer(Layer* layer);
-    [[clang::warn_unused_result]] Error getActiveConfig(
-            std::shared_ptr<const Config>* outConfig) const;
-    [[clang::warn_unused_result]] Error getActiveConfigIndex(int* outIndex) const;
-    [[clang::warn_unused_result]] Error getChangedCompositionTypes(
-            std::unordered_map<Layer*, Composition>* outTypes);
-    [[clang::warn_unused_result]] Error getColorModes(
-            std::vector<android::ui::ColorMode>* outModes) const;
+    [[clang::warn_unused_result]] virtual Error acceptChanges() = 0;
+    [[clang::warn_unused_result]] virtual Error createLayer(Layer** outLayer) = 0;
+    [[clang::warn_unused_result]] virtual Error destroyLayer(Layer* layer) = 0;
+    [[clang::warn_unused_result]] virtual Error getActiveConfig(
+            std::shared_ptr<const Config>* outConfig) const = 0;
+    [[clang::warn_unused_result]] virtual Error getActiveConfigIndex(int* outIndex) const = 0;
+    [[clang::warn_unused_result]] virtual Error getChangedCompositionTypes(
+            std::unordered_map<Layer*, Composition>* outTypes) = 0;
+    [[clang::warn_unused_result]] virtual Error getColorModes(
+            std::vector<android::ui::ColorMode>* outModes) const = 0;
     // Returns a bitmask which contains HdrMetadata::Type::*.
-    [[clang::warn_unused_result]] int32_t getSupportedPerFrameMetadata() const;
-    [[clang::warn_unused_result]] Error getRenderIntents(
+    [[clang::warn_unused_result]] virtual int32_t getSupportedPerFrameMetadata() const = 0;
+    [[clang::warn_unused_result]] virtual Error getRenderIntents(
             android::ui::ColorMode colorMode,
-            std::vector<android::ui::RenderIntent>* outRenderIntents) const;
-    [[clang::warn_unused_result]] Error getDataspaceSaturationMatrix(
-            android::ui::Dataspace dataspace, android::mat4* outMatrix);
+            std::vector<android::ui::RenderIntent>* outRenderIntents) const = 0;
+    [[clang::warn_unused_result]] virtual Error getDataspaceSaturationMatrix(
+            android::ui::Dataspace dataspace, android::mat4* outMatrix) = 0;
+
+    // Doesn't call into the HWC2 device, so no Errors are possible
+    virtual std::vector<std::shared_ptr<const Config>> getConfigs() const = 0;
+
+    [[clang::warn_unused_result]] virtual Error getName(std::string* outName) const = 0;
+    [[clang::warn_unused_result]] virtual Error getRequests(
+            DisplayRequest* outDisplayRequests,
+            std::unordered_map<Layer*, LayerRequest>* outLayerRequests) = 0;
+    [[clang::warn_unused_result]] virtual Error getType(DisplayType* outType) const = 0;
+    [[clang::warn_unused_result]] virtual Error supportsDoze(bool* outSupport) const = 0;
+    [[clang::warn_unused_result]] virtual Error getHdrCapabilities(
+            android::HdrCapabilities* outCapabilities) const = 0;
+    [[clang::warn_unused_result]] virtual Error getDisplayedContentSamplingAttributes(
+            android::ui::PixelFormat* outFormat, android::ui::Dataspace* outDataspace,
+            uint8_t* outComponentMask) const = 0;
+    [[clang::warn_unused_result]] virtual Error setDisplayContentSamplingEnabled(
+            bool enabled, uint8_t componentMask, uint64_t maxFrames) const = 0;
+    [[clang::warn_unused_result]] virtual Error getDisplayedContentSample(
+            uint64_t maxFrames, uint64_t timestamp,
+            android::DisplayedFrameStats* outStats) const = 0;
+    [[clang::warn_unused_result]] virtual Error getReleaseFences(
+            std::unordered_map<Layer*, android::sp<android::Fence>>* outFences) const = 0;
+    [[clang::warn_unused_result]] virtual Error present(
+            android::sp<android::Fence>* outPresentFence) = 0;
+    [[clang::warn_unused_result]] virtual Error setActiveConfig(
+            const std::shared_ptr<const Config>& config) = 0;
+    [[clang::warn_unused_result]] virtual Error setClientTarget(
+            uint32_t slot, const android::sp<android::GraphicBuffer>& target,
+            const android::sp<android::Fence>& acquireFence, android::ui::Dataspace dataspace) = 0;
+    [[clang::warn_unused_result]] virtual Error setColorMode(
+            android::ui::ColorMode mode, android::ui::RenderIntent renderIntent) = 0;
+    [[clang::warn_unused_result]] virtual Error setColorTransform(
+            const android::mat4& matrix, android_color_transform_t hint) = 0;
+    [[clang::warn_unused_result]] virtual Error setOutputBuffer(
+            const android::sp<android::GraphicBuffer>& buffer,
+            const android::sp<android::Fence>& releaseFence) = 0;
+    [[clang::warn_unused_result]] virtual Error setPowerMode(PowerMode mode) = 0;
+    [[clang::warn_unused_result]] virtual Error setVsyncEnabled(Vsync enabled) = 0;
+    [[clang::warn_unused_result]] virtual Error validate(uint32_t* outNumTypes,
+                                                         uint32_t* outNumRequests) = 0;
+    [[clang::warn_unused_result]] virtual Error presentOrValidate(
+            uint32_t* outNumTypes, uint32_t* outNumRequests,
+            android::sp<android::Fence>* outPresentFence, uint32_t* state) = 0;
+};
+
+namespace impl {
+
+class Display : public HWC2::Display {
+public:
+    Display(android::Hwc2::Composer& composer, android::Hwc2::PowerAdvisor& advisor,
+            const std::unordered_set<Capability>& capabilities, hwc2_display_t id,
+            DisplayType type);
+    ~Display() override;
+
+    // Required by HWC2
+    Error acceptChanges() override;
+    Error createLayer(Layer** outLayer) override;
+    Error destroyLayer(Layer* layer) override;
+    Error getActiveConfig(std::shared_ptr<const Config>* outConfig) const override;
+    Error getActiveConfigIndex(int* outIndex) const override;
+    Error getChangedCompositionTypes(std::unordered_map<Layer*, Composition>* outTypes) override;
+    Error getColorModes(std::vector<android::ui::ColorMode>* outModes) const override;
+    // Returns a bitmask which contains HdrMetadata::Type::*.
+    int32_t getSupportedPerFrameMetadata() const override;
+    Error getRenderIntents(android::ui::ColorMode colorMode,
+                           std::vector<android::ui::RenderIntent>* outRenderIntents) const override;
+    Error getDataspaceSaturationMatrix(android::ui::Dataspace dataspace,
+                                       android::mat4* outMatrix) override;
 
     // Doesn't call into the HWC2 device, so no errors are possible
-    std::vector<std::shared_ptr<const Config>> getConfigs() const;
+    std::vector<std::shared_ptr<const Config>> getConfigs() const override;
 
-    [[clang::warn_unused_result]] Error getName(std::string* outName) const;
-    [[clang::warn_unused_result]] Error getRequests(
-            DisplayRequest* outDisplayRequests,
-            std::unordered_map<Layer*, LayerRequest>* outLayerRequests);
-    [[clang::warn_unused_result]] Error getType(DisplayType* outType) const;
-    [[clang::warn_unused_result]] Error supportsDoze(bool* outSupport) const;
-    [[clang::warn_unused_result]] Error getHdrCapabilities(
-            android::HdrCapabilities* outCapabilities) const;
-    [[clang::warn_unused_result]] Error getDisplayedContentSamplingAttributes(
-            android::ui::PixelFormat* outFormat, android::ui::Dataspace* outDataspace,
-            uint8_t* outComponentMask) const;
-    [[clang::warn_unused_result]] Error setDisplayContentSamplingEnabled(bool enabled,
-                                                                         uint8_t componentMask,
-                                                                         uint64_t maxFrames) const;
-    [[clang::warn_unused_result]] Error getDisplayedContentSample(
-            uint64_t maxFrames, uint64_t timestamp, android::DisplayedFrameStats* outStats) const;
-    [[clang::warn_unused_result]] Error getReleaseFences(
-            std::unordered_map<Layer*,
-                    android::sp<android::Fence>>* outFences) const;
-    [[clang::warn_unused_result]] Error present(
-            android::sp<android::Fence>* outPresentFence);
-    [[clang::warn_unused_result]] Error setActiveConfig(
-            const std::shared_ptr<const Config>& config);
-    [[clang::warn_unused_result]] Error setClientTarget(
-            uint32_t slot, const android::sp<android::GraphicBuffer>& target,
-            const android::sp<android::Fence>& acquireFence,
-            android::ui::Dataspace dataspace);
-    [[clang::warn_unused_result]] Error setColorMode(
-            android::ui::ColorMode mode,
-            android::ui::RenderIntent renderIntent);
-    [[clang::warn_unused_result]] Error setColorTransform(
-            const android::mat4& matrix, android_color_transform_t hint);
-    [[clang::warn_unused_result]] Error setOutputBuffer(
-            const android::sp<android::GraphicBuffer>& buffer,
-            const android::sp<android::Fence>& releaseFence);
-    [[clang::warn_unused_result]] Error setPowerMode(PowerMode mode);
-    [[clang::warn_unused_result]] Error setVsyncEnabled(Vsync enabled);
-    [[clang::warn_unused_result]] Error validate(uint32_t* outNumTypes,
-            uint32_t* outNumRequests);
-    [[clang::warn_unused_result]] Error presentOrValidate(uint32_t* outNumTypes,
-            uint32_t* outNumRequests,
-            android::sp<android::Fence>* outPresentFence, uint32_t* state);
+    Error getName(std::string* outName) const override;
+    Error getRequests(DisplayRequest* outDisplayRequests,
+                      std::unordered_map<Layer*, LayerRequest>* outLayerRequests) override;
+    Error getType(DisplayType* outType) const override;
+    Error supportsDoze(bool* outSupport) const override;
+    Error getHdrCapabilities(android::HdrCapabilities* outCapabilities) const override;
+    Error getDisplayedContentSamplingAttributes(android::ui::PixelFormat* outFormat,
+                                                android::ui::Dataspace* outDataspace,
+                                                uint8_t* outComponentMask) const override;
+    Error setDisplayContentSamplingEnabled(bool enabled, uint8_t componentMask,
+                                           uint64_t maxFrames) const override;
+    Error getDisplayedContentSample(uint64_t maxFrames, uint64_t timestamp,
+                                    android::DisplayedFrameStats* outStats) const override;
+    Error getReleaseFences(
+            std::unordered_map<Layer*, android::sp<android::Fence>>* outFences) const override;
+    Error present(android::sp<android::Fence>* outPresentFence) override;
+    Error setActiveConfig(const std::shared_ptr<const HWC2::Display::Config>& config) override;
+    Error setClientTarget(uint32_t slot, const android::sp<android::GraphicBuffer>& target,
+                          const android::sp<android::Fence>& acquireFence,
+                          android::ui::Dataspace dataspace) override;
+    Error setColorMode(android::ui::ColorMode mode,
+                       android::ui::RenderIntent renderIntent) override;
+    Error setColorTransform(const android::mat4& matrix, android_color_transform_t hint) override;
+    Error setOutputBuffer(const android::sp<android::GraphicBuffer>& buffer,
+                          const android::sp<android::Fence>& releaseFence) override;
+    Error setPowerMode(PowerMode mode) override;
+    Error setVsyncEnabled(Vsync enabled) override;
+    Error validate(uint32_t* outNumTypes, uint32_t* outNumRequests) override;
+    Error presentOrValidate(uint32_t* outNumTypes, uint32_t* outNumRequests,
+                            android::sp<android::Fence>* outPresentFence, uint32_t* state) override;
 
     // Other Display methods
-
-    hwc2_display_t getId() const { return mId; }
-    bool isConnected() const { return mIsConnected; }
-    void setConnected(bool connected);  // For use by Device only
-    const std::unordered_set<DisplayCapability>& getCapabilities() const {
+    hwc2_display_t getId() const override { return mId; }
+    bool isConnected() const override { return mIsConnected; }
+    void setConnected(bool connected) override; // For use by Device only
+    const std::unordered_set<DisplayCapability>& getCapabilities() const override {
         return mDisplayCapabilities;
     };
 
@@ -309,6 +362,7 @@
     std::unordered_map<hwc2_config_t, std::shared_ptr<const Config>> mConfigs;
     std::unordered_set<DisplayCapability> mDisplayCapabilities;
 };
+} // namespace impl
 
 // Convenience C++ class to access hwc2_device_t Layer functions directly.
 class Layer
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 27d3dc5..1c2853a 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -38,12 +38,16 @@
 #define VDS_LOGV(msg, ...) ALOGV("[%s] " msg, \
         mDisplayName.c_str(), ##__VA_ARGS__)
 
-static const char* dbgCompositionTypeStr(DisplaySurface::CompositionType type) {
+static const char* dbgCompositionTypeStr(compositionengine::DisplaySurface::CompositionType type) {
     switch (type) {
-        case DisplaySurface::COMPOSITION_UNKNOWN: return "UNKNOWN";
-        case DisplaySurface::COMPOSITION_GLES:    return "GLES";
-        case DisplaySurface::COMPOSITION_HWC:     return "HWC";
-        case DisplaySurface::COMPOSITION_MIXED:   return "MIXED";
+        case compositionengine::DisplaySurface::COMPOSITION_UNKNOWN:
+            return "UNKNOWN";
+        case compositionengine::DisplaySurface::COMPOSITION_GLES:
+            return "GLES";
+        case compositionengine::DisplaySurface::COMPOSITION_HWC:
+            return "HWC";
+        case compositionengine::DisplaySurface::COMPOSITION_MIXED:
+            return "MIXED";
         default:                                  return "<INVALID>";
     }
 }
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index 33678df..87ae7dd 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -20,13 +20,13 @@
 #include <optional>
 #include <string>
 
-#include "DisplayIdentification.h"
-#include "DisplaySurface.h"
-#include "HWComposerBufferCache.h"
-
+#include <compositionengine/DisplaySurface.h>
 #include <gui/ConsumerBase.h>
 #include <gui/IGraphicBufferProducer.h>
 
+#include "DisplayIdentification.h"
+#include "HWComposerBufferCache.h"
+
 // ---------------------------------------------------------------------------
 namespace android {
 // ---------------------------------------------------------------------------
@@ -73,7 +73,7 @@
  * the HWC output buffer. When HWC composition is complete, the scratch buffer
  * is released and the output buffer is queued to the sink.
  */
-class VirtualDisplaySurface : public DisplaySurface,
+class VirtualDisplaySurface : public compositionengine::DisplaySurface,
                               public BnGraphicBufferProducer,
                               private ConsumerBase {
 public:
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index ee49610..fde90f4 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);
@@ -109,6 +107,7 @@
     mCurrentState.cornerRadius = 0.0f;
     mCurrentState.api = -1;
     mCurrentState.hasColorTransform = false;
+    mCurrentState.colorDataspace = ui::Dataspace::UNKNOWN;
 
     // drawing state & current state are identical
     mDrawingState = mCurrentState;
@@ -380,7 +379,7 @@
     return size;
 }
 
-Rect Layer::computeInitialCrop(const Rect& windowBounds) const {
+Rect Layer::computeInitialCrop(const sp<const DisplayDevice>& display) const {
     // the crop is the area of the window that gets cropped, but not
     // scaled in any ways.
     const State& s(getDrawingState());
@@ -391,17 +390,12 @@
     // pixels in the buffer.
 
     FloatRect activeCropFloat = computeBounds();
-
-    // If we have valid window boundaries then we need to crop to the window
-    // boundaries in layer space.
-    if (windowBounds.isValid()) {
-        const ui::Transform t = getTransform();
-        // Transform to screen space.
-        activeCropFloat = t.transform(activeCropFloat);
-        activeCropFloat = activeCropFloat.intersect(windowBounds.toFloatRect());
-        // Back to layer space to work with the content crop.
-        activeCropFloat = t.inverse().transform(activeCropFloat);
-    }
+    ui::Transform t = getTransform();
+    // Transform to screen space.
+    activeCropFloat = t.transform(activeCropFloat);
+    activeCropFloat = activeCropFloat.intersect(display->getViewport().toFloatRect());
+    // Back to layer space to work with the content crop.
+    activeCropFloat = t.inverse().transform(activeCropFloat);
     // This needs to be here as transform.transform(Rect) computes the
     // transformed rect and then takes the bounding box of the result before
     // returning. This means
@@ -431,7 +425,7 @@
     cropCoords[3] = vec2(win.right, win.top);
 }
 
-FloatRect Layer::computeCrop(const Rect& windowBounds) const {
+FloatRect Layer::computeCrop(const sp<const DisplayDevice>& display) const {
     // the content crop is the area of the content that gets scaled to the
     // layer's size. This is in buffer space.
     FloatRect crop = getContentCrop().toFloatRect();
@@ -439,7 +433,7 @@
     // In addition there is a WM-specified crop we pull from our drawing state.
     const State& s(getDrawingState());
 
-    Rect activeCrop = computeInitialCrop(windowBounds);
+    Rect activeCrop = computeInitialCrop(display);
     Rect bufferSize = getBufferSize(s);
 
     // Transform the window crop to match the buffer coordinate system,
@@ -599,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;
         }
     }
 
@@ -1315,11 +1311,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) {
@@ -1624,19 +1623,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();
@@ -2087,8 +2093,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());
 
@@ -2100,6 +2104,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) {
@@ -2135,9 +2144,13 @@
     return mRemovedFromCurrentState;
 }
 
-InputWindowInfo Layer::fillInputInfo(const Rect& screenBounds) {
+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();
@@ -2148,20 +2161,21 @@
     }
 
     // Transform layer size to screen space and inset it by surface insets.
-    Rect layerBounds = getCroppedBufferSize(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());
+    }
     layerBounds = t.transform(layerBounds);
     layerBounds.inset(info.surfaceInset, info.surfaceInset, info.surfaceInset, info.surfaceInset);
 
-    // Intersect with screen bounds to shrink the frame by the surface insets. The surface insets
-    // are not set on the screen bounds directly since the surface inset region may already be
-    // cropped by a parent layer.
-    Rect frame;
-    screenBounds.intersect(layerBounds, &frame);
-
-    info.frameLeft = frame.left;
-    info.frameTop = frame.top;
-    info.frameRight = frame.right;
-    info.frameBottom = frame.bottom;
+    // Input coordinate should match the layer bounds.
+    info.frameLeft = layerBounds.left;
+    info.frameTop = layerBounds.top;
+    info.frameRight = layerBounds.right;
+    info.frameBottom = layerBounds.bottom;
 
     // Position the touchable region relative to frame screen location and restrict it to frame
     // bounds.
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 7bc7a82..d30961a 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,8 @@
         mat4 colorTransform;
         bool hasColorTransform;
 
+        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
         // recent callback handle.
         std::deque<sp<CallbackHandle>> callbackHandles;
@@ -211,6 +212,7 @@
     virtual ~Layer();
 
     void setPrimaryDisplayOnly() { mPrimaryDisplayOnly = true; }
+    bool getPrimaryDisplayOnly() const { return mPrimaryDisplayOnly; }
 
     // ------------------------------------------------------------------------
     // Geometry setting functions.
@@ -281,7 +283,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);
@@ -308,6 +310,8 @@
             const std::vector<sp<CallbackHandle>>& /*handles*/) {
         return false;
     };
+    virtual bool setColorAlpha(float /*alpha*/) { return false; };
+    virtual bool setColorDataspace(ui::Dataspace /*dataspace*/) { return false; };
 
     ui::Dataspace getDataSpace() const { return mCurrentDataSpace; }
 
@@ -328,6 +332,9 @@
     uint32_t getTransactionFlags(uint32_t flags);
     uint32_t setTransactionFlags(uint32_t flags);
 
+    // Deprecated, please use compositionengine::Output::belongsInOutput()
+    // instead.
+    // TODO(lpique): Move the remaining callers (screencap) to the new function.
     bool belongsToDisplay(uint32_t layerStack, bool isPrimaryDisplay) const {
         return getLayerStack() == layerStack && (!mPrimaryDisplayOnly || isPrimaryDisplay);
     }
@@ -671,22 +678,12 @@
 
     uint32_t getEffectiveUsage(uint32_t usage) const;
 
-    // Computes the crop applied to this layer. windowBounds is the boundary of
-    // layer-stack space, so the cropping rectangle will be clipped to those
-    // bounds in that space. The crop rectangle is returned in buffer space. If
-    // windowBounds is invalid, then it is ignored.
-    virtual FloatRect computeCrop(const Rect& windowBounds) const;
-
-    // See the above method, but pulls the window boundaries from the display.
-    FloatRect computeCrop(const sp<const DisplayDevice>& display) const {
-        return computeCrop(display->getViewport());
-    }
+    virtual FloatRect computeCrop(const sp<const DisplayDevice>& display) const;
     // Compute the initial crop as specified by parent layers and the
     // SurfaceControl for this layer. Does not include buffer crop from the
     // IGraphicBufferProducer client, as that should not affect child clipping.
     // Returns in screen space.
-    Rect computeInitialCrop(const Rect& windowBounds) const;
-
+    Rect computeInitialCrop(const sp<const DisplayDevice>& display) const;
     /**
      * Setup rounded corners coordinates of this layer, taking into account the layer bounds and
      * crop coordinates, transforming them into layer space.
@@ -773,7 +770,7 @@
     bool mPendingHWCDestroy{false};
     void setInputInfo(const InputWindowInfo& info);
 
-    InputWindowInfo fillInputInfo(const Rect& screenBounds);
+    InputWindowInfo fillInputInfo();
     bool hasInput() const;
 
 protected:
diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp
index b74b901..9b2a6fc 100644
--- a/services/surfaceflinger/Scheduler/DispSync.cpp
+++ b/services/surfaceflinger/Scheduler/DispSync.cpp
@@ -657,6 +657,9 @@
     Mutex::Autolock lock(mMutex);
     nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
     nsecs_t phase = mReferenceTime + mPhase;
+    if (mPeriod == 0) {
+        return 0;
+    }
     return (((now - phase) / mPeriod) + periodOffset + 1) * mPeriod + phase;
 }
 
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index bf925b2..52abe9c 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -19,8 +19,11 @@
 #include <pthread.h>
 #include <sched.h>
 #include <sys/types.h>
+
 #include <chrono>
 #include <cstdint>
+#include <optional>
+#include <type_traits>
 
 #include <android-base/stringprintf.h>
 
@@ -35,16 +38,68 @@
 #include "EventThread.h"
 
 using namespace std::chrono_literals;
-using android::base::StringAppendF;
-
-// ---------------------------------------------------------------------------
 
 namespace android {
 
-// ---------------------------------------------------------------------------
+using base::StringAppendF;
+using base::StringPrintf;
 
-EventThreadConnection::EventThreadConnection(EventThread* eventThread)
-      : count(-1), mEventThread(eventThread), mChannel(gui::BitTube::DefaultSize) {}
+namespace {
+
+auto vsyncPeriod(VSyncRequest request) {
+    return static_cast<std::underlying_type_t<VSyncRequest>>(request);
+}
+
+std::string toString(VSyncRequest request) {
+    switch (request) {
+        case VSyncRequest::None:
+            return "VSyncRequest::None";
+        case VSyncRequest::Single:
+            return "VSyncRequest::Single";
+        default:
+            return StringPrintf("VSyncRequest::Periodic{period=%d}", vsyncPeriod(request));
+    }
+}
+
+std::string toString(const EventThreadConnection& connection) {
+    return StringPrintf("Connection{%p, %s}", &connection,
+                        toString(connection.vsyncRequest).c_str());
+}
+
+std::string toString(const DisplayEventReceiver::Event& event) {
+    switch (event.header.type) {
+        case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
+            return StringPrintf("Hotplug{displayId=%u, %s}", event.header.id,
+                                event.hotplug.connected ? "connected" : "disconnected");
+        case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
+            return StringPrintf("VSync{displayId=%u, count=%u}", event.header.id,
+                                event.vsync.count);
+        default:
+            return "Event{}";
+    }
+}
+
+DisplayEventReceiver::Event makeHotplug(uint32_t displayId, nsecs_t timestamp, bool connected) {
+    DisplayEventReceiver::Event event;
+    event.header = {DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG, displayId, timestamp};
+    event.hotplug.connected = connected;
+    return event;
+}
+
+DisplayEventReceiver::Event makeVSync(uint32_t displayId, nsecs_t timestamp, uint32_t count) {
+    DisplayEventReceiver::Event event;
+    event.header = {DisplayEventReceiver::DISPLAY_EVENT_VSYNC, displayId, timestamp};
+    event.vsync.count = count;
+    return event;
+}
+
+} // namespace
+
+EventThreadConnection::EventThreadConnection(EventThread* eventThread,
+                                             ResyncCallback resyncCallback)
+      : resyncCallback(std::move(resyncCallback)),
+        mEventThread(eventThread),
+        mChannel(gui::BitTube::DefaultSize) {}
 
 EventThreadConnection::~EventThreadConnection() {
     // do nothing here -- clean-up will happen automatically
@@ -61,8 +116,8 @@
     return NO_ERROR;
 }
 
-status_t EventThreadConnection::setVsyncRate(uint32_t count) {
-    mEventThread->setVsyncRate(count, this);
+status_t EventThreadConnection::setVsyncRate(uint32_t rate) {
+    mEventThread->setVsyncRate(rate, this);
     return NO_ERROR;
 }
 
@@ -88,38 +143,32 @@
 namespace impl {
 
 EventThread::EventThread(std::unique_ptr<VSyncSource> src,
-                         const ResyncWithRateLimitCallback& resyncWithRateLimitCallback,
                          const InterceptVSyncsCallback& interceptVSyncsCallback,
                          const ResetIdleTimerCallback& resetIdleTimerCallback,
                          const char* threadName)
-      : EventThread(nullptr, std::move(src), resyncWithRateLimitCallback, interceptVSyncsCallback,
-                    threadName) {
+      : EventThread(nullptr, std::move(src), interceptVSyncsCallback, threadName) {
     mResetIdleTimer = resetIdleTimerCallback;
 }
 
-EventThread::EventThread(VSyncSource* src, ResyncWithRateLimitCallback resyncWithRateLimitCallback,
-                         InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
-      : EventThread(src, nullptr, resyncWithRateLimitCallback, interceptVSyncsCallback,
-                    threadName) {}
+EventThread::EventThread(VSyncSource* src, InterceptVSyncsCallback interceptVSyncsCallback,
+                         const char* threadName)
+      : EventThread(src, nullptr, interceptVSyncsCallback, threadName) {}
 
 EventThread::EventThread(VSyncSource* src, std::unique_ptr<VSyncSource> uniqueSrc,
-                         ResyncWithRateLimitCallback resyncWithRateLimitCallback,
                          InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
       : mVSyncSource(src),
         mVSyncSourceUnique(std::move(uniqueSrc)),
-        mResyncWithRateLimitCallback(resyncWithRateLimitCallback),
-        mInterceptVSyncsCallback(interceptVSyncsCallback) {
+        mInterceptVSyncsCallback(interceptVSyncsCallback),
+        mThreadName(threadName) {
     if (src == nullptr) {
         mVSyncSource = mVSyncSourceUnique.get();
     }
-    for (auto& event : mVSyncEvent) {
-        event.header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
-        event.header.id = 0;
-        event.header.timestamp = 0;
-        event.vsync.count = 0;
-    }
+    mVSyncSource->setCallback(this);
 
-    mThread = std::thread(&EventThread::threadMain, this);
+    mThread = std::thread([this]() NO_THREAD_SAFETY_ANALYSIS {
+        std::unique_lock<std::mutex> lock(mMutex);
+        threadMain(lock);
+    });
 
     pthread_setname_np(mThread.native_handle(), threadName);
 
@@ -137,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();
@@ -150,8 +201,8 @@
     mVSyncSource->setPhaseOffset(phaseOffset);
 }
 
-sp<EventThreadConnection> EventThread::createEventConnection() const {
-    return new EventThreadConnection(const_cast<EventThread*>(this));
+sp<EventThreadConnection> EventThread::createEventConnection(ResyncCallback resyncCallback) const {
+    return new EventThreadConnection(const_cast<EventThread*>(this), std::move(resyncCallback));
 }
 
 status_t EventThread::registerDisplayEventConnection(const sp<EventThreadConnection>& connection) {
@@ -179,14 +230,17 @@
     }
 }
 
-void EventThread::setVsyncRate(uint32_t count, const sp<EventThreadConnection>& connection) {
-    if (int32_t(count) >= 0) { // server must protect against bad params
-        std::lock_guard<std::mutex> lock(mMutex);
-        const int32_t new_count = (count == 0) ? -1 : count;
-        if (connection->count != new_count) {
-            connection->count = new_count;
-            mCondition.notify_all();
-        }
+void EventThread::setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) {
+    if (static_cast<std::underlying_type_t<VSyncRequest>>(rate) < 0) {
+        return;
+    }
+
+    std::lock_guard<std::mutex> lock(mMutex);
+
+    const auto request = rate == 0 ? VSyncRequest::None : static_cast<VSyncRequest>(rate);
+    if (connection->vsyncRequest != request) {
+        connection->vsyncRequest = request;
+        mCondition.notify_all();
     }
 }
 
@@ -195,255 +249,227 @@
         ATRACE_NAME("resetIdleTimer");
         mResetIdleTimer();
     }
-    if (mResyncWithRateLimitCallback) {
-        mResyncWithRateLimitCallback();
+
+    if (connection->resyncCallback) {
+        connection->resyncCallback();
     }
 
     std::lock_guard<std::mutex> lock(mMutex);
 
-    if (connection->count < 0) {
-        connection->count = 0;
+    if (connection->vsyncRequest == VSyncRequest::None) {
+        connection->vsyncRequest = VSyncRequest::Single;
         mCondition.notify_all();
     }
 }
 
 void EventThread::onScreenReleased() {
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!mUseSoftwareVSync) {
-        // disable reliance on h/w vsync
-        mUseSoftwareVSync = 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 (mUseSoftwareVSync) {
-        // resume use of h/w vsync
-        mUseSoftwareVSync = 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);
-    mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
-    mVSyncEvent[0].header.id = 0;
-    mVSyncEvent[0].header.timestamp = timestamp;
-    mVSyncEvent[0].vsync.count++;
+
+    LOG_FATAL_IF(!mVSyncState);
+    mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count));
     mCondition.notify_all();
 }
 
 void EventThread::onHotplugReceived(DisplayType displayType, bool connected) {
     std::lock_guard<std::mutex> lock(mMutex);
 
-    DisplayEventReceiver::Event event;
-    event.header.type = DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG;
-    event.header.id = displayType == DisplayType::Primary ? 0 : 1;
-    event.header.timestamp = systemTime();
-    event.hotplug.connected = connected;
-
-    mPendingEvents.push(event);
+    const uint32_t displayId = displayType == DisplayType::Primary ? 0 : 1;
+    mPendingEvents.push_back(makeHotplug(displayId, systemTime(), connected));
     mCondition.notify_all();
 }
 
-void EventThread::threadMain() NO_THREAD_SAFETY_ANALYSIS {
-    std::unique_lock<std::mutex> lock(mMutex);
-    while (mKeepRunning) {
-        DisplayEventReceiver::Event event;
-        std::vector<sp<EventThreadConnection>> signalConnections;
-        signalConnections = waitForEventLocked(&lock, &event);
+void EventThread::threadMain(std::unique_lock<std::mutex>& lock) {
+    DisplayEventConsumers consumers;
 
-        // dispatch events to listeners...
-        for (const sp<EventThreadConnection>& conn : signalConnections) {
-            // now see if we still need to report this event
-            status_t err = conn->postEvent(event);
-            if (err == -EAGAIN || err == -EWOULDBLOCK) {
-                // The destination doesn't accept events anymore, it's probably
-                // full. For now, we just drop the events on the floor.
-                // FIXME: Note that some events cannot be dropped and would have
-                // to be re-sent later.
-                // Right-now we don't have the ability to do this.
-                ALOGW("EventThread: dropping event (%08x) for connection %p", event.header.type,
-                      conn.get());
-            } else if (err < 0) {
-                // handle any other error on the pipe as fatal. the only
-                // reasonable thing to do is to clean-up this connection.
-                // The most common error we'll get here is -EPIPE.
-                removeDisplayEventConnectionLocked(conn);
-            }
-        }
-    }
-}
+    while (mState != State::Quit) {
+        std::optional<DisplayEventReceiver::Event> event;
 
-// This will return when (1) a vsync event has been received, and (2) there was
-// at least one connection interested in receiving it when we started waiting.
-std::vector<sp<EventThreadConnection>> EventThread::waitForEventLocked(
-        std::unique_lock<std::mutex>* lock, DisplayEventReceiver::Event* outEvent) {
-    std::vector<sp<EventThreadConnection>> signalConnections;
+        // Determine next event to dispatch.
+        if (!mPendingEvents.empty()) {
+            event = mPendingEvents.front();
+            mPendingEvents.pop_front();
 
-    while (signalConnections.empty() && mKeepRunning) {
-        bool eventPending = false;
-        bool waitForVSync = false;
+            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;
 
-        size_t vsyncCount = 0;
-        nsecs_t timestamp = 0;
-        for (auto& event : mVSyncEvent) {
-            timestamp = event.header.timestamp;
-            if (timestamp) {
-                // we have a vsync event to dispatch
-                if (mInterceptVSyncsCallback) {
-                    mInterceptVSyncsCallback(timestamp);
-                }
-                *outEvent = event;
-                event.header.timestamp = 0;
-                vsyncCount = event.vsync.count;
-                break;
+                case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
+                    if (mInterceptVSyncsCallback) {
+                        mInterceptVSyncsCallback(event->header.timestamp);
+                    }
+                    break;
             }
         }
 
-        if (!timestamp) {
-            // no vsync event, see if there are some other event
-            eventPending = !mPendingEvents.empty();
-            if (eventPending) {
-                // we have some other event to dispatch
-                *outEvent = mPendingEvents.front();
-                mPendingEvents.pop();
-            }
-        }
+        bool vsyncRequested = false;
 
-        // find out connections waiting for events
+        // Find connections that should consume this event.
         auto it = mDisplayEventConnections.begin();
         while (it != mDisplayEventConnections.end()) {
-            sp<EventThreadConnection> connection(it->promote());
-            if (connection != nullptr) {
-                bool added = false;
-                if (connection->count >= 0) {
-                    // we need vsync events because at least
-                    // one connection is waiting for it
-                    waitForVSync = true;
-                    if (timestamp) {
-                        // we consume the event only if it's time
-                        // (ie: we received a vsync event)
-                        if (connection->count == 0) {
-                            // fired this time around
-                            connection->count = -1;
-                            signalConnections.push_back(connection);
-                            added = true;
-                        } else if (connection->count == 1 ||
-                                   (vsyncCount % connection->count) == 0) {
-                            // continuous event, and time to report it
-                            signalConnections.push_back(connection);
-                            added = true;
-                        }
-                    }
+            if (const auto connection = it->promote()) {
+                vsyncRequested |= connection->vsyncRequest != VSyncRequest::None;
+
+                if (event && shouldConsumeEvent(*event, connection)) {
+                    consumers.push_back(connection);
                 }
 
-                if (eventPending && !timestamp && !added) {
-                    // we don't have a vsync event to process
-                    // (timestamp==0), but we have some pending
-                    // messages.
-                    signalConnections.push_back(connection);
-                }
                 ++it;
             } else {
-                // we couldn't promote this reference, the connection has
-                // died, so clean-up!
                 it = mDisplayEventConnections.erase(it);
             }
         }
 
-        // Here we figure out if we need to enable or disable vsyncs
-        if (timestamp && !waitForVSync) {
-            // we received a VSYNC but we have no clients
-            // don't report it, and disable VSYNC events
-            disableVSyncLocked();
-        } else if (!timestamp && waitForVSync) {
-            // 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();
+        if (!consumers.empty()) {
+            dispatchEvent(*event, consumers);
+            consumers.clear();
         }
 
-        // note: !timestamp implies signalConnections.isEmpty(), because we
-        // don't populate signalConnections if there's no vsync pending
-        if (!timestamp && !eventPending) {
-            // wait for something to happen
-            if (waitForVSync) {
-                // This is where we spend most of our time, waiting
-                // for vsync events and new client registrations.
-                //
-                // If the screen is off, we can't use h/w vsync, so we
-                // use a 16ms timeout instead.  It doesn't need to be
-                // precise, we just need to keep feeding our clients.
-                //
-                // We don't want to stall if there's a driver bug, so we
-                // use a (long) timeout when waiting for h/w vsync, and
-                // generate fake events when necessary.
-                bool softwareSync = mUseSoftwareVSync;
-                auto timeout = softwareSync ? 16ms : 1000ms;
-                if (mCondition.wait_for(*lock, timeout) == std::cv_status::timeout) {
-                    if (!softwareSync) {
-                        ALOGW("Timed out waiting for hw vsync; faking it");
-                    }
-                    // FIXME: how do we decide which display id the fake
-                    // vsync came from ?
-                    mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
-                    mVSyncEvent[0].header.id = 0;
-                    mVSyncEvent[0].header.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
-                    mVSyncEvent[0].vsync.count++;
-                }
-            } else {
-                // Nobody is interested in vsync, so we just want to sleep.
-                // h/w vsync should be disabled, so this will wait until we
-                // get a new connection, or an existing connection becomes
-                // interested in receiving vsync again.
-                mCondition.wait(*lock);
+        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) {
+            continue;
+        }
+
+        // Wait for event or client registration/request.
+        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 = mState == State::SyntheticVSync ? 16ms : 1000ms;
+            if (mCondition.wait_for(lock, timeout) == std::cv_status::timeout) {
+                ALOGW_IF(mState == State::VSync, "Faking VSYNC due to driver stall");
+
+                LOG_FATAL_IF(!mVSyncState);
+                mPendingEvents.push_back(makeVSync(mVSyncState->displayId,
+                                                   systemTime(SYSTEM_TIME_MONOTONIC),
+                                                   ++mVSyncState->count));
             }
         }
     }
-
-    // here we're guaranteed to have a timestamp and some connections to signal
-    // (The connections might have dropped out of mDisplayEventConnections
-    // while we were asleep, but we'll still have strong references to them.)
-    return signalConnections;
 }
 
-void EventThread::enableVSyncLocked() {
-    if (!mUseSoftwareVSync) {
-        // never enable h/w VSYNC when screen is off
-        if (!mVsyncEnabled) {
-            mVsyncEnabled = true;
-            mVSyncSource->setCallback(this);
-            mVSyncSource->setVSyncEnabled(true);
-        }
+bool EventThread::shouldConsumeEvent(const DisplayEventReceiver::Event& event,
+                                     const sp<EventThreadConnection>& connection) const {
+    switch (event.header.type) {
+        case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
+            return true;
+
+        case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
+            switch (connection->vsyncRequest) {
+                case VSyncRequest::None:
+                    return false;
+                case VSyncRequest::Single:
+                    connection->vsyncRequest = VSyncRequest::None;
+                    return true;
+                case VSyncRequest::Periodic:
+                    return true;
+                default:
+                    return event.vsync.count % vsyncPeriod(connection->vsyncRequest) == 0;
+            }
+
+        default:
+            return false;
     }
-    mDebugVsyncEnabled = true;
 }
 
-void EventThread::disableVSyncLocked() {
-    if (mVsyncEnabled) {
-        mVsyncEnabled = false;
-        mVSyncSource->setVSyncEnabled(false);
-        mDebugVsyncEnabled = false;
+void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event,
+                                const DisplayEventConsumers& consumers) {
+    for (const auto& consumer : consumers) {
+        switch (consumer->postEvent(event)) {
+            case NO_ERROR:
+                break;
+
+            case -EAGAIN:
+                // TODO: Try again if pipe is full.
+                ALOGW("Failed dispatching %s for %s", toString(event).c_str(),
+                      toString(*consumer).c_str());
+                break;
+
+            default:
+                // Treat EPIPE and other errors as fatal.
+                removeDisplayEventConnectionLocked(consumer);
+        }
     }
 }
 
 void EventThread::dump(std::string& result) const {
     std::lock_guard<std::mutex> lock(mMutex);
-    StringAppendF(&result, "VSYNC state: %s\n", mDebugVsyncEnabled ? "enabled" : "disabled");
-    StringAppendF(&result, "  soft-vsync: %s\n", mUseSoftwareVSync ? "enabled" : "disabled");
-    StringAppendF(&result, "  numListeners=%zu,\n  events-delivered: %u\n",
-                  mDisplayEventConnections.size(), mVSyncEvent[0].vsync.count);
-    for (const wp<EventThreadConnection>& weak : mDisplayEventConnections) {
-        sp<EventThreadConnection> connection = weak.promote();
-        StringAppendF(&result, "    %p: count=%d\n", connection.get(),
-                      connection != nullptr ? connection->count : 0);
+
+    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, "  other-events-pending: %zu\n", mPendingEvents.size());
+
+    StringAppendF(&result, "  pending events (count=%zu):\n", mPendingEvents.size());
+    for (const auto& event : mPendingEvents) {
+        StringAppendF(&result, "    %s\n", toString(event).c_str());
+    }
+
+    StringAppendF(&result, "  connections (count=%zu):\n", mDisplayEventConnections.size());
+    for (const auto& ptr : mDisplayEventConnections) {
+        if (const auto connection = ptr.promote()) {
+            StringAppendF(&result, "    %s\n", toString(*connection).c_str());
+        }
+    }
+}
+
+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
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index e110488..89b799e 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -18,11 +18,11 @@
 
 #include <sys/types.h>
 
-#include <array>
 #include <condition_variable>
 #include <cstdint>
+#include <deque>
 #include <mutex>
-#include <queue>
+#include <optional>
 #include <thread>
 #include <vector>
 
@@ -44,6 +44,15 @@
 
 // ---------------------------------------------------------------------------
 
+using ResyncCallback = std::function<void()>;
+
+enum class VSyncRequest {
+    None = -1,
+    Single = 0,
+    Periodic = 1,
+    // Subsequent values are periods.
+};
+
 class VSyncSource {
 public:
     class Callback {
@@ -60,22 +69,22 @@
 
 class EventThreadConnection : public BnDisplayEventConnection {
 public:
-    explicit EventThreadConnection(EventThread* eventThread);
+    EventThreadConnection(EventThread* eventThread, ResyncCallback resyncCallback);
     virtual ~EventThreadConnection();
 
     virtual status_t postEvent(const DisplayEventReceiver::Event& event);
 
     status_t stealReceiveChannel(gui::BitTube* outChannel) override;
-    status_t setVsyncRate(uint32_t count) override;
+    status_t setVsyncRate(uint32_t rate) override;
     void requestNextVsync() override; // asynchronous
     // Requesting Vsync for HWC does not reset the idle timer, since HWC requires a refresh
     // in order to update the configs.
     void requestNextVsyncForHWC();
 
-    // count >= 1 : continuous event. count is the vsync rate
-    // count == 0 : one-shot event that has not fired
-    // count ==-1 : one-shot event that fired this round / disabled
-    int32_t count;
+    // Called in response to requestNextVsync.
+    const ResyncCallback resyncCallback;
+
+    VSyncRequest vsyncRequest = VSyncRequest::None;
 
 private:
     virtual void onFirstRef();
@@ -90,7 +99,8 @@
 
     virtual ~EventThread();
 
-    virtual sp<EventThreadConnection> createEventConnection() const = 0;
+    virtual sp<EventThreadConnection> createEventConnection(
+            ResyncCallback resyncCallback) const = 0;
 
     // called before the screen is turned off from main thread
     virtual void onScreenReleased() = 0;
@@ -107,7 +117,7 @@
 
     virtual status_t registerDisplayEventConnection(
             const sp<EventThreadConnection>& connection) = 0;
-    virtual void setVsyncRate(uint32_t count, const sp<EventThreadConnection>& connection) = 0;
+    virtual void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) = 0;
     // Requests the next vsync. If resetIdleTimer is set to true, it resets the idle timer.
     virtual void requestNextVsync(const sp<EventThreadConnection>& connection,
                                   bool resetIdleTimer) = 0;
@@ -117,23 +127,21 @@
 
 class EventThread : public android::EventThread, private VSyncSource::Callback {
 public:
-    using ResyncWithRateLimitCallback = std::function<void()>;
     using InterceptVSyncsCallback = std::function<void(nsecs_t)>;
     using ResetIdleTimerCallback = std::function<void()>;
 
     // TODO(b/113612090): Once the Scheduler is complete this constructor will become obsolete.
-    EventThread(VSyncSource* src, ResyncWithRateLimitCallback resyncWithRateLimitCallback,
-                InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName);
+    EventThread(VSyncSource* src, InterceptVSyncsCallback interceptVSyncsCallback,
+                const char* threadName);
     EventThread(std::unique_ptr<VSyncSource> src,
-                const ResyncWithRateLimitCallback& resyncWithRateLimitCallback,
                 const InterceptVSyncsCallback& interceptVSyncsCallback,
                 const ResetIdleTimerCallback& resetIdleTimerCallback, const char* threadName);
     ~EventThread();
 
-    sp<EventThreadConnection> createEventConnection() const override;
+    sp<EventThreadConnection> createEventConnection(ResyncCallback resyncCallback) const override;
 
     status_t registerDisplayEventConnection(const sp<EventThreadConnection>& connection) override;
-    void setVsyncRate(uint32_t count, const sp<EventThreadConnection>& connection) override;
+    void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) override;
     void requestNextVsync(const sp<EventThreadConnection>& connection,
                           bool resetIdleTimer) override;
 
@@ -153,21 +161,21 @@
 private:
     friend EventThreadTest;
 
+    using DisplayEventConsumers = std::vector<sp<EventThreadConnection>>;
+
     // TODO(b/113612090): Once the Scheduler is complete this constructor will become obsolete.
     EventThread(VSyncSource* src, std::unique_ptr<VSyncSource> uniqueSrc,
-                ResyncWithRateLimitCallback resyncWithRateLimitCallback,
                 InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName);
 
+    void threadMain(std::unique_lock<std::mutex>& lock) REQUIRES(mMutex);
 
-    void threadMain();
-    std::vector<sp<EventThreadConnection>> waitForEventLocked(std::unique_lock<std::mutex>* lock,
-                                                              DisplayEventReceiver::Event* event)
-            REQUIRES(mMutex);
+    bool shouldConsumeEvent(const DisplayEventReceiver::Event& event,
+                            const sp<EventThreadConnection>& connection) const REQUIRES(mMutex);
+    void dispatchEvent(const DisplayEventReceiver::Event& event,
+                       const DisplayEventConsumers& consumers) REQUIRES(mMutex);
 
     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;
@@ -178,24 +186,45 @@
     // TODO(b/113612090): Once the Scheduler is complete this pointer will become obsolete.
     VSyncSource* mVSyncSource GUARDED_BY(mMutex) = nullptr;
     std::unique_ptr<VSyncSource> mVSyncSourceUnique GUARDED_BY(mMutex) = nullptr;
-    // constants
-    const ResyncWithRateLimitCallback mResyncWithRateLimitCallback;
+
     const InterceptVSyncsCallback mInterceptVSyncsCallback;
+    const char* const mThreadName;
 
     std::thread mThread;
     mutable std::mutex mMutex;
     mutable std::condition_variable mCondition;
 
-    // protected by mLock
     std::vector<wp<EventThreadConnection>> mDisplayEventConnections GUARDED_BY(mMutex);
-    std::queue<DisplayEventReceiver::Event> mPendingEvents GUARDED_BY(mMutex);
-    std::array<DisplayEventReceiver::Event, 2> mVSyncEvent GUARDED_BY(mMutex);
-    bool mUseSoftwareVSync GUARDED_BY(mMutex) = false;
-    bool mVsyncEnabled GUARDED_BY(mMutex) = false;
-    bool mKeepRunning GUARDED_BY(mMutex) = true;
+    std::deque<DisplayEventReceiver::Event> mPendingEvents GUARDED_BY(mMutex);
 
-    // for debugging
-    bool mDebugVsyncEnabled GUARDED_BY(mMutex) = false;
+    // VSYNC state of connected display.
+    struct VSyncState {
+        explicit VSyncState(uint32_t displayId) : displayId(displayId) {}
+
+        const uint32_t displayId;
+
+        // Number of VSYNC events since display was connected.
+        uint32_t count = 0;
+
+        // True if VSYNC should be faked, e.g. when display is off.
+        bool synthetic = false;
+    };
+
+    // 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);
+
+    // State machine for event loop.
+    enum class State {
+        Idle,
+        Quit,
+        SyntheticVSync,
+        VSync,
+    };
+
+    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/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 66f42bb..75a410b 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -85,7 +85,8 @@
     mHandler = new Handler(*this);
 }
 
-void MessageQueue::setEventThread(android::EventThread* eventThread) {
+void MessageQueue::setEventThread(android::EventThread* eventThread,
+                                  ResyncCallback resyncCallback) {
     if (mEventThread == eventThread) {
         return;
     }
@@ -95,7 +96,7 @@
     }
 
     mEventThread = eventThread;
-    mEvents = eventThread->createEventConnection();
+    mEvents = eventThread->createEventConnection(std::move(resyncCallback));
     mEvents->stealReceiveChannel(&mEventTube);
     mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver,
                    this);
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 0bf00b0..56a00c0 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -29,12 +29,12 @@
 #include <private/gui/BitTube.h>
 
 #include "Barrier.h"
+#include "EventThread.h"
 
 #include <functional>
 
 namespace android {
 
-class EventThread;
 class SurfaceFlinger;
 
 // ---------------------------------------------------------------------------
@@ -86,7 +86,7 @@
 
     virtual void init(const sp<SurfaceFlinger>& flinger) = 0;
     // TODO(akrulec): Remove this function once everything is migrated to Scheduler.
-    virtual void setEventThread(EventThread* events) = 0;
+    virtual void setEventThread(EventThread* events, ResyncCallback resyncCallback) = 0;
     virtual void setEventConnection(const sp<EventThreadConnection>& connection) = 0;
     virtual void waitMessage() = 0;
     virtual status_t postMessage(const sp<MessageBase>& message, nsecs_t reltime = 0) = 0;
@@ -127,7 +127,7 @@
 public:
     ~MessageQueue() override = default;
     void init(const sp<SurfaceFlinger>& flinger) override;
-    void setEventThread(android::EventThread* events) override;
+    void setEventThread(android::EventThread* events, ResyncCallback resyncCallback) override;
     void setEventConnection(const sp<EventThreadConnection>& connection) override;
 
     void waitMessage() override;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
new file mode 100644
index 0000000..bfe5da5
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -0,0 +1,112 @@
+/*
+ * 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 <algorithm>
+#include <numeric>
+
+#include "android-base/stringprintf.h"
+
+#include "DisplayHardware/HWComposer.h"
+#include "Scheduler/SchedulerUtils.h"
+
+namespace android {
+namespace scheduler {
+
+/**
+ * This class is used to encapsulate configuration for refresh rates. It holds infomation
+ * about available refresh rates on the device, and the mapping between the numbers and human
+ * readable names.
+ */
+class RefreshRateConfigs {
+public:
+    // Enum to indicate which vsync rate to run at. Power saving is intended to be the lowest
+    // (eg. when the screen is in AOD mode or off), default is the old 60Hz, and performance
+    // is the new 90Hz. Eventually we want to have a way for vendors to map these in the configs.
+    enum class RefreshRateType { POWER_SAVING, DEFAULT, PERFORMANCE };
+
+    struct RefreshRate {
+        // Type of the refresh rate.
+        RefreshRateType type;
+        // This config ID corresponds to the position of the config in the vector that is stored
+        // on the device.
+        int configId;
+        // Human readable name of the refresh rate.
+        std::string name;
+    };
+
+    // TODO(b/122916473): Get this information from configs prepared by vendors, instead of
+    // baking them in.
+    explicit RefreshRateConfigs(
+            const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
+        init(configs);
+    }
+    ~RefreshRateConfigs() = default;
+
+    const std::vector<RefreshRate>& getRefreshRates() { return mRefreshRates; }
+
+private:
+    void init(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
+        // This is the rate that HWC encapsulates right now when the device is in DOZE mode.
+        mRefreshRates.push_back(
+                RefreshRate{RefreshRateType::POWER_SAVING, SCREEN_OFF_CONFIG_ID, "ScreenOff"});
+
+        if (configs.size() < 1) {
+            ALOGE("Device does not have valid configs. Config size is 0.");
+            return;
+        }
+
+        // Create a map between config index and vsync period. This is all the info we need
+        // from the configs.
+        std::vector<std::pair<int, nsecs_t>> configIdToVsyncPeriod;
+        for (int i = 0; i < configs.size(); ++i) {
+            configIdToVsyncPeriod.emplace_back(i, configs.at(i)->getVsyncPeriod());
+        }
+
+        std::sort(configIdToVsyncPeriod.begin(), configIdToVsyncPeriod.end(),
+                  [](const std::pair<int, nsecs_t>& a, const std::pair<int, nsecs_t>& b) {
+                      return a.second > b.second;
+                  });
+
+        // When the configs are ordered by the resync rate. We assume that the first one is DEFAULT.
+        nsecs_t vsyncPeriod = configIdToVsyncPeriod[0].second;
+        if (vsyncPeriod != 0) {
+            const float fps = 1e9 / vsyncPeriod;
+            mRefreshRates.push_back(RefreshRate{RefreshRateType::DEFAULT,
+                                                configIdToVsyncPeriod[0].first,
+                                                base::StringPrintf("%2.ffps", fps)});
+        }
+        if (configs.size() < 2) {
+            return;
+        }
+
+        // When the configs are ordered by the resync rate. We assume that the second one is
+        // PERFORMANCE, eg. the higher rate.
+        vsyncPeriod = configIdToVsyncPeriod[1].second;
+        if (vsyncPeriod != 0) {
+            const float fps = 1e9 / vsyncPeriod;
+            mRefreshRates.push_back(RefreshRate{RefreshRateType::PERFORMANCE,
+                                                configIdToVsyncPeriod[1].first,
+                                                base::StringPrintf("%2.ffps", fps)});
+        }
+    }
+
+    std::vector<RefreshRate> mRefreshRates;
+};
+
+} // namespace scheduler
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
new file mode 100644
index 0000000..7e22232
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -0,0 +1,148 @@
+/*
+ * 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 <numeric>
+
+#include "Scheduler/RefreshRateConfigs.h"
+#include "Scheduler/SchedulerUtils.h"
+
+#include "android-base/stringprintf.h"
+#include "utils/Timers.h"
+
+namespace android {
+namespace scheduler {
+
+/**
+ * Class to encapsulate statistics about refresh rates that the display is using. When the power
+ * mode is set to HWC_POWER_MODE_NORMAL, SF is switching between refresh rates that are stored in
+ * the device's configs. Otherwise, we assume the HWC is running in power saving mode under the
+ * hood (eg. the device is in DOZE, or screen off mode).
+ */
+class RefreshRateStats {
+    static constexpr int64_t MS_PER_S = 1000;
+    static constexpr int64_t MS_PER_MIN = 60 * MS_PER_S;
+    static constexpr int64_t MS_PER_HOUR = 60 * MS_PER_MIN;
+    static constexpr int64_t MS_PER_DAY = 24 * MS_PER_HOUR;
+
+public:
+    explicit RefreshRateStats(
+            const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs)
+          : mRefreshRateConfigs(std::make_unique<RefreshRateConfigs>(configs)),
+            mPreviousRecordedTime(systemTime()) {}
+    ~RefreshRateStats() = default;
+
+    // Sets power mode. We only collect the information when the power mode is not
+    // HWC_POWER_MODE_NORMAL. When power mode is HWC_POWER_MODE_NORMAL, we collect the stats based
+    // on config mode.
+    void setPowerMode(int mode) {
+        if (mCurrentPowerMode == mode) {
+            return;
+        }
+        // If power mode is normal, the time is going to be recorded under config modes.
+        if (mode == HWC_POWER_MODE_NORMAL) {
+            mCurrentPowerMode = mode;
+            return;
+        }
+        flushTime();
+        mCurrentPowerMode = mode;
+    }
+
+    // Sets config mode. If the mode has changed, it records how much time was spent in the previous
+    // mode.
+    void setConfigMode(int mode) {
+        if (mCurrentConfigMode == mode) {
+            return;
+        }
+        flushTime();
+        mCurrentConfigMode = mode;
+    }
+
+    // Returns a map between human readable refresh rate and number of seconds the device spent in
+    // that mode.
+    std::unordered_map<std::string, int64_t> getTotalTimes() {
+        // If the power mode is on, then we are probably switching between the config modes. If
+        // it's not then the screen is probably off. Make sure to flush times before printing
+        // them.
+        flushTime();
+
+        std::unordered_map<std::string, int64_t> totalTime;
+        for (auto config : mRefreshRateConfigs->getRefreshRates()) {
+            int64_t totalTimeForConfig = 0;
+            if (mConfigModesTotalTime.find(config.configId) != mConfigModesTotalTime.end()) {
+                totalTimeForConfig = mConfigModesTotalTime.at(config.configId);
+            }
+            totalTime[config.name] = totalTimeForConfig;
+        }
+        return totalTime;
+    }
+
+    // Traverses through the map of config modes and returns how long they've been running in easy
+    // to read format.
+    std::string doDump() {
+        std::ostringstream stream;
+        stream << "+  Refresh rate: running time in seconds\n";
+        for (auto stats : getTotalTimes()) {
+            stream << stats.first.c_str() << ": " << getDateFormatFromMs(stats.second) << "\n";
+        }
+        return stream.str();
+    }
+
+private:
+    void flushTime() {
+        // Normal power mode is counted under different config modes.
+        if (mCurrentPowerMode == HWC_POWER_MODE_NORMAL) {
+            flushTimeForMode(mCurrentConfigMode);
+        } else {
+            flushTimeForMode(SCREEN_OFF_CONFIG_ID);
+        }
+    }
+
+    // Calculates the time that passed in ms between the last time we recorded time and the time
+    // this method was called.
+    void flushTimeForMode(int mode) {
+        nsecs_t currentTime = systemTime();
+        int64_t timeElapsedMs = ns2ms(currentTime - mPreviousRecordedTime);
+        mPreviousRecordedTime = currentTime;
+
+        mConfigModesTotalTime[mode] += timeElapsedMs;
+    }
+
+    // Formats the time in milliseconds into easy to read format.
+    static std::string getDateFormatFromMs(int64_t timeMs) {
+        auto [days, dayRemainderMs] = std::div(timeMs, MS_PER_DAY);
+        auto [hours, hourRemainderMs] = std::div(dayRemainderMs, MS_PER_HOUR);
+        auto [mins, minsRemainderMs] = std::div(hourRemainderMs, MS_PER_MIN);
+        auto [sec, secRemainderMs] = std::div(minsRemainderMs, MS_PER_S);
+        return base::StringPrintf("%" PRId64 "d%02" PRId64 ":%02" PRId64 ":%02" PRId64
+                                  ".%03" PRId64,
+                                  days, hours, mins, sec, secRemainderMs);
+    }
+
+    // Keeps information about refresh rate configs that device has.
+    std::unique_ptr<RefreshRateConfigs> mRefreshRateConfigs;
+
+    int64_t mCurrentConfigMode = 0;
+    int32_t mCurrentPowerMode = HWC_POWER_MODE_OFF;
+
+    std::unordered_map<int /* power mode */, int64_t /* duration in ms */> mConfigModesTotalTime;
+
+    nsecs_t mPreviousRecordedTime;
+};
+
+} // namespace scheduler
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index fec53af..b003451 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -28,7 +28,6 @@
 #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
 #include <android/hardware/configstore/1.2/ISurfaceFlingerConfigs.h>
 #include <configstore/Utils.h>
-
 #include <cutils/properties.h>
 #include <gui/ISurfaceComposer.h>
 #include <ui/DisplayStatInfo.h>
@@ -42,11 +41,13 @@
 #include "IdleTimer.h"
 #include "InjectVSyncSource.h"
 #include "SchedulerUtils.h"
+#include "SurfaceFlingerProperties.h"
 
 namespace android {
 
 using namespace android::hardware::configstore;
 using namespace android::hardware::configstore::V1_0;
+using namespace android::sysprop;
 
 #define RETURN_VALUE_IF_INVALID(value) \
     if (handle == nullptr || mConnections.count(handle->id) == 0) return value
@@ -56,11 +57,8 @@
 std::atomic<int64_t> Scheduler::sNextId = 0;
 
 Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function)
-      : mHasSyncFramework(
-                getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasSyncFramework>(true)),
-        mDispSyncPresentTimeOffset(
-                getInt64<ISurfaceFlingerConfigs,
-                         &ISurfaceFlingerConfigs::presentTimeOffsetFromVSyncNs>(0)),
+      : mHasSyncFramework(running_without_sync_framework(true)),
+        mDispSyncPresentTimeOffset(present_time_offset_from_vsync_ns(0)),
         mPrimaryHWVsyncEnabled(false),
         mHWVsyncAvailable(false) {
     // Note: We create a local temporary with the real DispSync implementation
@@ -86,39 +84,37 @@
 Scheduler::~Scheduler() = default;
 
 sp<Scheduler::ConnectionHandle> Scheduler::createConnection(
-        const std::string& connectionName, int64_t phaseOffsetNs,
-        impl::EventThread::ResyncWithRateLimitCallback 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, resyncCallback,
-                            interceptCallback);
+            makeEventThread(connectionName, mPrimaryDispSync.get(), phaseOffsetNs,
+                            std::move(interceptCallback));
     auto connection = std::make_unique<Connection>(new ConnectionHandle(id),
-                                                   eventThread->createEventConnection(),
+                                                   eventThread->createEventConnection(
+                                                           std::move(resyncCallback)),
                                                    std::move(eventThread));
+
     mConnections.insert(std::make_pair(id, std::move(connection)));
     return mConnections[id]->handle;
 }
 
 std::unique_ptr<EventThread> Scheduler::makeEventThread(
-        const std::string& connectionName, DispSync* dispSync, int64_t phaseOffsetNs,
-        impl::EventThread::ResyncWithRateLimitCallback resyncCallback,
+        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), resyncCallback,
-                                               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(
-        const sp<Scheduler::ConnectionHandle>& handle) {
+        const sp<Scheduler::ConnectionHandle>& handle, ResyncCallback resyncCallback) {
     RETURN_VALUE_IF_INVALID(nullptr);
-    return mConnections[handle->id]->thread->createEventConnection();
+    return mConnections[handle->id]->thread->createEventConnection(std::move(resyncCallback));
 }
 
 EventThread* Scheduler::getEventThread(const sp<Scheduler::ConnectionHandle>& handle) {
@@ -353,4 +349,10 @@
     }
 }
 
+std::string Scheduler::doDump() {
+    std::ostringstream stream;
+    stream << "+  Idle timer interval: " << mSetIdleTimerMs << " ms" << std::endl;
+    return stream.str();
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 3538f31..b717605 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -73,11 +73,11 @@
 
     /** Creates an EventThread connection. */
     sp<ConnectionHandle> createConnection(
-            const std::string& connectionName, int64_t phaseOffsetNs,
-            impl::EventThread::ResyncWithRateLimitCallback resyncCallback,
+            const char* connectionName, int64_t phaseOffsetNs, ResyncCallback resyncCallback,
             impl::EventThread::InterceptVSyncsCallback interceptCallback);
 
-    sp<IDisplayEventConnection> createDisplayEventConnection(const sp<ConnectionHandle>& handle);
+    sp<IDisplayEventConnection> createDisplayEventConnection(const sp<ConnectionHandle>& handle,
+                                                             ResyncCallback resyncCallback);
 
     // Getter methods.
     EventThread* getEventThread(const sp<ConnectionHandle>& handle);
@@ -118,11 +118,12 @@
     void setExpiredIdleTimerCallback(const ExpiredIdleTimerCallback& expiredTimerCallback);
     // Callback that gets invoked once the idle timer is reset.
     void setResetIdleTimerCallback(const ResetIdleTimerCallback& resetTimerCallback);
+    // Returns relevant information about Scheduler for dumpsys purposes.
+    std::string doDump();
 
 protected:
     virtual std::unique_ptr<EventThread> makeEventThread(
-            const std::string& connectionName, DispSync* dispSync, int64_t phaseOffsetNs,
-            impl::EventThread::ResyncWithRateLimitCallback resyncCallback,
+            const char* connectionName, DispSync* dispSync, int64_t phaseOffsetNs,
             impl::EventThread::InterceptVSyncsCallback interceptCallback);
 
 private:
@@ -148,7 +149,7 @@
 
     // The offset in nanoseconds to use, when DispSync timestamps present fence
     // signaling time.
-    const nsecs_t mDispSyncPresentTimeOffset;
+    nsecs_t mDispSyncPresentTimeOffset;
 
     // Each connection has it's own ID. This variable keeps track of the count.
     static std::atomic<int64_t> sNextId;
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.h b/services/surfaceflinger/Scheduler/SchedulerUtils.h
index 17c57db..edd23de 100644
--- a/services/surfaceflinger/Scheduler/SchedulerUtils.h
+++ b/services/surfaceflinger/Scheduler/SchedulerUtils.h
@@ -26,6 +26,11 @@
 // about layers.
 static constexpr size_t ARRAY_SIZE = 30;
 
+// This number is used to have a place holder for when the screen is not NORMAL/ON. Currently
+// the config is not visible to SF, and is completely maintained by HWC. However, we would
+// still like to keep track of time when the device is in this config.
+static constexpr int SCREEN_OFF_CONFIG_ID = -1;
+
 // Calculates the statistical mean (average) in the data structure (array, vector). The
 // function does not modify the contents of the array.
 template <typename T>
diff --git a/services/surfaceflinger/StartPropertySetThread.h b/services/surfaceflinger/StartPropertySetThread.h
index a64c21b..bbdcde2 100644
--- a/services/surfaceflinger/StartPropertySetThread.h
+++ b/services/surfaceflinger/StartPropertySetThread.h
@@ -33,7 +33,7 @@
 // Any property_set() will block during init stage so need to be offloaded
 // to this thread. see b/63844978.
 public:
-    StartPropertySetThread(bool timestampPropertyValue);
+    explicit StartPropertySetThread(bool timestampPropertyValue);
     status_t Start();
 private:
     virtual bool threadLoop();
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index bd16d64..acf0013 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -17,16 +17,18 @@
 // #define LOG_NDEBUG 0
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
-#include <stdint.h>
 #include <sys/types.h>
-#include <algorithm>
 #include <errno.h>
-#include <math.h>
-#include <mutex>
 #include <dlfcn.h>
-#include <inttypes.h>
-#include <stdatomic.h>
+
+#include <algorithm>
+#include <cinttypes>
+#include <cmath>
+#include <cstdint>
+#include <functional>
+#include <mutex>
 #include <optional>
+#include <unordered_map>
 
 #include <cutils/properties.h>
 #include <log/log.h>
@@ -36,6 +38,10 @@
 #include <binder/PermissionCache.h>
 
 #include <compositionengine/CompositionEngine.h>
+#include <compositionengine/Display.h>
+#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/RenderSurface.h>
+#include <compositionengine/impl/OutputCompositionState.h>
 #include <dvr/vr_flinger.h>
 #include <gui/BufferQueue.h>
 #include <gui/GuiConfig.h>
@@ -52,6 +58,7 @@
 #include <ui/GraphicBufferAllocator.h>
 #include <ui/PixelFormat.h>
 #include <ui/UiConfig.h>
+#include <utils/CallStack.h>
 #include <utils/StopWatch.h>
 #include <utils/String16.h>
 #include <utils/String8.h>
@@ -105,11 +112,13 @@
 #include <configstore/Utils.h>
 
 #include <layerproto/LayerProtoParser.h>
+#include "SurfaceFlingerProperties.h"
 
 namespace android {
 
 using namespace android::hardware::configstore;
 using namespace android::hardware::configstore::V1_0;
+using namespace android::sysprop;
 using base::StringAppendF;
 using ui::ColorMode;
 using ui::Dataspace;
@@ -275,67 +284,50 @@
       : SurfaceFlinger(factory, SkipInitialization) {
     ALOGI("SurfaceFlinger is starting");
 
-    vsyncPhaseOffsetNs = getInt64< ISurfaceFlingerConfigs,
-            &ISurfaceFlingerConfigs::vsyncEventPhaseOffsetNs>(1000000);
+    vsyncPhaseOffsetNs = vsync_event_phase_offset_ns(1000000);
 
-    sfVsyncPhaseOffsetNs = getInt64< ISurfaceFlingerConfigs,
-            &ISurfaceFlingerConfigs::vsyncSfEventPhaseOffsetNs>(1000000);
+    sfVsyncPhaseOffsetNs = vsync_sf_event_phase_offset_ns(1000000);
 
-    hasSyncFramework = getBool< ISurfaceFlingerConfigs,
-            &ISurfaceFlingerConfigs::hasSyncFramework>(true);
+    hasSyncFramework = running_without_sync_framework(true);
 
-    dispSyncPresentTimeOffset = getInt64< ISurfaceFlingerConfigs,
-            &ISurfaceFlingerConfigs::presentTimeOffsetFromVSyncNs>(0);
+    dispSyncPresentTimeOffset = present_time_offset_from_vsync_ns(0);
 
-    useHwcForRgbToYuv = getBool< ISurfaceFlingerConfigs,
-            &ISurfaceFlingerConfigs::useHwcForRGBtoYUV>(false);
+    useHwcForRgbToYuv = force_hwc_copy_for_virtual_displays(false);
 
-    maxVirtualDisplaySize = getUInt64<ISurfaceFlingerConfigs,
-            &ISurfaceFlingerConfigs::maxVirtualDisplaySize>(0);
+    maxVirtualDisplaySize = max_virtual_display_dimension(0);
 
     // Vr flinger is only enabled on Daydream ready devices.
-    useVrFlinger = getBool< ISurfaceFlingerConfigs,
-            &ISurfaceFlingerConfigs::useVrFlinger>(false);
+    useVrFlinger = use_vr_flinger(false);
 
-    maxFrameBufferAcquiredBuffers = getInt64< ISurfaceFlingerConfigs,
-            &ISurfaceFlingerConfigs::maxFrameBufferAcquiredBuffers>(2);
+    maxFrameBufferAcquiredBuffers = max_frame_buffer_acquired_buffers(2);
 
-    hasWideColorDisplay =
-            getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasWideColorDisplay>(false);
-    useColorManagement =
-            getBool<V1_2::ISurfaceFlingerConfigs,
-                    &V1_2::ISurfaceFlingerConfigs::useColorManagement>(false);
+    hasWideColorDisplay = has_wide_color_display(false);
 
-    auto surfaceFlingerConfigsServiceV1_2 = V1_2::ISurfaceFlingerConfigs::getService();
-    if (surfaceFlingerConfigsServiceV1_2) {
-        surfaceFlingerConfigsServiceV1_2->getCompositionPreference(
-                [&](auto tmpDefaultDataspace, auto tmpDefaultPixelFormat,
-                    auto tmpWideColorGamutDataspace, auto tmpWideColorGamutPixelFormat) {
-                    defaultCompositionDataspace = tmpDefaultDataspace;
-                    defaultCompositionPixelFormat = tmpDefaultPixelFormat;
-                    wideColorGamutCompositionDataspace = tmpWideColorGamutDataspace;
-                    wideColorGamutCompositionPixelFormat = tmpWideColorGamutPixelFormat;
-                });
-    }
-    mDefaultCompositionDataspace = defaultCompositionDataspace;
-    mWideColorGamutCompositionDataspace = wideColorGamutCompositionDataspace;
+    useColorManagement = use_color_management(false);
 
-    useContextPriority = getBool<ISurfaceFlingerConfigs,
-                                 &ISurfaceFlingerConfigs::useContextPriority>(true);
+    mDefaultCompositionDataspace =
+            static_cast<ui::Dataspace>(default_composition_dataspace(Dataspace::V0_SRGB));
+    mWideColorGamutCompositionDataspace =
+            static_cast<ui::Dataspace>(wcg_composition_dataspace(Dataspace::V0_SRGB));
+    defaultCompositionDataspace = mDefaultCompositionDataspace;
+    wideColorGamutCompositionDataspace = mWideColorGamutCompositionDataspace;
+    defaultCompositionPixelFormat = static_cast<ui::PixelFormat>(
+            default_composition_pixel_format(ui::PixelFormat::RGBA_8888));
+    wideColorGamutCompositionPixelFormat =
+            static_cast<ui::PixelFormat>(wcg_composition_pixel_format(ui::PixelFormat::RGBA_8888));
 
-    V1_1::DisplayOrientation primaryDisplayOrientation =
-        getDisplayOrientation<V1_1::ISurfaceFlingerConfigs,
-                              &V1_1::ISurfaceFlingerConfigs::primaryDisplayOrientation>(
-            V1_1::DisplayOrientation::ORIENTATION_0);
+    useContextPriority = use_context_priority(true);
 
-    switch (primaryDisplayOrientation) {
-        case V1_1::DisplayOrientation::ORIENTATION_90:
+    auto tmpPrimaryDisplayOrientation = primary_display_orientation(
+            SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_0);
+    switch (tmpPrimaryDisplayOrientation) {
+        case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_90:
             SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientation90;
             break;
-        case V1_1::DisplayOrientation::ORIENTATION_180:
+        case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_180:
             SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientation180;
             break;
-        case V1_1::DisplayOrientation::ORIENTATION_270:
+        case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_270:
             SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientation270;
             break;
         default:
@@ -617,6 +609,8 @@
 
     Mutex::Autolock _l(mStateLock);
 
+    auto resyncCallback = makeResyncCallback(std::bind(&SurfaceFlinger::getVsyncPeriod, this));
+
     // start the EventThread
     if (mUseScheduler) {
         mScheduler = getFactory().createScheduler([this](bool enabled) {
@@ -625,12 +619,11 @@
 
         mAppConnectionHandle =
                 mScheduler->createConnection("appConnection", SurfaceFlinger::vsyncPhaseOffsetNs,
-                                             [this] { resyncWithRateLimit(); },
+                                             resyncCallback,
                                              impl::EventThread::InterceptVSyncsCallback());
         mSfConnectionHandle =
                 mScheduler->createConnection("sfConnection", SurfaceFlinger::sfVsyncPhaseOffsetNs,
-                                             [this] { resyncWithRateLimit(); },
-                                             [this](nsecs_t timestamp) {
+                                             resyncCallback, [this](nsecs_t timestamp) {
                                                  mInterceptor->saveVSyncEvent(timestamp);
                                              });
 
@@ -643,7 +636,6 @@
                                                  SurfaceFlinger::vsyncPhaseOffsetNs, true, "app");
         mEventThread =
                 std::make_unique<impl::EventThread>(mEventThreadSource.get(),
-                                                    [this] { resyncWithRateLimit(); },
                                                     impl::EventThread::InterceptVSyncsCallback(),
                                                     "appEventThread");
         mSfEventThreadSource =
@@ -652,12 +644,11 @@
 
         mSFEventThread =
                 std::make_unique<impl::EventThread>(mSfEventThreadSource.get(),
-                                                    [this] { resyncWithRateLimit(); },
                                                     [this](nsecs_t timestamp) {
                                                         mInterceptor->saveVSyncEvent(timestamp);
                                                     },
                                                     "sfEventThread");
-        mEventQueue->setEventThread(mSFEventThread.get());
+        mEventQueue->setEventThread(mSFEventThread.get(), std::move(resyncCallback));
         mVsyncModulator.setEventThreads(mSFEventThread.get(), mEventThread.get());
     }
 
@@ -732,6 +723,8 @@
     if (mUseScheduler) {
         mScheduler->setExpiredIdleTimerCallback([this]() { setRefreshRateTo(60.f /* fps */); });
         mScheduler->setResetIdleTimerCallback([this]() { setRefreshRateTo(90.f /* fps */); });
+        mRefreshRateStats = std::make_unique<scheduler::RefreshRateStats>(
+                getHwComposer().getConfigs(*display->getId()));
     }
 
     ALOGV("Done initializing");
@@ -941,13 +934,15 @@
 
 status_t SurfaceFlinger::setActiveConfigAsync(const sp<IBinder>& displayToken, int mode) {
     ATRACE_NAME("setActiveConfigAsync");
-    postMessageAsync(new LambdaMessage([=] { setActiveConfigInternal(displayToken, mode); }));
+    postMessageAsync(new LambdaMessage(
+            [=]() NO_THREAD_SAFETY_ANALYSIS { setActiveConfigInternal(displayToken, mode); }));
     return NO_ERROR;
 }
 
 status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& displayToken, int mode) {
     ATRACE_NAME("setActiveConfigSync");
-    postMessageSync(new LambdaMessage([&] { setActiveConfigInternal(displayToken, mode); }));
+    postMessageSync(new LambdaMessage(
+            [&]() NO_THREAD_SAFETY_ANALYSIS { setActiveConfigInternal(displayToken, mode); }));
     return NO_ERROR;
 }
 
@@ -980,6 +975,9 @@
         // Don't update config if we are already running in the desired mode.
         return;
     }
+    if (mUseScheduler) {
+        mRefreshRateStats->setConfigMode(mode);
+    }
 
     const auto displayId = display->getId();
     LOG_ALWAYS_FATAL_IF(!displayId);
@@ -988,7 +986,7 @@
     getHwComposer().setActiveConfig(*displayId, mode);
 
     ATRACE_INT("ActiveConfigMode", mode);
-    resyncToHardwareVsync(true);
+    resyncToHardwareVsync(true, getVsyncPeriod());
 }
 
 status_t SurfaceFlinger::getDisplayColorModes(const sp<IBinder>& displayToken,
@@ -1016,40 +1014,11 @@
 
 ColorMode SurfaceFlinger::getActiveColorMode(const sp<IBinder>& displayToken) {
     if (const auto display = getDisplayDevice(displayToken)) {
-        return display->getActiveColorMode();
+        return display->getCompositionDisplay()->getState().colorMode;
     }
     return static_cast<ColorMode>(BAD_VALUE);
 }
 
-void SurfaceFlinger::setActiveColorModeInternal(const sp<DisplayDevice>& display, ColorMode mode,
-                                                Dataspace dataSpace, RenderIntent renderIntent) {
-    if (display->isVirtual()) {
-        ALOGE("%s: Invalid operation on virtual display", __FUNCTION__);
-        return;
-    }
-
-    ColorMode currentMode = display->getActiveColorMode();
-    Dataspace currentDataSpace = display->getCompositionDataSpace();
-    RenderIntent currentRenderIntent = display->getActiveRenderIntent();
-
-    if (mode == currentMode && dataSpace == currentDataSpace &&
-        renderIntent == currentRenderIntent) {
-        return;
-    }
-
-    display->setActiveColorMode(mode);
-    display->setCompositionDataSpace(dataSpace);
-    display->setActiveRenderIntent(renderIntent);
-
-    const auto displayId = display->getId();
-    LOG_ALWAYS_FATAL_IF(!displayId);
-    getHwComposer().setActiveColorMode(*displayId, mode, renderIntent);
-
-    ALOGV("Set active color mode: %s (%d), active render intent: %s (%d), display=%s",
-          decodeColorMode(mode).c_str(), mode, decodeRenderIntent(renderIntent).c_str(),
-          renderIntent, to_string(*displayId).c_str());
-}
-
 status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, ColorMode mode) {
     postMessageSync(new LambdaMessage([&] {
         Vector<ColorMode> modes;
@@ -1068,8 +1037,8 @@
             ALOGW("Attempt to set active color mode %s (%d) for virtual display",
                   decodeColorMode(mode).c_str(), mode);
         } else {
-            setActiveColorModeInternal(display, mode, Dataspace::UNKNOWN,
-                                       RenderIntent::COLORIMETRIC);
+            display->getCompositionDisplay()->setColorMode(mode, Dataspace::UNKNOWN,
+                                                           RenderIntent::COLORIMETRIC);
         }
     }));
 
@@ -1152,6 +1121,42 @@
                                                      outStats);
 }
 
+status_t SurfaceFlinger::getProtectedContentSupport(bool* outSupported) const {
+    if (!outSupported) {
+        return BAD_VALUE;
+    }
+    *outSupported = getRenderEngine().supportsProtectedContent();
+    return NO_ERROR;
+}
+
+status_t SurfaceFlinger::cacheBuffer(const sp<IBinder>& token, const sp<GraphicBuffer>& buffer,
+                                     int32_t* outBufferId) {
+    if (!outBufferId) {
+        return BAD_VALUE;
+    }
+    *outBufferId = mBufferStateLayerCache.add(token, buffer);
+    return NO_ERROR;
+}
+
+status_t SurfaceFlinger::uncacheBuffer(const sp<IBinder>& token, int32_t bufferId) {
+    mBufferStateLayerCache.release(token, bufferId);
+    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);
@@ -1160,6 +1165,8 @@
             return;
         }
 
+        auto resyncCallback = makeResyncCallback(std::bind(&SurfaceFlinger::getVsyncPeriod, this));
+
         // TODO(akrulec): Part of the Injector should be refactored, so that it
         // can be passed to Scheduler.
         if (enable) {
@@ -1167,14 +1174,14 @@
             if (mVSyncInjector.get() == nullptr) {
                 mVSyncInjector = std::make_unique<InjectVSyncSource>();
                 mInjectorEventThread = std::make_unique<
-                        impl::EventThread>(mVSyncInjector.get(), [this] { resyncWithRateLimit(); },
+                        impl::EventThread>(mVSyncInjector.get(),
                                            impl::EventThread::InterceptVSyncsCallback(),
                                            "injEventThread");
             }
-            mEventQueue->setEventThread(mInjectorEventThread.get());
+            mEventQueue->setEventThread(mInjectorEventThread.get(), std::move(resyncCallback));
         } else {
             ALOGV("VSync Injections disabled");
-            mEventQueue->setEventThread(mSFEventThread.get());
+            mEventQueue->setEventThread(mSFEventThread.get(), std::move(resyncCallback));
         }
 
         mInjectVSyncs = enable;
@@ -1231,17 +1238,22 @@
 
 sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
         ISurfaceComposer::VsyncSource vsyncSource) {
+    auto resyncCallback = makeResyncCallback([this] {
+        Mutex::Autolock lock(mStateLock);
+        return getVsyncPeriod();
+    });
+
     if (mUseScheduler) {
         if (vsyncSource == eVsyncSourceSurfaceFlinger) {
-            return mScheduler->createDisplayEventConnection(mSfConnectionHandle);
+            return mScheduler->createDisplayEventConnection(mSfConnectionHandle, resyncCallback);
         } else {
-            return mScheduler->createDisplayEventConnection(mAppConnectionHandle);
+            return mScheduler->createDisplayEventConnection(mAppConnectionHandle, resyncCallback);
         }
     } else {
         if (vsyncSource == eVsyncSourceSurfaceFlinger) {
-            return mSFEventThread->createEventConnection();
+            return mSFEventThread->createEventConnection(resyncCallback);
         } else {
-            return mEventThread->createEventConnection();
+            return mEventThread->createEventConnection(resyncCallback);
         }
     }
 }
@@ -1285,6 +1297,16 @@
     } while (true);
 }
 
+nsecs_t SurfaceFlinger::getVsyncPeriod() const {
+    const auto displayId = getInternalDisplayId();
+    if (!displayId || !getHwComposer().isConnected(*displayId)) {
+        return 0;
+    }
+
+    const auto config = getHwComposer().getActiveConfig(*displayId);
+    return config ? config->getVsyncPeriod() : 0;
+}
+
 void SurfaceFlinger::enableHardwareVsync() {
     Mutex::Autolock _l(mHWVsyncLock);
     if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) {
@@ -1294,7 +1316,7 @@
     }
 }
 
-void SurfaceFlinger::resyncToHardwareVsync(bool makeAvailable) {
+void SurfaceFlinger::resyncToHardwareVsync(bool makeAvailable, nsecs_t period) {
     Mutex::Autolock _l(mHWVsyncLock);
 
     if (makeAvailable) {
@@ -1309,14 +1331,10 @@
         return;
     }
 
-    const auto displayId = getInternalDisplayId();
-    if (!displayId || !getHwComposer().isConnected(*displayId)) {
+    if (period <= 0) {
         return;
     }
 
-    const auto activeConfig = getHwComposer().getActiveConfig(*displayId);
-    const nsecs_t period = activeConfig->getVsyncPeriod();
-
     if (mUseScheduler) {
         mScheduler->setVsyncPeriod(period);
     } else {
@@ -1343,16 +1361,15 @@
     }
 }
 
-void SurfaceFlinger::resyncWithRateLimit() {
+void SurfaceFlinger::VsyncState::resync(const GetVsyncPeriod& getVsyncPeriod) {
     static constexpr nsecs_t kIgnoreDelay = ms2ns(500);
 
-    // No explicit locking is needed here since EventThread holds a lock while calling this method
-    static nsecs_t sLastResyncAttempted = 0;
     const nsecs_t now = systemTime();
-    if (now - sLastResyncAttempted > kIgnoreDelay) {
-        resyncToHardwareVsync(false);
+    const nsecs_t last = lastResyncTime.exchange(now);
+
+    if (now - last > kIgnoreDelay) {
+        flinger.resyncToHardwareVsync(false, getVsyncPeriod());
     }
-    sLastResyncAttempted = now;
 }
 
 void SurfaceFlinger::onVsyncReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
@@ -1552,9 +1569,8 @@
     setPowerModeInternal(display, currentDisplayPowerMode);
 
     // Reset the timing values to account for the period of the swapped in HWC
-    const auto activeConfig = getHwComposer().getActiveConfig(*display->getId());
-    const nsecs_t period = activeConfig->getVsyncPeriod();
-    mAnimFrameTracker.setDisplayRefreshPeriod(period);
+    const nsecs_t vsyncPeriod = getVsyncPeriod();
+    mAnimFrameTracker.setDisplayRefreshPeriod(vsyncPeriod);
 
     // The present fences returned from vr_hwc are not an accurate
     // representation of vsync times.
@@ -1568,10 +1584,10 @@
 
     // Use phase of 0 since phase is not known.
     // Use latency of 0, which will snap to the ideal latency.
-    DisplayStatInfo stats{0 /* vsyncTime */, period /* vsyncPeriod */};
+    DisplayStatInfo stats{0 /* vsyncTime */, vsyncPeriod};
     setCompositorTimingSnapped(stats, 0);
 
-    resyncToHardwareVsync(false);
+    resyncToHardwareVsync(false, vsyncPeriod);
 
     mRepaintEverything = true;
     setTransactionFlags(eDisplayTransactionNeeded);
@@ -1666,9 +1682,11 @@
     postComposition();
 
     mHadClientComposition = false;
-    for (const auto& [token, display] : mDisplays) {
+    for (const auto& [token, displayDevice] : mDisplays) {
+        auto display = displayDevice->getCompositionDisplay();
+        const auto displayId = display->getId();
         mHadClientComposition =
-                mHadClientComposition || getHwComposer().hasClientComposition(display->getId());
+                mHadClientComposition || getHwComposer().hasClientComposition(displayId);
     }
 
     // Setup RenderEngine sync fences if native sync is supported.
@@ -1708,13 +1726,14 @@
     // build the h/w work list
     if (CC_UNLIKELY(mGeometryInvalid)) {
         mGeometryInvalid = false;
-        for (const auto& [token, display] : mDisplays) {
+        for (const auto& [token, displayDevice] : mDisplays) {
+            auto display = displayDevice->getCompositionDisplay();
             const auto displayId = display->getId();
             if (!displayId) {
                 continue;
             }
 
-            const Vector<sp<Layer>>& currentLayers = display->getVisibleLayersSortedByZ();
+            const Vector<sp<Layer>>& currentLayers = displayDevice->getVisibleLayersSortedByZ();
             for (size_t i = 0; i < currentLayers.size(); i++) {
                 const auto& layer = currentLayers[i];
 
@@ -1725,7 +1744,7 @@
                     }
                 }
 
-                layer->setGeometry(display, i);
+                layer->setGeometry(displayDevice, i);
                 if (mDebugDisableHWC || mDebugRegion) {
                     layer->forceClientComposition(*displayId);
                 }
@@ -1734,29 +1753,27 @@
     }
 
     // Set the per-frame data
-    for (const auto& [token, display] : mDisplays) {
+    for (const auto& [token, displayDevice] : mDisplays) {
+        auto display = displayDevice->getCompositionDisplay();
         const auto displayId = display->getId();
         if (!displayId) {
             continue;
         }
+        auto* profile = display->getDisplayColorProfile();
 
         if (mDrawingState.colorMatrixChanged) {
             display->setColorTransform(mDrawingState.colorMatrix);
-            status_t result =
-                    getHwComposer().setColorTransform(*displayId, mDrawingState.colorMatrix);
-            ALOGE_IF(result != NO_ERROR, "Failed to set color transform on display %s: %d",
-                     to_string(*displayId).c_str(), result);
         }
-        for (auto& layer : display->getVisibleLayersSortedByZ()) {
+        for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
             if (layer->isHdrY410()) {
                 layer->forceClientComposition(*displayId);
             } else if ((layer->getDataSpace() == Dataspace::BT2020_PQ ||
                         layer->getDataSpace() == Dataspace::BT2020_ITU_PQ) &&
-                    !display->hasHDR10Support()) {
+                       !profile->hasHDR10Support()) {
                 layer->forceClientComposition(*displayId);
             } else if ((layer->getDataSpace() == Dataspace::BT2020_HLG ||
                         layer->getDataSpace() == Dataspace::BT2020_ITU_HLG) &&
-                    !display->hasHLGSupport()) {
+                       !profile->hasHLGSupport()) {
                 layer->forceClientComposition(*displayId);
             }
 
@@ -1775,16 +1792,17 @@
                 continue;
             }
 
-            layer->setPerFrameData(*displayId, display->getTransform(), display->getViewport(),
-                                   display->getSupportedPerFrameMetadata());
+            const auto& displayState = display->getState();
+            layer->setPerFrameData(*displayId, displayState.transform, displayState.viewport,
+                                   displayDevice->getSupportedPerFrameMetadata());
         }
 
         if (useColorManagement) {
             ColorMode  colorMode;
             Dataspace dataSpace;
             RenderIntent renderIntent;
-            pickColorMode(display, &colorMode, &dataSpace, &renderIntent);
-            setActiveColorModeInternal(display, colorMode, dataSpace, renderIntent);
+            pickColorMode(displayDevice, &colorMode, &dataSpace, &renderIntent);
+            display->setColorMode(colorMode, dataSpace, renderIntent);
         }
     }
 
@@ -1808,34 +1826,37 @@
     }
 }
 
-void SurfaceFlinger::doDebugFlashRegions(const sp<DisplayDevice>& display, bool repaintEverything)
-{
+void SurfaceFlinger::doDebugFlashRegions(const sp<DisplayDevice>& displayDevice,
+                                         bool repaintEverything) {
+    auto display = displayDevice->getCompositionDisplay();
+    const auto& displayState = display->getState();
+
     // is debugging enabled
     if (CC_LIKELY(!mDebugRegion))
         return;
 
-    if (display->isPoweredOn()) {
+    if (displayState.isEnabled) {
         // transform the dirty region into this screen's coordinate space
-        const Region dirtyRegion(display->getDirtyRegion(repaintEverything));
+        const Region dirtyRegion = display->getPhysicalSpaceDirtyRegion(repaintEverything);
         if (!dirtyRegion.isEmpty()) {
             // redraw the whole screen
-            doComposeSurfaces(display);
+            doComposeSurfaces(displayDevice);
 
             // and draw the dirty region
             auto& engine(getRenderEngine());
             engine.fillRegionWithColor(dirtyRegion, 1, 0, 1, 1);
 
-            display->queueBuffer(getHwComposer());
+            display->getRenderSurface()->queueBuffer();
         }
     }
 
-    postFramebuffer(display);
+    postFramebuffer(displayDevice);
 
     if (mDebugRegion > 1) {
         usleep(mDebugRegion * 1000);
     }
 
-    prepareFrame(display);
+    prepareFrame(displayDevice);
 }
 
 void SurfaceFlinger::doTracing(const char* where) {
@@ -1935,37 +1956,99 @@
     getBE().mCompositorTiming.presentLatency = snappedCompositeToPresentLatency;
 }
 
+// debug patch for b/119477596 - add stack guards to catch stack
+// corruptions and disable clang optimizations.
+// The code below is temporary and planned to be removed once stack
+// corruptions are found.
+#pragma clang optimize off
+class StackGuard {
+public:
+    StackGuard(const char* name, const char* func, int line) {
+        guarders.reserve(MIN_CAPACITY);
+        guarders.push_back({this, name, func, line});
+        validate();
+    }
+    ~StackGuard() {
+        for (auto i = guarders.end() - 1; i >= guarders.begin(); --i) {
+            if (i->guard == this) {
+                guarders.erase(i);
+                break;
+            }
+        }
+    }
+
+    static void validate() {
+        for (const auto& guard : guarders) {
+            if (guard.guard->cookie != COOKIE_VALUE) {
+                ALOGE("%s:%d: Stack corruption detected at %s", guard.func, guard.line, guard.name);
+                CallStack stack(LOG_TAG);
+                abort();
+            }
+        }
+    }
+
+private:
+    uint64_t cookie = COOKIE_VALUE;
+    static constexpr uint64_t COOKIE_VALUE = 0xc0febebedeadbeef;
+    static constexpr size_t MIN_CAPACITY = 16;
+
+    struct GuarderElement {
+        StackGuard* guard;
+        const char* name;
+        const char* func;
+        int line;
+    };
+
+    static std::vector<GuarderElement> guarders;
+};
+std::vector<StackGuard::GuarderElement> StackGuard::guarders;
+
+#define DEFINE_STACK_GUARD(__n) StackGuard __n##StackGuard(#__n, __FUNCTION__, __LINE__);
+
+#define ASSERT_ON_STACK_GUARD() StackGuard::validate();
 void SurfaceFlinger::postComposition()
 {
+    DEFINE_STACK_GUARD(begin);
     ATRACE_CALL();
     ALOGV("postComposition");
 
     // Release any buffers which were replaced this frame
     nsecs_t dequeueReadyTime = systemTime();
+    DEFINE_STACK_GUARD(dequeueReadyTime);
     for (auto& layer : mLayersWithQueuedFrames) {
         layer->releasePendingBuffer(dequeueReadyTime);
     }
+    ASSERT_ON_STACK_GUARD();
 
     // |mStateLock| not needed as we are on the main thread
-    const auto display = getDefaultDisplayDeviceLocked();
+    const auto displayDevice = getDefaultDisplayDeviceLocked();
+    DEFINE_STACK_GUARD(displayDevice);
 
     getBE().mGlCompositionDoneTimeline.updateSignalTimes();
     std::shared_ptr<FenceTime> glCompositionDoneFenceTime;
-    if (display && getHwComposer().hasClientComposition(display->getId())) {
+    DEFINE_STACK_GUARD(glCompositionDoneFenceTime);
+
+    if (displayDevice && getHwComposer().hasClientComposition(displayDevice->getId())) {
         glCompositionDoneFenceTime =
-                std::make_shared<FenceTime>(display->getClientTargetAcquireFence());
+                std::make_shared<FenceTime>(displayDevice->getCompositionDisplay()
+                                                    ->getRenderSurface()
+                                                    ->getClientTargetAcquireFence());
         getBE().mGlCompositionDoneTimeline.push(glCompositionDoneFenceTime);
     } else {
         glCompositionDoneFenceTime = FenceTime::NO_FENCE;
     }
 
+    ASSERT_ON_STACK_GUARD();
+
     getBE().mDisplayTimeline.updateSignalTimes();
-    mPreviousPresentFence =
-            display ? getHwComposer().getPresentFence(*display->getId()) : Fence::NO_FENCE;
+    mPreviousPresentFence = displayDevice ? getHwComposer().getPresentFence(*displayDevice->getId())
+                                          : Fence::NO_FENCE;
     auto presentFenceTime = std::make_shared<FenceTime>(mPreviousPresentFence);
+    DEFINE_STACK_GUARD(presentFenceTime);
     getBE().mDisplayTimeline.push(presentFenceTime);
 
     DisplayStatInfo stats;
+    DEFINE_STACK_GUARD(stats);
     if (mUseScheduler) {
         mScheduler->getDisplayStatInfo(&stats);
     } else {
@@ -1973,39 +2056,53 @@
         stats.vsyncPeriod = mPrimaryDispSync->getPeriod();
     }
 
+    ASSERT_ON_STACK_GUARD();
+
     // We use the mRefreshStartTime which might be sampled a little later than
     // when we started doing work for this frame, but that should be okay
     // since updateCompositorTiming has snapping logic.
     updateCompositorTiming(stats, mRefreshStartTime, presentFenceTime);
     CompositorTiming compositorTiming;
+    DEFINE_STACK_GUARD(compositorTiming);
+
     {
         std::lock_guard<std::mutex> lock(getBE().mCompositorTimingLock);
+        DEFINE_STACK_GUARD(lock);
         compositorTiming = getBE().mCompositorTiming;
+
+        ASSERT_ON_STACK_GUARD();
     }
 
     mDrawingState.traverseInZOrder([&](Layer* layer) {
-        bool frameLatched = layer->onPostComposition(display->getId(), glCompositionDoneFenceTime,
-                                                     presentFenceTime, compositorTiming);
+        bool frameLatched =
+                layer->onPostComposition(displayDevice->getId(), glCompositionDoneFenceTime,
+                                         presentFenceTime, compositorTiming);
+        DEFINE_STACK_GUARD(frameLatched);
         if (frameLatched) {
             recordBufferingStats(layer->getName().string(),
                     layer->getOccupancyHistory(false));
         }
+        ASSERT_ON_STACK_GUARD();
     });
 
     if (presentFenceTime->isValid()) {
+        ASSERT_ON_STACK_GUARD();
         if (mUseScheduler) {
             mScheduler->addPresentFence(presentFenceTime);
+            ASSERT_ON_STACK_GUARD();
         } else {
             if (mPrimaryDispSync->addPresentFence(presentFenceTime)) {
                 enableHardwareVsync();
             } else {
                 disableHardwareVsync(false);
             }
+            ASSERT_ON_STACK_GUARD();
         }
     }
 
     if (!hasSyncFramework) {
-        if (display && getHwComposer().isConnected(*display->getId()) && display->isPoweredOn()) {
+        if (displayDevice && getHwComposer().isConnected(*displayDevice->getId()) &&
+            displayDevice->isPoweredOn()) {
             if (mUseScheduler) {
                 mScheduler->enableHardwareVsync();
             } else {
@@ -2014,61 +2111,88 @@
         }
     }
 
+    ASSERT_ON_STACK_GUARD();
+
     if (mAnimCompositionPending) {
         mAnimCompositionPending = false;
 
         if (presentFenceTime->isValid()) {
             mAnimFrameTracker.setActualPresentFence(
                     std::move(presentFenceTime));
-        } else if (display && getHwComposer().isConnected(*display->getId())) {
+
+            ASSERT_ON_STACK_GUARD();
+        } else if (displayDevice && getHwComposer().isConnected(*displayDevice->getId())) {
             // The HWC doesn't support present fences, so use the refresh
             // timestamp instead.
-            const nsecs_t presentTime = getHwComposer().getRefreshTimestamp(*display->getId());
+            const nsecs_t presentTime =
+                    getHwComposer().getRefreshTimestamp(*displayDevice->getId());
+            DEFINE_STACK_GUARD(presentTime);
+
             mAnimFrameTracker.setActualPresentTime(presentTime);
+            ASSERT_ON_STACK_GUARD();
         }
         mAnimFrameTracker.advanceFrame();
     }
 
+    ASSERT_ON_STACK_GUARD();
+
     mTimeStats->incrementTotalFrames();
     if (mHadClientComposition) {
         mTimeStats->incrementClientCompositionFrames();
     }
 
+    ASSERT_ON_STACK_GUARD();
+
     mTimeStats->setPresentFenceGlobal(presentFenceTime);
 
-    if (display && getHwComposer().isConnected(*display->getId()) && !display->isPoweredOn()) {
+    ASSERT_ON_STACK_GUARD();
+
+    if (displayDevice && getHwComposer().isConnected(*displayDevice->getId()) &&
+        !displayDevice->isPoweredOn()) {
         return;
     }
 
     nsecs_t currentTime = systemTime();
+    DEFINE_STACK_GUARD(currentTime);
     if (mHasPoweredOff) {
         mHasPoweredOff = false;
     } else {
         nsecs_t elapsedTime = currentTime - getBE().mLastSwapTime;
+        DEFINE_STACK_GUARD(elapsedTime);
         size_t numPeriods = static_cast<size_t>(elapsedTime / stats.vsyncPeriod);
+        DEFINE_STACK_GUARD(numPeriods);
         if (numPeriods < SurfaceFlingerBE::NUM_BUCKETS - 1) {
             getBE().mFrameBuckets[numPeriods] += elapsedTime;
         } else {
             getBE().mFrameBuckets[SurfaceFlingerBE::NUM_BUCKETS - 1] += elapsedTime;
         }
         getBE().mTotalTime += elapsedTime;
+
+        ASSERT_ON_STACK_GUARD();
     }
     getBE().mLastSwapTime = currentTime;
+    ASSERT_ON_STACK_GUARD();
 
     {
         std::lock_guard lock(mTexturePoolMutex);
+        DEFINE_STACK_GUARD(lock);
         const size_t refillCount = mTexturePoolSize - mTexturePool.size();
+        DEFINE_STACK_GUARD(refillCount);
         if (refillCount > 0) {
             const size_t offset = mTexturePool.size();
             mTexturePool.resize(mTexturePoolSize);
             getRenderEngine().genTextures(refillCount, mTexturePool.data() + offset);
             ATRACE_INT("TexturePoolSize", mTexturePool.size());
         }
+        ASSERT_ON_STACK_GUARD();
     }
 
     mTransactionCompletedThread.addPresentFence(mPreviousPresentFence);
     mTransactionCompletedThread.sendCallbacks();
+
+    ASSERT_ON_STACK_GUARD();
 }
+#pragma clang optimize on // b/119477596
 
 void SurfaceFlinger::rebuildLayerStacks() {
     ATRACE_CALL();
@@ -2092,20 +2216,23 @@
         invalidateHwcGeometry();
 
         for (const auto& pair : mDisplays) {
-            const auto& display = pair.second;
+            const auto& displayDevice = pair.second;
+            auto display = displayDevice->getCompositionDisplay();
+            const auto& displayState = display->getState();
             Region opaqueRegion;
             Region dirtyRegion;
             Vector<sp<Layer>> layersSortedByZ;
             Vector<sp<Layer>> layersNeedingFences;
-            const ui::Transform& tr = display->getTransform();
-            const Rect bounds = display->getBounds();
-            if (display->isPoweredOn()) {
-                computeVisibleRegions(display, dirtyRegion, opaqueRegion);
+            const ui::Transform& tr = displayState.transform;
+            const Rect bounds = displayState.bounds;
+            if (displayState.isEnabled) {
+                computeVisibleRegions(displayDevice, dirtyRegion, opaqueRegion);
 
                 mDrawingState.traverseInZOrder([&](Layer* layer) {
                     bool hwcLayerDestroyed = false;
-                    const auto displayId = display->getId();
-                    if (layer->belongsToDisplay(display->getLayerStack(), display->isPrimary())) {
+                    const auto displayId = displayDevice->getId();
+                    if (display->belongsInOutput(layer->getLayerStack(),
+                                                 layer->getPrimaryDisplayOnly())) {
                         Region drawRegion(tr.transform(
                                 layer->visibleNonTransparentRegion));
                         drawRegion.andSelf(bounds);
@@ -2136,11 +2263,14 @@
                     }
                 });
             }
-            display->setVisibleLayersSortedByZ(layersSortedByZ);
-            display->setLayersNeedingFences(layersNeedingFences);
-            display->undefinedRegion.set(bounds);
-            display->undefinedRegion.subtractSelf(tr.transform(opaqueRegion));
-            display->dirtyRegion.orSelf(dirtyRegion);
+            displayDevice->setVisibleLayersSortedByZ(layersSortedByZ);
+            displayDevice->setLayersNeedingFences(layersNeedingFences);
+
+            Region undefinedRegion{bounds};
+            undefinedRegion.subtractSelf(tr.transform(opaqueRegion));
+
+            display->editState().undefinedRegion = undefinedRegion;
+            display->editState().dirtyRegion.orSelf(dirtyRegion);
         }
     }
 }
@@ -2205,9 +2335,11 @@
     Dataspace hdrDataSpace;
     Dataspace bestDataSpace = getBestDataspace(display, &hdrDataSpace);
 
+    auto* profile = display->getCompositionDisplay()->getDisplayColorProfile();
+
     // respect hdrDataSpace only when there is no legacy HDR support
-    const bool isHdr = hdrDataSpace != Dataspace::UNKNOWN &&
-        !display->hasLegacyHdrSupport(hdrDataSpace);
+    const bool isHdr =
+            hdrDataSpace != Dataspace::UNKNOWN && !profile->hasLegacyHdrSupport(hdrDataSpace);
     if (isHdr) {
         bestDataSpace = hdrDataSpace;
     }
@@ -2226,14 +2358,16 @@
             break;
     }
 
-    display->getBestColorMode(bestDataSpace, intent, outDataSpace, outMode, outRenderIntent);
+    profile->getBestColorMode(bestDataSpace, intent, outDataSpace, outMode, outRenderIntent);
 }
 
-void SurfaceFlinger::beginFrame(const sp<DisplayDevice>& display)
-{
-    bool dirty = !display->getDirtyRegion(false).isEmpty();
-    bool empty = display->getVisibleLayersSortedByZ().size() == 0;
-    bool wasEmpty = !display->lastCompositionHadVisibleLayers;
+void SurfaceFlinger::beginFrame(const sp<DisplayDevice>& displayDevice) {
+    auto display = displayDevice->getCompositionDisplay();
+    const auto& displayState = display->getState();
+
+    bool dirty = !display->getPhysicalSpaceDirtyRegion(false).isEmpty();
+    bool empty = displayDevice->getVisibleLayersSortedByZ().size() == 0;
+    bool wasEmpty = !displayState.lastCompositionHadVisibleLayers;
 
     // If nothing has changed (!dirty), don't recompose.
     // If something changed, but we don't currently have any visible layers,
@@ -2247,44 +2381,50 @@
 
     const char flagPrefix[] = {'-', '+'};
     static_cast<void>(flagPrefix);
-    ALOGV_IF(display->isVirtual(), "%s: %s composition for %s (%cdirty %cempty %cwasEmpty)",
-             __FUNCTION__, mustRecompose ? "doing" : "skipping", display->getDebugName().c_str(),
-             flagPrefix[dirty], flagPrefix[empty], flagPrefix[wasEmpty]);
+    ALOGV_IF(displayDevice->isVirtual(), "%s: %s composition for %s (%cdirty %cempty %cwasEmpty)",
+             __FUNCTION__, mustRecompose ? "doing" : "skipping",
+             displayDevice->getDebugName().c_str(), flagPrefix[dirty], flagPrefix[empty],
+             flagPrefix[wasEmpty]);
 
-    display->beginFrame(mustRecompose);
+    display->getRenderSurface()->beginFrame(mustRecompose);
 
     if (mustRecompose) {
-        display->lastCompositionHadVisibleLayers = !empty;
+        display->editState().lastCompositionHadVisibleLayers = !empty;
     }
 }
 
-void SurfaceFlinger::prepareFrame(const sp<DisplayDevice>& display)
-{
-    if (!display->isPoweredOn()) {
+void SurfaceFlinger::prepareFrame(const sp<DisplayDevice>& displayDevice) {
+    auto display = displayDevice->getCompositionDisplay();
+    const auto& displayState = display->getState();
+
+    if (!displayState.isEnabled) {
         return;
     }
 
-    status_t result = display->prepareFrame(getHwComposer(),
-                                            getBE().mCompositionInfo[display->getDisplayToken()]);
+    status_t result = display->getRenderSurface()->prepareFrame(
+            getBE().mCompositionInfo[displayDevice->getDisplayToken()]);
     ALOGE_IF(result != NO_ERROR, "prepareFrame failed for %s: %d (%s)",
-             display->getDebugName().c_str(), result, strerror(-result));
+             displayDevice->getDebugName().c_str(), result, strerror(-result));
 }
 
-void SurfaceFlinger::doComposition(const sp<DisplayDevice>& display, bool repaintEverything) {
+void SurfaceFlinger::doComposition(const sp<DisplayDevice>& displayDevice, bool repaintEverything) {
     ATRACE_CALL();
     ALOGV("doComposition");
 
-    if (display->isPoweredOn()) {
+    auto display = displayDevice->getCompositionDisplay();
+    const auto& displayState = display->getState();
+
+    if (displayState.isEnabled) {
         // transform the dirty region into this screen's coordinate space
-        const Region dirtyRegion(display->getDirtyRegion(repaintEverything));
+        const Region dirtyRegion = display->getPhysicalSpaceDirtyRegion(repaintEverything);
 
         // repaint the framebuffer (if needed)
-        doDisplayComposition(display, dirtyRegion);
+        doDisplayComposition(displayDevice, dirtyRegion);
 
-        display->dirtyRegion.clear();
-        display->flip();
+        display->editState().dirtyRegion.clear();
+        display->getRenderSurface()->flip();
     }
-    postFramebuffer(display);
+    postFramebuffer(displayDevice);
 }
 
 void SurfaceFlinger::postFrame()
@@ -2299,20 +2439,22 @@
     }
 }
 
-void SurfaceFlinger::postFramebuffer(const sp<DisplayDevice>& display)
-{
+void SurfaceFlinger::postFramebuffer(const sp<DisplayDevice>& displayDevice) {
     ATRACE_CALL();
     ALOGV("postFramebuffer");
 
+    auto display = displayDevice->getCompositionDisplay();
+    const auto& displayState = display->getState();
+    const auto displayId = display->getId();
+
     mPostFramebufferTime = systemTime();
 
-    if (display->isPoweredOn()) {
-        const auto displayId = display->getId();
+    if (displayState.isEnabled) {
         if (displayId) {
             getHwComposer().presentAndGetReleaseFences(*displayId);
         }
-        display->onPresentDisplayCompleted();
-        for (auto& layer : display->getVisibleLayersSortedByZ()) {
+        display->getRenderSurface()->onPresentDisplayCompleted();
+        for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
             sp<Fence> releaseFence = Fence::NO_FENCE;
 
             // The layer buffer from the previous frame (if any) is released
@@ -2329,8 +2471,9 @@
             // client target acquire fence when it is available, even though
             // this is suboptimal.
             if (layer->getCompositionType(displayId) == HWC2::Composition::Client) {
-                releaseFence = Fence::merge("LayerRelease", releaseFence,
-                                            display->getClientTargetAcquireFence());
+                releaseFence =
+                        Fence::merge("LayerRelease", releaseFence,
+                                     display->getRenderSurface()->getClientTargetAcquireFence());
             }
 
             layer->getBE().onLayerDisplayed(releaseFence);
@@ -2339,10 +2482,10 @@
         // We've got a list of layers needing fences, that are disjoint with
         // display->getVisibleLayersSortedByZ.  The best we can do is to
         // supply them with the present fence.
-        if (!display->getLayersNeedingFences().isEmpty()) {
+        if (!displayDevice->getLayersNeedingFences().isEmpty()) {
             sp<Fence> presentFence =
                     displayId ? getHwComposer().getPresentFence(*displayId) : Fence::NO_FENCE;
-            for (auto& layer : display->getLayersNeedingFences()) {
+            for (auto& layer : displayDevice->getLayersNeedingFences()) {
                 layer->getBE().onLayerDisplayed(presentFence);
             }
         }
@@ -2421,9 +2564,20 @@
     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<DisplaySurface>& dispSurface,
+        const DisplayDeviceState& state, const sp<compositionengine::DisplaySurface>& dispSurface,
         const sp<IGraphicBufferProducer>& producer) {
     DisplayDeviceCreationArgs creationArgs(this, displayToken, displayId);
     creationArgs.sequenceId = state.sequenceId;
@@ -2484,8 +2638,8 @@
         defaultColorMode = ColorMode::SRGB;
         defaultDataSpace = Dataspace::V0_SRGB;
     }
-    setActiveColorModeInternal(display, defaultColorMode, defaultDataSpace,
-                               RenderIntent::COLORIMETRIC);
+    display->getCompositionDisplay()->setColorMode(defaultColorMode, defaultDataSpace,
+                                                   RenderIntent::COLORIMETRIC);
     if (!state.isVirtual()) {
         LOG_ALWAYS_FATAL_IF(!displayId);
         display->setActiveConfig(getHwComposer().getActiveConfigIndex(*displayId));
@@ -2522,22 +2676,12 @@
 
                 // in drawing state but not in current state
                 if (const auto display = getDisplayDeviceLocked(draw.keyAt(i))) {
-                    display->disconnect(getHwComposer());
+                    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 {
@@ -2552,7 +2696,7 @@
                     // from the drawing state, so that it get re-added
                     // below.
                     if (const auto display = getDisplayDeviceLocked(displayToken)) {
-                        display->disconnect(getHwComposer());
+                        display->disconnect();
                     }
                     mDisplays.erase(displayToken);
                     mDrawingState.displays.removeItemsAt(i);
@@ -2583,7 +2727,7 @@
             if (draw.indexOfKey(curr.keyAt(i)) < 0) {
                 const DisplayDeviceState& state(curr[i]);
 
-                sp<DisplaySurface> dispSurface;
+                sp<compositionengine::DisplaySurface> dispSurface;
                 sp<IGraphicBufferProducer> producer;
                 sp<IGraphicBufferProducer> bqProducer;
                 sp<IGraphicBufferConsumer> bqConsumer;
@@ -2643,23 +2787,9 @@
                         LOG_ALWAYS_FATAL_IF(!displayId);
 
                         if (displayId == getInternalDisplayId()) {
-                            if (mUseScheduler) {
-                                mScheduler->hotplugReceived(mAppConnectionHandle,
-                                                            EventThread::DisplayType::Primary,
-                                                            true);
-                            } else {
-                                mEventThread->onHotplugReceived(EventThread::DisplayType::Primary,
-                                                                true);
-                            }
+                            dispatchDisplayHotplugEvent(EventThread::DisplayType::Primary, true);
                         } else if (displayId == getExternalDisplayId()) {
-                            if (mUseScheduler) {
-                                mScheduler->hotplugReceived(mAppConnectionHandle,
-                                                            EventThread::DisplayType::External,
-                                                            true);
-                            } else {
-                                mEventThread->onHotplugReceived(EventThread::DisplayType::External,
-                                                                true);
-                            }
+                            dispatchDisplayHotplugEvent(EventThread::DisplayType::External, true);
                         }
                     }
                 }
@@ -2742,7 +2872,9 @@
                 // if not, pick the only display it's on.
                 hintDisplay = nullptr;
                 for (const auto& [token, display] : mDisplays) {
-                    if (layer->belongsToDisplay(display->getLayerStack(), display->isPrimary())) {
+                    if (display->getCompositionDisplay()
+                                ->belongsInOutput(layer->getLayerStack(),
+                                                  layer->getPrimaryDisplayOnly())) {
                         if (hintDisplay) {
                             hintDisplay = nullptr;
                             break;
@@ -2825,8 +2957,7 @@
         if (layer->hasInput()) {
             // When calculating the screen bounds we ignore the transparent region since it may
             // result in an unwanted offset.
-            inputHandles.add(layer->fillInputInfo(
-                    layer->computeScreenBounds(false /* reduceTransparentRegion */)));
+            inputHandles.add(layer->fillInputInfo());
         }
     });
     mInputFlinger->setInputWindows(inputHandles);
@@ -2909,11 +3040,13 @@
     mTransactionCV.broadcast();
 }
 
-void SurfaceFlinger::computeVisibleRegions(const sp<const DisplayDevice>& display,
+void SurfaceFlinger::computeVisibleRegions(const sp<const DisplayDevice>& displayDevice,
                                            Region& outDirtyRegion, Region& outOpaqueRegion) {
     ATRACE_CALL();
     ALOGV("computeVisibleRegions");
 
+    auto display = displayDevice->getCompositionDisplay();
+
     Region aboveOpaqueLayers;
     Region aboveCoveredLayers;
     Region dirty;
@@ -2925,7 +3058,7 @@
         const Layer::State& s(layer->getDrawingState());
 
         // only consider the layers on the given layer stack
-        if (!layer->belongsToDisplay(display->getLayerStack(), display->isPrimary())) {
+        if (!display->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) {
             return;
         }
 
@@ -3049,9 +3182,10 @@
 }
 
 void SurfaceFlinger::invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty) {
-    for (const auto& [token, display] : mDisplays) {
-        if (layer->belongsToDisplay(display->getLayerStack(), display->isPrimary())) {
-            display->dirtyRegion.orSelf(dirty);
+    for (const auto& [token, displayDevice] : mDisplays) {
+        auto display = displayDevice->getCompositionDisplay();
+        if (display->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) {
+            display->editState().dirtyRegion.orSelf(dirty);
         }
     }
 }
@@ -3138,30 +3272,35 @@
     mGeometryInvalid = true;
 }
 
-void SurfaceFlinger::doDisplayComposition(const sp<DisplayDevice>& display,
+void SurfaceFlinger::doDisplayComposition(const sp<DisplayDevice>& displayDevice,
                                           const Region& inDirtyRegion) {
+    auto display = displayDevice->getCompositionDisplay();
+
     // We only need to actually compose the display if:
     // 1) It is being handled by hardware composer, which may need this to
     //    keep its virtual display state machine in sync, or
     // 2) There is work to be done (the dirty region isn't empty)
-    if (!display->getId() && inDirtyRegion.isEmpty()) {
+    if (!displayDevice->getId() && inDirtyRegion.isEmpty()) {
         ALOGV("Skipping display composition");
         return;
     }
 
     ALOGV("doDisplayComposition");
-    if (!doComposeSurfaces(display)) return;
+    if (!doComposeSurfaces(displayDevice)) return;
 
     // swap buffers (presentation)
-    display->queueBuffer(getHwComposer());
+    display->getRenderSurface()->queueBuffer();
 }
 
-bool SurfaceFlinger::doComposeSurfaces(const sp<DisplayDevice>& display) {
+bool SurfaceFlinger::doComposeSurfaces(const sp<DisplayDevice>& displayDevice) {
     ALOGV("doComposeSurfaces");
 
-    const Region bounds(display->bounds());
-    const DisplayRenderArea renderArea(display);
+    auto display = displayDevice->getCompositionDisplay();
+    const auto& displayState = display->getState();
     const auto displayId = display->getId();
+
+    const Region bounds(displayState.bounds);
+    const DisplayRenderArea renderArea(displayDevice);
     const bool hasClientComposition = getHwComposer().hasClientComposition(displayId);
     ATRACE_INT("hasClientComposition", hasClientComposition);
 
@@ -3174,12 +3313,12 @@
     if (hasClientComposition) {
         ALOGV("hasClientComposition");
 
-        sp<GraphicBuffer> buf = display->dequeueBuffer();
+        sp<GraphicBuffer> buf = display->getRenderSurface()->dequeueBuffer();
 
         if (buf == nullptr) {
             ALOGW("Dequeuing buffer for display [%s] failed, bailing out of "
                   "client composition for this frame",
-                  display->getDisplayName().c_str());
+                  displayDevice->getDisplayName().c_str());
             return false;
         }
 
@@ -3189,17 +3328,18 @@
 
         if (fbo->getStatus() != NO_ERROR) {
             ALOGW("Binding buffer for display [%s] failed with status: %d",
-                  display->getDisplayName().c_str(), fbo->getStatus());
+                  displayDevice->getDisplayName().c_str(), fbo->getStatus());
             return false;
         }
 
+        const auto* profile = display->getDisplayColorProfile();
         Dataspace outputDataspace = Dataspace::UNKNOWN;
-        if (display->hasWideColorGamut()) {
-            outputDataspace = display->getCompositionDataSpace();
+        if (profile->hasWideColorGamut()) {
+            outputDataspace = displayState.dataspace;
         }
         getRenderEngine().setOutputDataSpace(outputDataspace);
         getRenderEngine().setDisplayMaxLuminance(
-                display->getHdrCapabilities().getDesiredMaxLuminance());
+                profile->getHdrCapabilities().getDesiredMaxLuminance());
 
         const bool hasDeviceComposition = getHwComposer().hasDeviceComposition(displayId);
         const bool skipClientColorTransform =
@@ -3213,7 +3353,7 @@
             colorMatrix = mDrawingState.colorMatrix;
         }
 
-        display->setViewportAndProjection();
+        display->getRenderSurface()->setViewportAndProjection();
 
         // Never touch the framebuffer if we don't have any framebuffer layers
         if (hasDeviceComposition) {
@@ -3227,10 +3367,10 @@
             // 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(display->getScissor());
+            const Region letterbox = bounds.subtract(displayState.scissor);
 
             // compute the area to clear
-            const Region region = display->undefinedRegion.merge(letterbox);
+            const Region region = displayState.undefinedRegion.merge(letterbox);
 
             // screen is already cleared here
             if (!region.isEmpty()) {
@@ -3239,8 +3379,8 @@
             }
         }
 
-        const Rect& bounds = display->getBounds();
-        const Rect& scissor = display->getScissor();
+        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
@@ -3256,9 +3396,9 @@
      */
 
     ALOGV("Rendering client layers");
-    const ui::Transform& displayTransform = display->getTransform();
+    const ui::Transform& displayTransform = displayState.transform;
     bool firstLayer = true;
-    for (auto& layer : display->getVisibleLayersSortedByZ()) {
+    for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
         const Region clip(bounds.intersect(
                 displayTransform.transform(layer->visibleRegion)));
         ALOGV("Layer: %s", layer->getName().string());
@@ -3307,7 +3447,7 @@
     if (hasClientComposition) {
         getRenderEngine().setColorTransform(mat4());
         getRenderEngine().disableScissor();
-        display->finishBuffer();
+        display->getRenderSurface()->finishBuffer();
         // Clear out error flags here so that we don't wait until next
         // composition to log.
         getRenderEngine().checkErrors();
@@ -3337,9 +3477,11 @@
         }
         if (parent == nullptr && addToCurrentState) {
             mCurrentState.layersSortedByZ.add(lbc);
-        } else if (parent == nullptr || parent->isRemovedFromCurrentState()) {
-                ALOGE("addClientLayer called with a removed parent");
-                lbc->onRemovedFromCurrentState();
+        } else if (parent == nullptr) {
+            lbc->onRemovedFromCurrentState();
+        } else if (parent->isRemovedFromCurrentState()) {
+            parent->addChild(lbc);
+            lbc->onRemovedFromCurrentState();
         } else {
             parent->addChild(lbc);
         }
@@ -3390,8 +3532,8 @@
         auto& [applyToken, transactionQueue] = *it;
 
         while (!transactionQueue.empty()) {
-            const auto& [states, displays, flags] = transactionQueue.front();
-            if (composerStateContainsUnsignaledFences(states)) {
+            const auto& [states, displays, flags, desiredPresentTime] = transactionQueue.front();
+            if (!transactionIsReadyToBeApplied(desiredPresentTime, states)) {
                 break;
             }
             applyTransactionState(states, displays, flags, mInputWindowCommands);
@@ -3425,23 +3567,33 @@
     return false;
 }
 
-bool SurfaceFlinger::composerStateContainsUnsignaledFences(const Vector<ComposerState>& states) {
+bool SurfaceFlinger::transactionIsReadyToBeApplied(int64_t desiredPresentTime,
+                                                   const Vector<ComposerState>& states) {
+    const nsecs_t expectedPresentTime = mPrimaryDispSync->expectedPresentTime();
+    // Do not present if the desiredPresentTime has not passed unless it is more than one second
+    // in the future. We ignore timestamps more than 1 second in the future for stability reasons.
+    if (desiredPresentTime >= 0 && desiredPresentTime >= expectedPresentTime &&
+        desiredPresentTime < expectedPresentTime + s2ns(1)) {
+        return false;
+    }
+
     for (const ComposerState& state : states) {
         const layer_state_t& s = state.state;
         if (!(s.what & layer_state_t::eAcquireFenceChanged)) {
             continue;
         }
         if (s.acquireFence && s.acquireFence->getStatus() == Fence::Status::Unsignaled) {
-            return true;
+            return false;
         }
     }
-    return false;
+    return true;
 }
 
 void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& states,
                                          const Vector<DisplayState>& displays, uint32_t flags,
                                          const sp<IBinder>& applyToken,
-                                         const InputWindowCommands& inputWindowCommands) {
+                                         const InputWindowCommands& inputWindowCommands,
+                                         int64_t desiredPresentTime) {
     ATRACE_CALL();
     Mutex::Autolock _l(mStateLock);
 
@@ -3451,8 +3603,8 @@
 
     // If its TransactionQueue already has a pending TransactionState or if it is pending
     if (mTransactionQueues.find(applyToken) != mTransactionQueues.end() ||
-        composerStateContainsUnsignaledFences(states)) {
-        mTransactionQueues[applyToken].emplace(states, displays, flags);
+        !transactionIsReadyToBeApplied(desiredPresentTime, states)) {
+        mTransactionQueues[applyToken].emplace(states, displays, flags, desiredPresentTime);
         setTransactionFlags(eTransactionNeeded);
         return;
     }
@@ -3671,6 +3823,12 @@
         if (layer->setColor(s.color))
             flags |= eTraversalNeeded;
     }
+    if (what & layer_state_t::eColorAlphaChanged) {
+        if (layer->setColorAlpha(s.colorAlpha)) flags |= eTraversalNeeded;
+    }
+    if (what & layer_state_t::eColorDataspaceChanged) {
+        if (layer->setColorDataspace(s.colorDataspace)) flags |= eTraversalNeeded;
+    }
     if (what & layer_state_t::eColorTransformChanged) {
         if (layer->setColorTransform(s.colorTransform)) {
             flags |= eTraversalNeeded;
@@ -3804,8 +3962,15 @@
         if (layer->setSidebandStream(s.sidebandStream)) flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eInputInfoChanged) {
-        layer->setInputInfo(s.inputInfo);
-        flags |= eTraversalNeeded;
+        if (callingThreadHasUnscopedSurfaceFlingerAccess()) {
+            layer->setInputInfo(s.inputInfo);
+            flags |= eTraversalNeeded;
+        } else {
+            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())) {
@@ -3814,6 +3979,11 @@
             callbackHandles.emplace_back(new CallbackHandle(listener, callbackIds, s.surface));
         }
     }
+    if (what & layer_state_t::eCachedBufferChanged) {
+        sp<GraphicBuffer> buffer =
+                mBufferStateLayerCache.get(s.cachedBuffer.token, s.cachedBuffer.bufferId);
+        if (layer->setBuffer(buffer)) flags |= eTraversalNeeded;
+    }
     if (layer->setTransactionCompletedListeners(callbackHandles)) flags |= eTraversalNeeded;
     // Do not put anything that updates layer state or modifies flags after
     // setTransactionCompletedListener
@@ -3830,13 +4000,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));
@@ -3892,12 +4059,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,
@@ -4022,8 +4192,11 @@
 // ---------------------------------------------------------------------------
 
 void SurfaceFlinger::onInitializeDisplays() {
-    const auto displayToken = getInternalDisplayToken();
-    if (!displayToken) return;
+    const auto display = getDefaultDisplayDeviceLocked();
+    if (!display) return;
+
+    const sp<IBinder> token = display->getDisplayToken().promote();
+    LOG_ALWAYS_FATAL_IF(token == nullptr);
 
     // reset screen orientation and use primary layer stack
     Vector<ComposerState> state;
@@ -4031,7 +4204,7 @@
     DisplayState d;
     d.what = DisplayState::eDisplayProjectionChanged |
              DisplayState::eLayerStackChanged;
-    d.token = displayToken;
+    d.token = token;
     d.layerStack = 0;
     d.orientation = DisplayState::eOrientationDefault;
     d.frame.makeInvalid();
@@ -4039,26 +4212,23 @@
     d.width = 0;
     d.height = 0;
     displays.add(d);
-    setTransactionState(state, displays, 0, nullptr, mInputWindowCommands);
-
-    const auto display = getDisplayDevice(displayToken);
-    if (!display) return;
+    setTransactionState(state, displays, 0, nullptr, mInputWindowCommands, -1);
 
     setPowerModeInternal(display, HWC_POWER_MODE_NORMAL);
 
-    const auto activeConfig = getHwComposer().getActiveConfig(*display->getId());
-    const nsecs_t period = activeConfig->getVsyncPeriod();
-    mAnimFrameTracker.setDisplayRefreshPeriod(period);
+    const nsecs_t vsyncPeriod = getVsyncPeriod();
+    mAnimFrameTracker.setDisplayRefreshPeriod(vsyncPeriod);
 
     // Use phase of 0 since phase is not known.
     // Use latency of 0, which will snap to the ideal latency.
-    DisplayStatInfo stats{0 /* vsyncTime */, period /* vsyncPeriod */};
+    DisplayStatInfo stats{0 /* vsyncTime */, vsyncPeriod};
     setCompositorTimingSnapped(stats, 0);
 }
 
 void SurfaceFlinger::initializeDisplays() {
     // Async since we may be called from the main thread.
-    postMessageAsync(new LambdaMessage([this] { onInitializeDisplays(); }));
+    postMessageAsync(
+            new LambdaMessage([this]() NO_THREAD_SAFETY_ANALYSIS { onInitializeDisplays(); }));
 }
 
 void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, int mode) {
@@ -4092,7 +4262,7 @@
             } else {
                 mEventThread->onScreenAcquired();
             }
-            resyncToHardwareVsync(true);
+            resyncToHardwareVsync(true, getVsyncPeriod());
         }
 
         mVisibleRegionsDirty = true;
@@ -4137,7 +4307,7 @@
             } else {
                 mEventThread->onScreenAcquired();
             }
-            resyncToHardwareVsync(true);
+            resyncToHardwareVsync(true, getVsyncPeriod());
         }
     } else if (mode == HWC_POWER_MODE_DOZE_SUSPEND) {
         // Leave display going to doze
@@ -4161,13 +4331,17 @@
 
     if (display->isPrimary()) {
         mTimeStats->setPowerMode(mode);
+        if (mUseScheduler && mRefreshRateStats) {
+            // Update refresh rate stats.
+            mRefreshRateStats->setPowerMode(mode);
+        }
     }
 
     ALOGD("Finished setting power mode %d on display %s", mode, to_string(*displayId).c_str());
 }
 
 void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) {
-    postMessageSync(new LambdaMessage([&] {
+    postMessageSync(new LambdaMessage([&]() NO_THREAD_SAFETY_ANALYSIS {
         const auto display = getDisplayDevice(displayToken);
         if (!display) {
             ALOGE("Attempt to set power mode %d for invalid display token %p", mode,
@@ -4182,8 +4356,8 @@
 
 // ---------------------------------------------------------------------------
 
-status_t SurfaceFlinger::doDump(int fd, const Vector<String16>& args, bool asProto)
-        NO_THREAD_SAFETY_ANALYSIS {
+status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args,
+                                bool asProto) NO_THREAD_SAFETY_ANALYSIS {
     std::string result;
 
     IPCThreadState* ipc = IPCThreadState::self();
@@ -4207,114 +4381,36 @@
                           strerror(-err), err);
         }
 
-        bool dumpAll = true;
-        size_t index = 0;
-        size_t numArgs = args.size();
+        using namespace std::string_literals;
 
-        if (numArgs) {
-            if ((index < numArgs) &&
-                    (args[index] == String16("--list"))) {
-                index++;
-                listLayersLocked(args, index, result);
-                dumpAll = false;
-            }
+        static const std::unordered_map<std::string, Dumper> dumpers = {
+                {"--clear-layer-stats"s, dumper([this](std::string&) { mLayerStats.clear(); })},
+                {"--disable-layer-stats"s, dumper([this](std::string&) { mLayerStats.disable(); })},
+                {"--display-id"s, dumper(&SurfaceFlinger::dumpDisplayIdentificationData)},
+                {"--dispsync"s, dumper([this](std::string& s) { mPrimaryDispSync->dump(s); })},
+                {"--dump-layer-stats"s, dumper([this](std::string& s) { mLayerStats.dump(s); })},
+                {"--enable-layer-stats"s, dumper([this](std::string&) { mLayerStats.enable(); })},
+                {"--frame-composition"s, dumper(&SurfaceFlinger::dumpFrameCompositionInfo)},
+                {"--frame-events"s, dumper(&SurfaceFlinger::dumpFrameEventsLocked)},
+                {"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)},
+                {"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)},
+                {"--list"s, dumper(&SurfaceFlinger::listLayersLocked)},
+                {"--static-screen"s, dumper(&SurfaceFlinger::dumpStaticScreenStats)},
+                {"--timestats"s, protoDumper(&SurfaceFlinger::dumpTimeStats)},
+                {"--vsync"s, dumper(&SurfaceFlinger::dumpVSync)},
+                {"--wide-color"s, dumper(&SurfaceFlinger::dumpWideColorInfo)},
+        };
 
-            if ((index < numArgs) &&
-                    (args[index] == String16("--latency"))) {
-                index++;
-                dumpStatsLocked(args, index, result);
-                dumpAll = false;
-            }
+        const auto flag = args.empty() ? ""s : std::string(String8(args[0]));
 
-            if ((index < numArgs) &&
-                    (args[index] == String16("--latency-clear"))) {
-                index++;
-                clearStatsLocked(args, index, result);
-                dumpAll = false;
-            }
-
-            if ((index < numArgs) &&
-                    (args[index] == String16("--dispsync"))) {
-                index++;
-                mPrimaryDispSync->dump(result);
-                dumpAll = false;
-            }
-
-            if ((index < numArgs) &&
-                    (args[index] == String16("--static-screen"))) {
-                index++;
-                dumpStaticScreenStats(result);
-                dumpAll = false;
-            }
-
-            if ((index < numArgs) &&
-                    (args[index] == String16("--frame-events"))) {
-                index++;
-                dumpFrameEventsLocked(result);
-                dumpAll = false;
-            }
-
-            if ((index < numArgs) && (args[index] == String16("--wide-color"))) {
-                index++;
-                dumpWideColorInfo(result);
-                dumpAll = false;
-            }
-
-            if ((index < numArgs) &&
-                (args[index] == String16("--enable-layer-stats"))) {
-                index++;
-                mLayerStats.enable();
-                dumpAll = false;
-            }
-
-            if ((index < numArgs) &&
-                (args[index] == String16("--disable-layer-stats"))) {
-                index++;
-                mLayerStats.disable();
-                dumpAll = false;
-            }
-
-            if ((index < numArgs) &&
-                (args[index] == String16("--clear-layer-stats"))) {
-                index++;
-                mLayerStats.clear();
-                dumpAll = false;
-            }
-
-            if ((index < numArgs) &&
-                (args[index] == String16("--dump-layer-stats"))) {
-                index++;
-                mLayerStats.dump(result);
-                dumpAll = false;
-            }
-
-            if ((index < numArgs) &&
-                    (args[index] == String16("--frame-composition"))) {
-                index++;
-                dumpFrameCompositionInfo(result);
-                dumpAll = false;
-            }
-
-            if ((index < numArgs) &&
-                (args[index] == String16("--display-identification"))) {
-                index++;
-                dumpDisplayIdentificationData(result);
-                dumpAll = false;
-            }
-
-            if ((index < numArgs) && (args[index] == String16("--timestats"))) {
-                index++;
-                mTimeStats->parseArgs(asProto, args, index, result);
-                dumpAll = false;
-            }
-        }
-
-        if (dumpAll) {
+        if (const auto it = dumpers.find(flag); it != dumpers.end()) {
+            (it->second)(args, asProto, result);
+        } else {
             if (asProto) {
                 LayersProto layersProto = dumpProtoInfo(LayerVector::StateSet::Current);
                 result.append(layersProto.SerializeAsString().c_str(), layersProto.ByteSize());
             } else {
-                dumpAllLocked(args, index, result);
+                dumpAllLocked(args, result);
             }
         }
 
@@ -4326,48 +4422,29 @@
     return NO_ERROR;
 }
 
-void SurfaceFlinger::listLayersLocked(const Vector<String16>& /* args */, size_t& /* index */,
-                                      std::string& result) const {
+void SurfaceFlinger::listLayersLocked(std::string& result) const {
     mCurrentState.traverseInZOrder(
             [&](Layer* layer) { StringAppendF(&result, "%s\n", layer->getName().string()); });
 }
 
-void SurfaceFlinger::dumpStatsLocked(const Vector<String16>& args, size_t& index,
-                                     std::string& result) const {
-    String8 name;
-    if (index < args.size()) {
-        name = String8(args[index]);
-        index++;
-    }
+void SurfaceFlinger::dumpStatsLocked(const DumpArgs& args, std::string& result) const {
+    StringAppendF(&result, "%" PRId64 "\n", getVsyncPeriod());
 
-    if (const auto displayId = getInternalDisplayId();
-        displayId && getHwComposer().isConnected(*displayId)) {
-        const auto activeConfig = getHwComposer().getActiveConfig(*displayId);
-        const nsecs_t period = activeConfig->getVsyncPeriod();
-        StringAppendF(&result, "%" PRId64 "\n", period);
-    }
-
-    if (name.isEmpty()) {
-        mAnimFrameTracker.dumpStats(result);
-    } else {
+    if (args.size() > 1) {
+        const auto name = String8(args[1]);
         mCurrentState.traverseInZOrder([&](Layer* layer) {
             if (name == layer->getName()) {
                 layer->dumpFrameStats(result);
             }
         });
+    } else {
+        mAnimFrameTracker.dumpStats(result);
     }
 }
 
-void SurfaceFlinger::clearStatsLocked(const Vector<String16>& args, size_t& index,
-                                      std::string& /* result */) {
-    String8 name;
-    if (index < args.size()) {
-        name = String8(args[index]);
-        index++;
-    }
-
+void SurfaceFlinger::clearStatsLocked(const DumpArgs& args, std::string&) {
     mCurrentState.traverseInZOrder([&](Layer* layer) {
-        if (name.isEmpty() || (name == layer->getName())) {
+        if (args.size() < 2 || String8(args[1]) == layer->getName()) {
             layer->clearFrameStats();
         }
     });
@@ -4375,6 +4452,10 @@
     mAnimFrameTracker.clearStats();
 }
 
+void SurfaceFlinger::dumpTimeStats(const DumpArgs& args, bool asProto, std::string& result) const {
+    mTimeStats->parseArgs(asProto, args, result);
+}
+
 // This should only be called from the main thread.  Otherwise it would need
 // the lock and should use mCurrentState rather than mDrawingState.
 void SurfaceFlinger::logFrameStats() {
@@ -4400,6 +4481,26 @@
     result.append("]");
 }
 
+void SurfaceFlinger::dumpVSync(std::string& result) const {
+    const auto [sfEarlyOffset, appEarlyOffset] = mVsyncModulator.getEarlyOffsets();
+    const auto [sfEarlyGlOffset, appEarlyGlOffset] = mVsyncModulator.getEarlyGlOffsets();
+    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());
+
+    StringAppendF(&result, "Scheduler: %s\n\n", mUseScheduler ? "enabled" : "disabled");
+
+    if (mUseScheduler) {
+        mScheduler->dump(mAppConnectionHandle, result);
+    } else {
+        mEventThread->dump(result);
+    }
+}
+
 void SurfaceFlinger::dumpStaticScreenStats(std::string& result) const {
     result.append("Static screen stats:\n");
     for (size_t b = 0; b < SurfaceFlingerBE::NUM_BUCKETS - 1; ++b) {
@@ -4541,7 +4642,7 @@
             StringAppendF(&result, "    %s (%d)\n", decodeColorMode(mode).c_str(), mode);
         }
 
-        ColorMode currentMode = display->getActiveColorMode();
+        ColorMode currentMode = display->getCompositionDisplay()->getState().colorMode;
         StringAppendF(&result, "    Current color mode: %s (%d)\n",
                       decodeColorMode(currentMode).c_str(), currentMode);
     }
@@ -4577,18 +4678,21 @@
     return layersProto;
 }
 
-LayersProto SurfaceFlinger::dumpVisibleLayersProtoInfo(const DisplayDevice& display) const {
+LayersProto SurfaceFlinger::dumpVisibleLayersProtoInfo(const DisplayDevice& displayDevice) const {
     LayersProto layersProto;
 
     SizeProto* resolution = layersProto.mutable_resolution();
-    resolution->set_w(display.getWidth());
-    resolution->set_h(display.getHeight());
+    resolution->set_w(displayDevice.getWidth());
+    resolution->set_h(displayDevice.getHeight());
 
-    layersProto.set_color_mode(decodeColorMode(display.getActiveColorMode()));
-    layersProto.set_color_transform(decodeColorTransform(display.getColorTransform()));
-    layersProto.set_global_transform(static_cast<int32_t>(display.getOrientationTransform()));
+    auto display = displayDevice.getCompositionDisplay();
+    const auto& displayState = display->getState();
 
-    const auto displayId = display.getId();
+    layersProto.set_color_mode(decodeColorMode(displayState.colorMode));
+    layersProto.set_color_transform(decodeColorTransform(displayState.colorTransform));
+    layersProto.set_global_transform(displayState.orientation);
+
+    const auto displayId = displayDevice.getId();
     LOG_ALWAYS_FATAL_IF(!displayId);
     mDrawingState.traverseInZOrder([&](Layer* layer) {
         if (!layer->visibleRegion.isEmpty() && layer->getBE().mHwcLayers.count(*displayId)) {
@@ -4600,15 +4704,8 @@
     return layersProto;
 }
 
-void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index,
-                                   std::string& result) const {
-    bool colorize = false;
-    if (index < args.size()
-            && (args[index] == String16("--color"))) {
-        colorize = true;
-        index++;
-    }
-
+void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) const {
+    const bool colorize = !args.empty() && args[0] == String16("--color");
     Colorizer colorizer(colorize);
 
     // figure out if we're stuck somewhere
@@ -4638,33 +4735,14 @@
     result.append("Sync configuration: ");
     colorizer.reset(result);
     result.append(SyncFeatures::getInstance().toString());
-    result.append("\n");
+    result.append("\n\n");
 
     colorizer.bold(result);
-    result.append("DispSync configuration:\n");
+    result.append("VSYNC configuration:\n");
     colorizer.reset(result);
-
-    const auto [sfEarlyOffset, appEarlyOffset] = mVsyncModulator.getEarlyOffsets();
-    const auto [sfEarlyGlOffset, appEarlyGlOffset] = mVsyncModulator.getEarlyGlOffsets();
-    if (const auto displayId = getInternalDisplayId();
-        displayId && getHwComposer().isConnected(*displayId)) {
-        const auto activeConfig = getHwComposer().getActiveConfig(*displayId);
-        StringAppendF(&result,
-                      "Display %s: app phase %" PRId64 " ns, "
-                      "sf phase %" PRId64 " ns, "
-                      "early app phase %" PRId64 " ns, "
-                      "early sf phase %" PRId64 " ns, "
-                      "early app gl phase %" PRId64 " ns, "
-                      "early sf gl phase %" PRId64 " ns, "
-                      "present offset %" PRId64 " ns (refresh %" PRId64 " ns)",
-                      to_string(*displayId).c_str(), vsyncPhaseOffsetNs, sfVsyncPhaseOffsetNs,
-                      appEarlyOffset, sfEarlyOffset, appEarlyGlOffset, sfEarlyGlOffset,
-                      dispSyncPresentTimeOffset, activeConfig->getVsyncPeriod());
-    }
+    dumpVSync(result);
     result.append("\n");
 
-    // Dump static screen stats
-    result.append("\n");
     dumpStaticScreenStats(result);
     result.append("\n");
 
@@ -4715,7 +4793,8 @@
     getRenderEngine().dump(result);
 
     if (const auto display = getDefaultDisplayDeviceLocked()) {
-        display->undefinedRegion.dump(result, "undefinedRegion");
+        display->getCompositionDisplay()->getState().undefinedRegion.dump(result,
+                                                                          "undefinedRegion");
         StringAppendF(&result, "  orientation=%d, isPoweredOn=%d\n", display->getOrientation(),
                       display->isPoweredOn());
     }
@@ -4737,17 +4816,6 @@
 
     StringAppendF(&result, "  transaction time: %f us\n", inTransactionDuration / 1000.0);
 
-    StringAppendF(&result, "  use Scheduler: %s\n", mUseScheduler ? "true" : "false");
-    /*
-     * VSYNC state
-     */
-    if (mUseScheduler) {
-        mScheduler->dump(mAppConnectionHandle, result);
-    } else {
-        mEventThread->dump(result);
-    }
-    result.append("\n");
-
     /*
      * Tracing state
      */
@@ -4793,6 +4861,15 @@
         result.append(mVrFlinger->Dump());
         result.append("\n");
     }
+
+    /**
+     * Scheduler dump state.
+     */
+    if (mUseScheduler) {
+        result.append("\nScheduler state:\n");
+        result.append(mScheduler->doDump() + "\n");
+        result.append(mRefreshRateStats->doDump() + "\n");
+    }
 }
 
 const Vector<sp<Layer>>& SurfaceFlinger::getLayerSortedByZForHwcDisplay(DisplayId displayId) {
@@ -4906,7 +4983,11 @@
         case SET_TRANSACTION_STATE:
         case CREATE_CONNECTION:
         case GET_COLOR_MANAGEMENT:
-        case GET_COMPOSITION_PREFERENCE: {
+        case GET_COMPOSITION_PREFERENCE:
+        case GET_PROTECTED_CONTENT_SUPPORT:
+        case CACHE_BUFFER:
+        case UNCACHE_BUFFER:
+        case IS_WIDE_COLOR_DISPLAY: {
             return OK;
         }
         case CAPTURE_LAYERS:
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index eb7127e..4d84144 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -17,73 +17,70 @@
 #ifndef ANDROID_SURFACE_FLINGER_H
 #define ANDROID_SURFACE_FLINGER_H
 
-#include <memory>
-#include <stdint.h>
 #include <sys/types.h>
 
 /*
  * NOTE: Make sure this file doesn't include  anything from <gl/ > or <gl2/ >
  */
 
-#include <cutils/compiler.h>
+#include <android-base/thread_annotations.h>
 #include <cutils/atomic.h>
-
-#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
-#include <utils/RefBase.h>
-#include <utils/SortedVector.h>
-#include <utils/threads.h>
-#include <utils/Trace.h>
-
-#include <ui/FenceTime.h>
-#include <ui/PixelFormat.h>
-#include <math/mat4.h>
-
+#include <cutils/compiler.h>
+#include <gui/BufferQueue.h>
 #include <gui/FrameTimestamps.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/ISurfaceComposerClient.h>
 #include <gui/LayerState.h>
-
 #include <gui/OccupancyTracker.h>
-#include <gui/BufferQueue.h>
-
 #include <hardware/hwcomposer_defs.h>
-
+#include <layerproto/LayerProtoHeader.h>
+#include <math/mat4.h>
 #include <serviceutils/PriorityDumper.h>
-
 #include <system/graphics.h>
+#include <ui/FenceTime.h>
+#include <ui/PixelFormat.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/RefBase.h>
+#include <utils/SortedVector.h>
+#include <utils/Trace.h>
+#include <utils/threads.h>
 
 #include "Barrier.h"
+#include "BufferStateLayerCache.h"
 #include "DisplayDevice.h"
+#include "DisplayHardware/HWC2.h"
+#include "DisplayHardware/HWComposer.h"
+#include "Effects/Daltonizer.h"
 #include "FrameTracker.h"
 #include "LayerBE.h"
 #include "LayerStats.h"
 #include "LayerVector.h"
+#include "Scheduler/DispSync.h"
+#include "Scheduler/EventThread.h"
+#include "Scheduler/MessageQueue.h"
+#include "Scheduler/RefreshRateStats.h"
+#include "Scheduler/Scheduler.h"
+#include "Scheduler/VSyncModulator.h"
 #include "SurfaceFlingerFactory.h"
 #include "SurfaceInterceptor.h"
 #include "SurfaceTracing.h"
 #include "TransactionCompletedThread.h"
 
-#include "DisplayHardware/HWC2.h"
-#include "DisplayHardware/HWComposer.h"
-#include "Effects/Daltonizer.h"
-#include "Scheduler/DispSync.h"
-#include "Scheduler/EventThread.h"
-#include "Scheduler/MessageQueue.h"
-#include "Scheduler/Scheduler.h"
-#include "Scheduler/VSyncModulator.h"
-
+#include <atomic>
+#include <cstdint>
+#include <functional>
 #include <map>
+#include <memory>
 #include <mutex>
 #include <queue>
 #include <set>
 #include <string>
 #include <thread>
+#include <type_traits>
 #include <unordered_map>
 #include <utility>
 
-#include <layerproto/LayerProtoHeader.h>
-
 using namespace android::surfaceflinger;
 
 namespace android {
@@ -106,6 +103,10 @@
 class VSyncSource;
 struct CompositionInfo;
 
+namespace compositionengine {
+class DisplaySurface;
+} // namespace compositionengine
+
 namespace impl {
 class EventThread;
 } // namespace impl
@@ -140,7 +141,6 @@
     ENHANCED = 2,
 };
 
-
 class SurfaceFlingerBE
 {
 public:
@@ -148,10 +148,6 @@
 
     const std::string mHwcServiceName; // "default" for real use, something else for testing.
 
-    // constant members (no synchronization needed for access)
-    EGLContext mEGLContext;
-    EGLDisplay mEGLDisplay;
-
     FenceTimeline mGlCompositionDoneTimeline;
     FenceTimeline mDisplayTimeline;
 
@@ -298,10 +294,6 @@
     // starts SurfaceFlinger main loop in the current thread
     void run() ANDROID_API;
 
-    enum {
-        EVENT_VSYNC = HWC_EVENT_VSYNC
-    };
-
     // post an asynchronous message to the main thread
     status_t postMessageAsync(const sp<MessageBase>& msg, nsecs_t reltime = 0, uint32_t flags = 0);
 
@@ -412,77 +404,81 @@
     /* ------------------------------------------------------------------------
      * IBinder interface
      */
-    virtual status_t onTransact(uint32_t code, const Parcel& data,
-        Parcel* reply, uint32_t flags);
-    virtual status_t dump(int fd, const Vector<String16>& args) { return priorityDump(fd, args); }
+    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override;
+    status_t dump(int fd, const Vector<String16>& args) override { return priorityDump(fd, args); }
 
     /* ------------------------------------------------------------------------
      * ISurfaceComposer interface
      */
-    virtual sp<ISurfaceComposerClient> createConnection();
-    virtual sp<IBinder> createDisplay(const String8& displayName, bool secure);
-    virtual void destroyDisplay(const sp<IBinder>& displayToken);
-    virtual sp<IBinder> getBuiltInDisplay(int32_t id);
-    virtual void setTransactionState(const Vector<ComposerState>& state,
-                                     const Vector<DisplayState>& displays, uint32_t flags,
-                                     const sp<IBinder>& applyToken,
-                                     const InputWindowCommands& inputWindowCommands);
-    virtual void bootFinished();
-    virtual bool authenticateSurfaceTexture(
-        const sp<IGraphicBufferProducer>& bufferProducer) const;
-    virtual status_t getSupportedFrameTimestamps(
-            std::vector<FrameEvent>* outSupported) const;
-    virtual sp<IDisplayEventConnection> createDisplayEventConnection(
-            ISurfaceComposer::VsyncSource vsyncSource = eVsyncSourceApp);
-    virtual status_t captureScreen(const sp<IBinder>& displayToken, sp<GraphicBuffer>* outBuffer,
-                                   const ui::Dataspace reqDataspace,
-                                   const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
-                                   uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
-                                   ISurfaceComposer::Rotation rotation);
-    virtual status_t captureLayers(const sp<IBinder>& parentHandle, sp<GraphicBuffer>* outBuffer,
-                                   const ui::Dataspace reqDataspace,
-                                   const ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
-                                   float frameScale, bool childrenOnly);
-    virtual status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats);
-    virtual status_t getDisplayConfigs(const sp<IBinder>& displayToken,
-                                       Vector<DisplayInfo>* configs);
-    virtual int getActiveConfig(const sp<IBinder>& displayToken);
-    virtual status_t getDisplayColorModes(const sp<IBinder>& displayToken,
-                                          Vector<ui::ColorMode>* configs);
-    virtual ui::ColorMode getActiveColorMode(const sp<IBinder>& displayToken);
-    virtual status_t setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode colorMode);
-    virtual void setPowerMode(const sp<IBinder>& displayToken, int mode);
-    virtual status_t setActiveConfig(const sp<IBinder>& displayToken, int id);
-    virtual status_t clearAnimationFrameStats();
-    virtual status_t getAnimationFrameStats(FrameStats* outStats) const;
-    virtual status_t getHdrCapabilities(const sp<IBinder>& displayToken,
-                                        HdrCapabilities* outCapabilities) const;
-    virtual status_t enableVSyncInjections(bool enable);
-    virtual status_t injectVSync(nsecs_t when);
-    virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const;
-    virtual status_t getColorManagement(bool* outGetColorManagement) const;
+    sp<ISurfaceComposerClient> createConnection() override;
+    sp<IBinder> createDisplay(const String8& displayName, bool secure) override;
+    void destroyDisplay(const sp<IBinder>& displayToken) override;
+    sp<IBinder> getBuiltInDisplay(int32_t id) override;
+    void setTransactionState(const Vector<ComposerState>& state,
+                             const Vector<DisplayState>& displays, uint32_t flags,
+                             const sp<IBinder>& applyToken,
+                             const InputWindowCommands& inputWindowCommands,
+                             int64_t desiredPresentTime) override;
+    void bootFinished() override;
+    bool authenticateSurfaceTexture(
+            const sp<IGraphicBufferProducer>& bufferProducer) const override;
+    status_t getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) const override;
+    sp<IDisplayEventConnection> createDisplayEventConnection(
+            ISurfaceComposer::VsyncSource vsyncSource = eVsyncSourceApp) override;
+    status_t captureScreen(const sp<IBinder>& displayToken, sp<GraphicBuffer>* outBuffer,
+                           const ui::Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat,
+                           Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
+                           bool useIdentityTransform, ISurfaceComposer::Rotation rotation) override;
+    status_t captureLayers(const sp<IBinder>& parentHandle, sp<GraphicBuffer>* outBuffer,
+                           const ui::Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat,
+                           const Rect& sourceCrop, float frameScale, bool childrenOnly) override;
+    status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats) override;
+    status_t getDisplayConfigs(const sp<IBinder>& displayToken,
+                               Vector<DisplayInfo>* configs) override;
+    int getActiveConfig(const sp<IBinder>& displayToken) override;
+    status_t getDisplayColorModes(const sp<IBinder>& displayToken,
+                                  Vector<ui::ColorMode>* configs) override;
+    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;
+    status_t setActiveConfig(const sp<IBinder>& displayToken, int id) override;
+    status_t clearAnimationFrameStats() override;
+    status_t getAnimationFrameStats(FrameStats* outStats) const override;
+    status_t getHdrCapabilities(const sp<IBinder>& displayToken,
+                                HdrCapabilities* outCapabilities) const override;
+    status_t enableVSyncInjections(bool enable) override;
+    status_t injectVSync(nsecs_t when) override;
+    status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const override;
+    status_t getColorManagement(bool* outGetColorManagement) const override;
     status_t getCompositionPreference(ui::Dataspace* outDataspace, ui::PixelFormat* outPixelFormat,
                                       ui::Dataspace* outWideColorGamutDataspace,
                                       ui::PixelFormat* outWideColorGamutPixelFormat) const override;
-    virtual status_t getDisplayedContentSamplingAttributes(
-            const sp<IBinder>& display, ui::PixelFormat* outFormat, ui::Dataspace* outDataspace,
-            uint8_t* outComponentMask) const override;
-    virtual status_t setDisplayContentSamplingEnabled(const sp<IBinder>& display, bool enable,
-                                                      uint8_t componentMask,
-                                                      uint64_t maxFrames) const override;
-    virtual status_t getDisplayedContentSample(const sp<IBinder>& display, uint64_t maxFrames,
-                                               uint64_t timestamp,
-                                               DisplayedFrameStats* outStats) const override;
+    status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& display,
+                                                   ui::PixelFormat* outFormat,
+                                                   ui::Dataspace* outDataspace,
+                                                   uint8_t* outComponentMask) const override;
+    status_t setDisplayContentSamplingEnabled(const sp<IBinder>& display, bool enable,
+                                              uint8_t componentMask,
+                                              uint64_t maxFrames) const override;
+    status_t getDisplayedContentSample(const sp<IBinder>& display, uint64_t maxFrames,
+                                       uint64_t timestamp,
+                                       DisplayedFrameStats* outStats) const override;
+    status_t getProtectedContentSupport(bool* outSupported) const override;
+    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
      */
-    virtual void binderDied(const wp<IBinder>& who);
+    void binderDied(const wp<IBinder>& who) override;
 
     /* ------------------------------------------------------------------------
      * RefBase interface
      */
-    virtual void onFirstRef();
+    void onFirstRef() override;
 
     /* ------------------------------------------------------------------------
      * HWC2::ComposerCallback / HWComposer::EventHandler interface
@@ -504,17 +500,13 @@
     void signalRefresh();
 
     // called on the main thread in response to initializeDisplays()
-    void onInitializeDisplays();
+    void onInitializeDisplays() REQUIRES(mStateLock);
     // setActiveConfigInternal() posted on a main thread for async execution
     status_t setActiveConfigAsync(const sp<IBinder>& displayToken, int mode);
     // called on the main thread in response to setActiveConfig()
-    void setActiveConfigInternal(const sp<IBinder>& displayToken, int mode);
+    void setActiveConfigInternal(const sp<IBinder>& displayToken, int mode) REQUIRES(mStateLock);
     // called on the main thread in response to setPowerMode()
-    void setPowerModeInternal(const sp<DisplayDevice>& display, int mode);
-
-    // Called on the main thread in response to setActiveColorMode()
-    void setActiveColorModeInternal(const sp<DisplayDevice>& display, ui::ColorMode colorMode,
-                                    ui::Dataspace dataSpace, ui::RenderIntent renderIntent);
+    void setPowerModeInternal(const sp<DisplayDevice>& display, int mode) REQUIRES(mStateLock);
 
     // Returns whether the transaction actually modified any state
     bool handleMessageTransaction();
@@ -552,7 +544,8 @@
     void latchAndReleaseBuffer(const sp<Layer>& layer);
     void commitTransaction();
     bool containsAnyInvalidClientState(const Vector<ComposerState>& states);
-    bool composerStateContainsUnsignaledFences(const Vector<ComposerState>& states);
+    bool transactionIsReadyToBeApplied(int64_t desiredPresentTime,
+                                       const Vector<ComposerState>& states);
     uint32_t setClientStateLocked(const ComposerState& composerState);
     uint32_t setDisplayStateLocked(const DisplayState& s);
     uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands);
@@ -560,10 +553,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,
@@ -706,6 +698,7 @@
 
     void preComposition();
     void postComposition();
+    void getCompositorTiming(CompositorTiming* compositorTiming);
     void updateCompositorTiming(const DisplayStatInfo& stats, nsecs_t compositeTime,
                                 std::shared_ptr<FenceTime>& presentFenceTime);
     void setCompositorTimingSnapped(const DisplayStatInfo& stats,
@@ -749,42 +742,54 @@
     /* ------------------------------------------------------------------------
      * Display management
      */
-    sp<DisplayDevice> setupNewDisplayDeviceInternal(const wp<IBinder>& displayToken,
-                                                    const std::optional<DisplayId>& displayId,
-                                                    const DisplayDeviceState& state,
-                                                    const sp<DisplaySurface>& dispSurface,
-                                                    const sp<IGraphicBufferProducer>& producer);
+    sp<DisplayDevice> setupNewDisplayDeviceInternal(
+            const wp<IBinder>& displayToken, const std::optional<DisplayId>& displayId,
+            const DisplayDeviceState& state,
+            const sp<compositionengine::DisplaySurface>& dispSurface,
+            const sp<IGraphicBufferProducer>& producer);
     void processDisplayChangesLocked();
     void processDisplayHotplugEventsLocked();
 
+    void dispatchDisplayHotplugEvent(EventThread::DisplayType displayType, bool connected);
+
     /* ------------------------------------------------------------------------
      * VSync
      */
+    nsecs_t getVsyncPeriod() const REQUIRES(mStateLock);
     void enableHardwareVsync();
-    void resyncToHardwareVsync(bool makeAvailable);
+    void resyncToHardwareVsync(bool makeAvailable, nsecs_t period);
     void disableHardwareVsync(bool makeUnavailable);
 
-public:
-    void resyncWithRateLimit();
-    void getCompositorTiming(CompositorTiming* compositorTiming);
-private:
     // Sets the refresh rate to newFps by switching active configs, if they are available for
     // the desired refresh rate.
     void setRefreshRateTo(float newFps);
 
-    /* ------------------------------------------------------------------------
-     * Debugging & dumpsys
+    using GetVsyncPeriod = std::function<nsecs_t()>;
+
+    // Stores per-display state about VSYNC.
+    struct VsyncState {
+        explicit VsyncState(SurfaceFlinger& flinger) : flinger(flinger) {}
+
+        void resync(const GetVsyncPeriod&);
+
+        SurfaceFlinger& flinger;
+        std::atomic<nsecs_t> lastResyncTime = 0;
+    };
+
+    const std::shared_ptr<VsyncState> mPrimaryVsyncState{std::make_shared<VsyncState>(*this)};
+
+    auto makeResyncCallback(GetVsyncPeriod&& getVsyncPeriod) {
+        std::weak_ptr<VsyncState> ptr = mPrimaryVsyncState;
+        return [ptr, getVsyncPeriod = std::move(getVsyncPeriod)]() {
+            if (const auto vsync = ptr.lock()) {
+                vsync->resync(getVsyncPeriod);
+            }
+        };
+    }
+
+    /*
+     * Display identification
      */
-public:
-    status_t dumpCritical(int fd, const Vector<String16>& /*args*/, bool asProto) {
-        return doDump(fd, Vector<String16>(), asProto);
-    }
-
-    status_t dumpAll(int fd, const Vector<String16>& args, bool asProto) {
-        return doDump(fd, args, asProto);
-    }
-
-private:
     sp<IBinder> getPhysicalDisplayToken(DisplayId displayId) const {
         const auto it = mPhysicalDisplayTokens.find(displayId);
         return it != mPhysicalDisplayTokens.end() ? it->second : nullptr;
@@ -816,15 +821,48 @@
         return hwcDisplayId ? getHwComposer().toPhysicalDisplayId(*hwcDisplayId) : std::nullopt;
     }
 
-    void listLayersLocked(const Vector<String16>& args, size_t& index, std::string& result) const;
-    void dumpStatsLocked(const Vector<String16>& args, size_t& index, std::string& result) const;
-    void clearStatsLocked(const Vector<String16>& args, size_t& index, std::string& result);
-    void dumpAllLocked(const Vector<String16>& args, size_t& index, std::string& result) const;
+    /*
+     * Debugging & dumpsys
+     */
     bool startDdmConnection();
-    void appendSfConfigString(std::string& result) const;
 
+    using DumpArgs = Vector<String16>;
+    using Dumper = std::function<void(const DumpArgs&, bool asProto, std::string&)>;
+
+    template <typename F, std::enable_if_t<!std::is_member_function_pointer_v<F>>* = nullptr>
+    static Dumper dumper(F&& dump) {
+        using namespace std::placeholders;
+        return std::bind(std::forward<F>(dump), _3);
+    }
+
+    template <typename F, std::enable_if_t<std::is_member_function_pointer_v<F>>* = nullptr>
+    Dumper dumper(F dump) {
+        using namespace std::placeholders;
+        return std::bind(dump, this, _3);
+    }
+
+    template <typename F>
+    Dumper argsDumper(F dump) {
+        using namespace std::placeholders;
+        return std::bind(dump, this, _1, _3);
+    }
+
+    template <typename F>
+    Dumper protoDumper(F dump) {
+        using namespace std::placeholders;
+        return std::bind(dump, this, _1, _2, _3);
+    }
+
+    void dumpAllLocked(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
+
+    void appendSfConfigString(std::string& result) const;
+    void listLayersLocked(std::string& result) const;
+    void dumpStatsLocked(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
+    void clearStatsLocked(const DumpArgs& args, std::string& result);
+    void dumpTimeStats(const DumpArgs& args, bool asProto, std::string& result) const;
     void logFrameStats();
 
+    void dumpVSync(std::string& result) const REQUIRES(mStateLock);
     void dumpStaticScreenStats(std::string& result) const;
     // Not const because each Layer needs to query Fences and cache timestamps.
     void dumpFrameEventsLocked(std::string& result);
@@ -841,7 +879,16 @@
     bool isLayerTripleBufferingDisabled() const {
         return this->mLayerTripleBufferingDisabled;
     }
-    status_t doDump(int fd, const Vector<String16>& args, bool asProto);
+
+    status_t doDump(int fd, const DumpArgs& args, bool asProto);
+
+    status_t dumpCritical(int fd, const DumpArgs&, bool asProto) override {
+        return doDump(fd, DumpArgs(), asProto);
+    }
+
+    status_t dumpAll(int fd, const DumpArgs& args, bool asProto) override {
+        return doDump(fd, args, asProto);
+    }
 
     /* ------------------------------------------------------------------------
      * VrFlinger
@@ -979,12 +1026,17 @@
     };
     struct TransactionState {
         TransactionState(const Vector<ComposerState>& composerStates,
-                         const Vector<DisplayState>& displayStates, uint32_t transactionFlags)
-              : states(composerStates), displays(displayStates), flags(transactionFlags) {}
+                         const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
+                         int64_t desiredPresentTime)
+              : states(composerStates),
+                displays(displayStates),
+                flags(transactionFlags),
+                time(desiredPresentTime) {}
 
         Vector<ComposerState> states;
         Vector<DisplayState> displays;
         uint32_t flags;
+        int64_t time;
     };
     std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IBinderHash> mTransactionQueues;
 
@@ -1016,14 +1068,21 @@
     SurfaceFlingerBE mBE;
     std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine;
 
+    /* ------------------------------------------------------------------------
+     * Scheduler
+     */
     bool mUseScheduler = false;
     std::unique_ptr<Scheduler> mScheduler;
     sp<Scheduler::ConnectionHandle> mAppConnectionHandle;
     sp<Scheduler::ConnectionHandle> mSfConnectionHandle;
+    std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
 
+    /* ------------------------------------------------------------------------ */
     sp<IInputFlinger> mInputFlinger;
 
     InputWindowCommands mInputWindowCommands;
+
+    BufferStateLayerCache mBufferStateLayerCache;
 };
 }; // namespace android
 
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
new file mode 100644
index 0000000..b654ba7
--- /dev/null
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -0,0 +1,246 @@
+
+#include <sysprop/SurfaceFlingerProperties.sysprop.h>
+
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
+#include <android/hardware/configstore/1.1/types.h>
+#include <android/hardware/configstore/1.2/ISurfaceFlingerConfigs.h>
+#include <configstore/Utils.h>
+
+#include <tuple>
+
+#include "SurfaceFlingerProperties.h"
+
+namespace android {
+namespace sysprop {
+using namespace android::hardware::configstore;
+using namespace android::hardware::configstore::V1_0;
+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();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return getInt64<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::vsyncEventPhaseOffsetNs>(
+            defaultValue);
+}
+
+int64_t vsync_sf_event_phase_offset_ns(int64_t defaultValue) {
+    auto temp = SurfaceFlingerProperties::vsync_sf_event_phase_offset_ns();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return getInt64<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::vsyncSfEventPhaseOffsetNs>(
+            defaultValue);
+}
+
+bool use_context_priority(bool defaultValue) {
+    auto temp = SurfaceFlingerProperties::use_context_priority();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::useContextPriority>(
+            defaultValue);
+}
+
+int64_t max_frame_buffer_acquired_buffers(int64_t defaultValue) {
+    auto temp = SurfaceFlingerProperties::max_frame_buffer_acquired_buffers();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return getInt64<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::maxFrameBufferAcquiredBuffers>(
+            defaultValue);
+}
+
+bool has_wide_color_display(bool defaultValue) {
+    auto temp = SurfaceFlingerProperties::has_wide_color_display();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasWideColorDisplay>(
+            defaultValue);
+}
+
+bool running_without_sync_framework(bool defaultValue) {
+    auto temp = SurfaceFlingerProperties::running_without_sync_framework();
+    if (temp.has_value()) {
+        return !(*temp);
+    }
+    return getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasSyncFramework>(defaultValue);
+}
+
+bool has_HDR_display(bool defaultValue) {
+    auto temp = SurfaceFlingerProperties::has_HDR_display();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasHDRDisplay>(defaultValue);
+}
+
+int64_t present_time_offset_from_vsync_ns(int64_t defaultValue) {
+    auto temp = SurfaceFlingerProperties::present_time_offset_from_vsync_ns();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return getInt64<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::presentTimeOffsetFromVSyncNs>(
+            defaultValue);
+}
+
+bool force_hwc_copy_for_virtual_displays(bool defaultValue) {
+    auto temp = SurfaceFlingerProperties::force_hwc_copy_for_virtual_displays();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::useHwcForRGBtoYUV>(
+            defaultValue);
+}
+
+int64_t max_virtual_display_dimension(int64_t defaultValue) {
+    auto temp = SurfaceFlingerProperties::max_virtual_display_dimension();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return getUInt64<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::maxVirtualDisplaySize>(
+            defaultValue);
+}
+
+bool use_vr_flinger(bool defaultValue) {
+    auto temp = SurfaceFlingerProperties::use_vr_flinger();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::useVrFlinger>(defaultValue);
+}
+
+bool start_graphics_allocator_service(bool defaultValue) {
+    auto temp = SurfaceFlingerProperties::start_graphics_allocator_service();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::startGraphicsAllocatorService>(
+            defaultValue);
+}
+
+SurfaceFlingerProperties::primary_display_orientation_values primary_display_orientation(
+        SurfaceFlingerProperties::primary_display_orientation_values defaultValue) {
+    auto temp = SurfaceFlingerProperties::primary_display_orientation();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    auto configDefault = DisplayOrientation::ORIENTATION_0;
+    switch (defaultValue) {
+        case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_90:
+            configDefault = DisplayOrientation::ORIENTATION_90;
+            break;
+        case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_180:
+            configDefault = DisplayOrientation::ORIENTATION_180;
+            break;
+        case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_270:
+            configDefault = DisplayOrientation::ORIENTATION_270;
+            break;
+        default:
+            configDefault = DisplayOrientation::ORIENTATION_0;
+            break;
+    }
+    DisplayOrientation result =
+            getDisplayOrientation<V1_1::ISurfaceFlingerConfigs,
+                                  &V1_1::ISurfaceFlingerConfigs::primaryDisplayOrientation>(
+                    configDefault);
+    switch (result) {
+        case DisplayOrientation::ORIENTATION_90:
+            return SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_90;
+        case DisplayOrientation::ORIENTATION_180:
+            return SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_180;
+        case DisplayOrientation::ORIENTATION_270:
+            return SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_270;
+        default:
+            break;
+    }
+    return SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_0;
+}
+
+bool use_color_management(bool defaultValue) {
+    auto tmpuseColorManagement = SurfaceFlingerProperties::use_color_management();
+    auto tmpHasHDRDisplay = SurfaceFlingerProperties::has_HDR_display();
+    auto tmpHasWideColorDisplay = SurfaceFlingerProperties::has_wide_color_display();
+    if (tmpuseColorManagement.has_value() && tmpHasHDRDisplay.has_value() &&
+        tmpHasWideColorDisplay.has_value()) {
+        return *tmpuseColorManagement || *tmpHasHDRDisplay || *tmpHasWideColorDisplay;
+    }
+    auto surfaceFlingerConfigsServiceV1_2 = ISurfaceFlingerConfigs::getService();
+    if (surfaceFlingerConfigsServiceV1_2) {
+        return getBool<V1_2::ISurfaceFlingerConfigs,
+                       &V1_2::ISurfaceFlingerConfigs::useColorManagement>(defaultValue);
+    }
+    return defaultValue;
+}
+
+auto getCompositionPreference(sp<V1_2::ISurfaceFlingerConfigs> configsServiceV1_2) {
+    Dataspace defaultCompositionDataspace = Dataspace::V0_SRGB;
+    PixelFormat defaultCompositionPixelFormat = PixelFormat::RGBA_8888;
+    Dataspace wideColorGamutCompositionDataspace = Dataspace::V0_SRGB;
+    PixelFormat wideColorGamutCompositionPixelFormat = PixelFormat::RGBA_8888;
+    configsServiceV1_2->getCompositionPreference(
+            [&](auto tmpDefaultDataspace, auto tmpDefaultPixelFormat,
+                auto tmpWideColorGamutDataspace, auto tmpWideColorGamutPixelFormat) {
+                defaultCompositionDataspace = tmpDefaultDataspace;
+                defaultCompositionPixelFormat = tmpDefaultPixelFormat;
+                wideColorGamutCompositionDataspace = tmpWideColorGamutDataspace;
+                wideColorGamutCompositionPixelFormat = tmpWideColorGamutPixelFormat;
+            });
+    return std::tuple(defaultCompositionDataspace, defaultCompositionPixelFormat,
+                      wideColorGamutCompositionDataspace, wideColorGamutCompositionPixelFormat);
+}
+
+int64_t default_composition_dataspace(Dataspace defaultValue) {
+    auto temp = SurfaceFlingerProperties::default_composition_dataspace();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    auto configsServiceV1_2 = V1_2::ISurfaceFlingerConfigs::getService();
+    if (configsServiceV1_2) {
+        return static_cast<int64_t>(get<0>(getCompositionPreference(configsServiceV1_2)));
+    }
+    return static_cast<int64_t>(defaultValue);
+}
+
+int32_t default_composition_pixel_format(PixelFormat defaultValue) {
+    auto temp = SurfaceFlingerProperties::default_composition_pixel_format();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    auto configsServiceV1_2 = V1_2::ISurfaceFlingerConfigs::getService();
+    if (configsServiceV1_2) {
+        return static_cast<int32_t>(get<1>(getCompositionPreference(configsServiceV1_2)));
+    }
+    return static_cast<int32_t>(defaultValue);
+}
+
+int64_t wcg_composition_dataspace(Dataspace defaultValue) {
+    auto temp = SurfaceFlingerProperties::wcg_composition_dataspace();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    auto configsServiceV1_2 = V1_2::ISurfaceFlingerConfigs::getService();
+    if (configsServiceV1_2) {
+        return static_cast<int64_t>(get<2>(getCompositionPreference(configsServiceV1_2)));
+    }
+    return static_cast<int64_t>(defaultValue);
+}
+
+int32_t wcg_composition_pixel_format(PixelFormat defaultValue) {
+    auto temp = SurfaceFlingerProperties::wcg_composition_pixel_format();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    auto configsServiceV1_2 = V1_2::ISurfaceFlingerConfigs::getService();
+    if (configsServiceV1_2) {
+        return static_cast<int32_t>(get<3>(getCompositionPreference(configsServiceV1_2)));
+    }
+    return static_cast<int32_t>(defaultValue);
+}
+
+} // namespace sysprop
+} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
new file mode 100644
index 0000000..9b26883
--- /dev/null
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -0,0 +1,58 @@
+
+#ifndef SURFACEFLINGERPROPERTIES_H_
+#define SURFACEFLINGERPROPERTIES_H_
+
+#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
+#include <android/hardware/configstore/1.2/ISurfaceFlingerConfigs.h>
+#include <sysprop/SurfaceFlingerProperties.sysprop.h>
+
+#include <cstdint>
+#include <optional>
+#include <vector>
+
+namespace android {
+namespace sysprop {
+
+int64_t vsync_event_phase_offset_ns(int64_t defaultValue);
+
+int64_t vsync_sf_event_phase_offset_ns(int64_t defaultValue);
+
+bool use_context_priority(bool defaultValue);
+
+int64_t max_frame_buffer_acquired_buffers(int64_t defaultValue);
+
+bool has_wide_color_display(bool defaultValue);
+
+bool running_without_sync_framework(bool defaultValue);
+
+bool has_HDR_display(bool defaultValue);
+
+int64_t present_time_offset_from_vsync_ns(int64_t defaultValue);
+
+bool force_hwc_copy_for_virtual_displays(bool defaultValue);
+
+int64_t max_virtual_display_dimension(int64_t defaultValue);
+
+bool use_vr_flinger(bool defaultValue);
+
+bool start_graphics_allocator_service(bool defaultValue);
+
+SurfaceFlingerProperties::primary_display_orientation_values primary_display_orientation(
+        SurfaceFlingerProperties::primary_display_orientation_values defaultValue);
+
+bool use_color_management(bool defaultValue);
+
+int64_t default_composition_dataspace(
+        android::hardware::graphics::common::V1_2::Dataspace defaultValue);
+
+int32_t default_composition_pixel_format(
+        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_2::PixelFormat defaultValue);
+} // namespace sysprop
+} // namespace android
+#endif // SURFACEFLINGERPROPERTIES_H_
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 6a5488a..8d3776b 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -32,19 +32,12 @@
 
 namespace android {
 
-void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, size_t& index,
-                          std::string& result) {
+void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, std::string& result) {
     ATRACE_CALL();
 
-    if (args.size() > index + 10) {
-        ALOGD("Invalid args count");
-        return;
-    }
-
     std::unordered_map<std::string, int32_t> argsMap;
-    while (index < args.size()) {
+    for (size_t index = 0; index < args.size(); ++index) {
         argsMap[std::string(String8(args[index]).c_str())] = index;
-        ++index;
     }
 
     if (argsMap.count("-disable")) {
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 71c3ed7..e8fbcab 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -77,7 +77,7 @@
     TimeStats() = default;
     ~TimeStats() = default;
 
-    void parseArgs(bool asProto, const Vector<String16>& args, size_t& index, std::string& result);
+    void parseArgs(bool asProto, const Vector<String16>& args, std::string& result);
     bool isEnabled();
 
     void incrementTotalFrames();
diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp
index a1a8692..d2b7fe0 100644
--- a/services/surfaceflinger/TransactionCompletedThread.cpp
+++ b/services/surfaceflinger/TransactionCompletedThread.cpp
@@ -62,8 +62,7 @@
     mThread = std::thread(&TransactionCompletedThread::threadMain, this);
 }
 
-void TransactionCompletedThread::registerPendingLatchedCallbackHandle(
-        const sp<CallbackHandle>& handle) {
+void TransactionCompletedThread::registerPendingCallbackHandle(const sp<CallbackHandle>& handle) {
     std::lock_guard lock(mMutex);
 
     sp<IBinder> listener = IInterface::asBinder(handle->listener);
@@ -72,19 +71,10 @@
     mPendingTransactions[listener][callbackIds]++;
 }
 
-void TransactionCompletedThread::addLatchedCallbackHandles(
-        const std::deque<sp<CallbackHandle>>& handles, nsecs_t latchTime,
-        const sp<Fence>& previousReleaseFence) {
+void TransactionCompletedThread::addPresentedCallbackHandles(
+        const std::deque<sp<CallbackHandle>>& handles) {
     std::lock_guard lock(mMutex);
 
-    // If the previous release fences have not signaled, something as probably gone wrong.
-    // Store the fences and check them again before sending a callback.
-    if (previousReleaseFence &&
-        previousReleaseFence->getSignalTime() == Fence::SIGNAL_TIME_PENDING) {
-        ALOGD("release fence from the previous frame has not signaled");
-        mPreviousReleaseFences.push_back(previousReleaseFence);
-    }
-
     for (const auto& handle : handles) {
         auto listener = mPendingTransactions.find(IInterface::asBinder(handle->listener));
         auto& pendingCallbacks = listener->second;
@@ -101,17 +91,16 @@
             ALOGE("there are more latched callbacks than there were registered callbacks");
         }
 
-        addCallbackHandle(handle, latchTime);
+        addCallbackHandle(handle);
     }
 }
 
-void TransactionCompletedThread::addUnlatchedCallbackHandle(const sp<CallbackHandle>& handle) {
+void TransactionCompletedThread::addUnpresentedCallbackHandle(const sp<CallbackHandle>& handle) {
     std::lock_guard lock(mMutex);
     addCallbackHandle(handle);
 }
 
-void TransactionCompletedThread::addCallbackHandle(const sp<CallbackHandle>& handle,
-                                                   nsecs_t latchTime) {
+void TransactionCompletedThread::addCallbackHandle(const sp<CallbackHandle>& handle) {
     const sp<IBinder> listener = IInterface::asBinder(handle->listener);
 
     // If we don't already have a reference to this listener, linkToDeath so we get a notification
@@ -128,9 +117,9 @@
     listenerStats.listener = handle->listener;
 
     auto& transactionStats = listenerStats.transactionStats[handle->callbackIds];
-    transactionStats.latchTime = latchTime;
+    transactionStats.latchTime = handle->latchTime;
     transactionStats.surfaceStats.emplace_back(handle->surfaceControl, handle->acquireTime,
-                                               handle->releasePreviousBuffer);
+                                               handle->previousReleaseFence);
 }
 
 void TransactionCompletedThread::addPresentFence(const sp<Fence>& presentFence) {
@@ -151,15 +140,6 @@
     while (mKeepRunning) {
         mConditionVariable.wait(mMutex);
 
-        // We should never hit this case. The release fences from the previous frame should have
-        // signaled long before the current frame is presented.
-        for (const auto& fence : mPreviousReleaseFences) {
-            status_t status = fence->wait(100);
-            if (status != NO_ERROR) {
-                ALOGE("previous release fence has not signaled, err %d", status);
-            }
-        }
-
         // For each listener
         auto it = mListenerStats.begin();
         while (it != mListenerStats.end()) {
@@ -200,7 +180,6 @@
 
         if (mPresentFence) {
             mPresentFence.clear();
-            mPreviousReleaseFences.clear();
         }
     }
 }
diff --git a/services/surfaceflinger/TransactionCompletedThread.h b/services/surfaceflinger/TransactionCompletedThread.h
index 1612f69..f49306d 100644
--- a/services/surfaceflinger/TransactionCompletedThread.h
+++ b/services/surfaceflinger/TransactionCompletedThread.h
@@ -40,7 +40,9 @@
     sp<IBinder> surfaceControl;
 
     bool releasePreviousBuffer = false;
+    sp<Fence> previousReleaseFence;
     nsecs_t acquireTime = -1;
+    nsecs_t latchTime = -1;
 };
 
 class TransactionCompletedThread {
@@ -54,14 +56,13 @@
     // layer has received the CallbackHandle so the TransactionCompletedThread knows not to send
     // a callback for that Listener/Transaction pair until that CallbackHandle has been latched and
     // presented.
-    void registerPendingLatchedCallbackHandle(const sp<CallbackHandle>& handle);
-    // Notifies the TransactionCompletedThread that a pending CallbackHandle has been latched.
-    void addLatchedCallbackHandles(const std::deque<sp<CallbackHandle>>& handles, nsecs_t latchTime,
-                                   const sp<Fence>& previousReleaseFence);
+    void registerPendingCallbackHandle(const sp<CallbackHandle>& handle);
+    // Notifies the TransactionCompletedThread that a pending CallbackHandle has been presented.
+    void addPresentedCallbackHandles(const std::deque<sp<CallbackHandle>>& handles);
 
     // Adds the Transaction CallbackHandle from a layer that does not need to be relatched and
     // presented this frame.
-    void addUnlatchedCallbackHandle(const sp<CallbackHandle>& handle);
+    void addUnpresentedCallbackHandle(const sp<CallbackHandle>& handle);
 
     void addPresentFence(const sp<Fence>& presentFence);
 
@@ -70,8 +71,7 @@
 private:
     void threadMain();
 
-    void addCallbackHandle(const sp<CallbackHandle>& handle, nsecs_t latchTime = -1)
-            REQUIRES(mMutex);
+    void addCallbackHandle(const sp<CallbackHandle>& handle) REQUIRES(mMutex);
 
     class ThreadDeathRecipient : public IBinder::DeathRecipient {
     public:
@@ -110,7 +110,6 @@
     bool mKeepRunning GUARDED_BY(mMutex) = true;
 
     sp<Fence> mPresentFence GUARDED_BY(mMutex);
-    std::vector<sp<Fence>> mPreviousReleaseFences GUARDED_BY(mMutex);
 };
 
 } // namespace android
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/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index 92ae87b..e7986d3 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -21,30 +21,35 @@
 #include <android/frameworks/displayservice/1.0/IDisplayService.h>
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
 #include <android/hardware/graphics/allocator/2.0/IAllocator.h>
+#include <android/hardware/graphics/allocator/3.0/IAllocator.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/ProcessState.h>
 #include <configstore/Utils.h>
-#include <cutils/sched_policy.h>
 #include <displayservice/DisplayService.h>
 #include <hidl/LegacySupport.h>
+#include <processgroup/sched_policy.h>
 #include "SurfaceFlinger.h"
 #include "SurfaceFlingerFactory.h"
+#include "SurfaceFlingerProperties.h"
 
 using namespace android;
 
 static status_t startGraphicsAllocatorService() {
     using android::hardware::configstore::getBool;
     using android::hardware::configstore::V1_0::ISurfaceFlingerConfigs;
-    if (!getBool<ISurfaceFlingerConfigs,
-            &ISurfaceFlingerConfigs::startGraphicsAllocatorService>(false)) {
+    if (!android::sysprop::start_graphics_allocator_service(false)) {
         return OK;
     }
 
-    using android::hardware::graphics::allocator::V2_0::IAllocator;
+    status_t result = hardware::registerPassthroughServiceImplementation<
+            android::hardware::graphics::allocator::V3_0::IAllocator>();
+    if (result == OK) {
+        return OK;
+    }
 
-    status_t result =
-        hardware::registerPassthroughServiceImplementation<IAllocator>();
+    result = hardware::registerPassthroughServiceImplementation<
+            android::hardware::graphics::allocator::V2_0::IAllocator>();
     if (result != OK) {
         ALOGE("could not start graphics allocator service");
         return result;
diff --git a/services/surfaceflinger/surfaceflinger.rc b/services/surfaceflinger/surfaceflinger.rc
index aea602b..5bec502 100644
--- a/services/surfaceflinger/surfaceflinger.rc
+++ b/services/surfaceflinger/surfaceflinger.rc
@@ -2,6 +2,7 @@
     class core animation
     user system
     group graphics drmrpc readproc
+    updatable
     onrestart restart zygote
     writepid /dev/stune/foreground/tasks
     socket pdx/system/vr/display/client     stream 0666 system graphics u:object_r:pdx_display_client_endpoint_socket:s0
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
new file mode 100644
index 0000000..cc7b280
--- /dev/null
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -0,0 +1,252 @@
+# 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.
+
+module: "android.sysprop.SurfaceFlingerProperties"
+owner: Platform
+
+# The following two propertiess define (respectively):
+#
+# - The phase offset between hardware vsync and when apps are woken up by the
+#   Choreographer callback
+# - The phase offset between hardware vsync and when SurfaceFlinger wakes up
+#   to consume input
+# Their values may be tuned to trade off between display pipeline latency (both
+# overall latency and the lengths of the app --> SF and SF --> display phases)
+# and frame delivery jitter (which typically manifests as "jank" or "jerkiness"
+# while interacting with the device). The default values must produce a
+# relatively low amount of jitter at the expense of roughly two frames of
+# app --> display latency, and unless significant testing is performed to avoid
+# increased display jitter (both manual investigation using systrace [1] and
+# automated testing using dumpsys gfxinfo [2] are recommended), they should not
+# be modified.
+#
+# [1] https://developer.android.com/studio/profile/systrace.html
+# [2] https://developer.android.com/training/testing/performance.html
+prop {
+    api_name: "vsync_event_phase_offset_ns"
+    type: Long
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.vsync_event_phase_offset_ns"
+}
+
+prop {
+    api_name: "vsync_sf_event_phase_offset_ns"
+    type: Long
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.vsync_sf_event_phase_offset_ns"
+}
+
+# Instruct the Render Engine to use EGL_IMG_context_priority hint if available.
+prop {
+    api_name: "use_context_priority"
+    type: Boolean
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.use_context_priority"
+}
+
+# Controls the number of buffers SurfaceFlinger will allocate for use in FramebufferSurface.
+prop {
+    api_name: "max_frame_buffer_acquired_buffers"
+    type: Long
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.max_frame_buffer_acquired_buffers"
+}
+
+# hasWideColorDisplay indicates that the device has
+# or can support a wide-color display, e.g. color space
+# greater than sRGB. Typical display may have same
+# color primaries as DCI-P3.
+# Indicate support for this feature by setting
+# TARGET_HAS_WIDE_COLOR_DISPLAY to true in BoardConfig.mk
+# This also means that the device is color managed.
+# A color managed device will use the appropriate
+# display mode depending on the content on the screen.
+# Default is sRGB.
+prop {
+    api_name: "has_wide_color_display"
+    type: Boolean
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.has_wide_color_display"
+}
+
+# Indicates if Sync framework is available. Sync framework provides fence
+# mechanism which significantly reduces buffer processing latency.
+prop {
+    api_name: "running_without_sync_framework"
+    type: Boolean
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.running_without_sync_framework"
+}
+
+# hwHDRDisplay indicates that the device has an High Dynamic Range display.
+# A display is considered High Dynamic Range if it
+#
+#     1. is a wide color gamut display, typically DCI-P3 or lager
+#     2. has high luminance capability, typically 540 nits or higher at 10% OPR
+#
+# Indicate support for this feature by setting
+# ro.surface_flinger.has_HDR_display to true in device.mk
+# ro.surface_flinger.has_wide_color_display must be set to true when
+# ro.surface_flinger.has_HDR_display is true.
+prop {
+    api_name: "has_HDR_display"
+    type: Boolean
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.has_HDR_display"
+}
+
+# Specify the offset in nanoseconds to add to vsync time when timestamping present fences.
+prop {
+    api_name: "present_time_offset_from_vsync_ns"
+    type: Long
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.present_time_offset_from_vsync_ns"
+}
+
+# Some hardware can do RGB->YUV conversion more efficiently in hardware
+# controlled by HWC than in hardware controlled by the video encoder.
+# This instruct VirtualDisplaySurface to use HWC for such conversion on
+# GL composition.
+prop {
+    api_name: "force_hwc_copy_for_virtual_displays"
+    type: Boolean
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.force_hwc_copy_for_virtual_displays"
+}
+
+# Maximum dimension supported by HWC for virtual display.
+# Must be equals to min(max_width, max_height).
+prop {
+    api_name: "max_virtual_display_dimension"
+    type: Long
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.max_virtual_display_dimension"
+}
+
+# Return true if surface flinger should use vr flinger for compatible vr
+# apps, false otherwise. Devices that will never be running vr apps should
+# return false to avoid extra resource usage. Daydream ready devices must
+# return true for full vr support.
+prop {
+    api_name: "use_vr_flinger"
+    type: Boolean
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.use_vr_flinger"
+}
+
+# Returns true if surface flinger should start
+# hardware.graphics.allocator@2.0::IAllocator service.
+prop {
+    api_name: "start_graphics_allocator_service"
+    type: Boolean
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.start_graphics_allocator_service"
+}
+
+# Returns the orientation of the primary display device.
+prop {
+    api_name: "primary_display_orientation"
+    type: Enum
+    enum_values: "ORIENTATION_0|ORIENTATION_90|ORIENTATION_180|ORIENTATION_270"
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.primary_display_orientation"
+}
+
+# useColorManagement indicates whether SurfaceFlinger should manage color
+# by switching to appropriate color mode automatically depending on the
+# Dataspace of the surfaces on screen.
+prop {
+    api_name: "use_color_management"
+    type: Boolean
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.use_color_management"
+}
+
+# The following four propertiess define:
+# Returns the default data space and pixel format that SurfaceFlinger
+# expects to receive and output as well as the wide color gamut data space
+# and pixel format for wide color gamut surfaces.
+# To determine the data space and pixel format, there are a few things
+# we recommend to consider:
+#
+#   1. Hardware composer's capability to composite contents with the chosen
+#      data space and pixel format efficiently;
+#   2. Hardware composer's ability to composite contents when sRGB contents
+#      and the chosen wide color gamut data space contents coexist;
+#   3. For better blending, consider using pixel format where the alpha
+#      channel has as many bits as the RGB color channel.
+#   4. Memory consumption and efficient buffer compression when considering
+#      more bits in pixel format.
+
+# dataspace is the default data space that SurfaceFlinger expects.
+# The data space must not be Dataspace::UNKNOWN, if unspecified,
+# the default data space is Dataspace::V0_SRGB;
+prop {
+    api_name: "default_composition_dataspace"
+    type: Long
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.default_composition_dataspace"
+}
+
+# pixelFormat is the default pixel format that SurfaceFlinger
+# expects. If unspecified, the default pixel format is
+# PixelFormat::RGBA_8888.
+prop {
+    api_name: "default_composition_pixel_format"
+    type: Integer
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.default_composition_pixel_format"
+}
+
+# wcgDataspace is the data space that SurfaceFlinger expects for
+# wide color gamut surfaces.
+# When hasWideColorDisplay returns true, this API must return a
+# valid wide color gamut data space.
+# The data space must not be UNKNOWN, if unspecified, the data space
+# is V0_SRGB by default, which essentially indicates there's no wide
+# color gamut, meaning hasWideColorDisplay returns false.
+prop {
+    api_name: "wcg_composition_dataspace"
+    type: Long
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.wcg_composition_dataspace"
+}
+
+# wcgPixelFormat is the pixel format that SurfaceFlinger expects for
+# wide color gamut surfaces. If unspecified, the pixel format is
+# PixelFormat::RGBA_8888 by default.
+prop {
+    api_name: "wcg_composition_pixel_format"
+    type: Integer
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.wcg_composition_pixel_format"
+}
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index 8560f5e..48b2b80 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");
@@ -312,4 +313,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 2283b87..72c75bb 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -304,7 +304,7 @@
 
     void expectChildColor(uint32_t x, uint32_t y) { checkPixel(x, y, 200, 200, 200); }
 
-    ScreenCapture(const sp<GraphicBuffer>& outBuffer) : mOutBuffer(outBuffer) {
+    explicit ScreenCapture(const sp<GraphicBuffer>& outBuffer) : mOutBuffer(outBuffer) {
         mOutBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, reinterpret_cast<void**>(&mPixels));
     }
 
@@ -2435,6 +2435,160 @@
     Transaction().setSidebandStream(layer, nullptr).apply();
 }
 
+TEST_F(LayerTransactionTest, CacheBuffer_BufferState) {
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+
+    int32_t bufferId = -1;
+    ASSERT_EQ(NO_ERROR, mClient->cacheBuffer(buffer, &bufferId));
+    ASSERT_GE(bufferId, 0);
+
+    ASSERT_EQ(NO_ERROR, mClient->uncacheBuffer(bufferId));
+}
+
+TEST_F(LayerTransactionTest, CacheBuffers_BufferState) {
+    std::vector<int32_t> bufferIds;
+    int32_t bufferCount = 20;
+
+    for (int i = 0; i < bufferCount; i++) {
+        sp<GraphicBuffer> buffer =
+                new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                                  BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                          BufferUsage::COMPOSER_OVERLAY,
+                                  "test");
+        int32_t bufferId = -1;
+        ASSERT_EQ(NO_ERROR, mClient->cacheBuffer(buffer, &bufferId));
+        if (bufferId < 0) {
+            EXPECT_GE(bufferId, 0);
+            break;
+        }
+        bufferIds.push_back(bufferId);
+    }
+
+    for (int32_t bufferId : bufferIds) {
+        ASSERT_EQ(NO_ERROR, mClient->uncacheBuffer(bufferId));
+    }
+}
+
+TEST_F(LayerTransactionTest, CacheBufferInvalid_BufferState) {
+    sp<GraphicBuffer> buffer = nullptr;
+
+    int32_t bufferId = -1;
+    ASSERT_NE(NO_ERROR, mClient->cacheBuffer(buffer, &bufferId));
+    ASSERT_LT(bufferId, 0);
+}
+
+TEST_F(LayerTransactionTest, UncacheBufferTwice_BufferState) {
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+
+    int32_t bufferId = -1;
+    ASSERT_EQ(NO_ERROR, mClient->cacheBuffer(buffer, &bufferId));
+    ASSERT_GE(bufferId, 0);
+
+    ASSERT_EQ(NO_ERROR, mClient->uncacheBuffer(bufferId));
+    mClient->uncacheBuffer(bufferId);
+}
+
+TEST_F(LayerTransactionTest, UncacheBufferInvalidId_BufferState) {
+    mClient->uncacheBuffer(-1);
+    mClient->uncacheBuffer(0);
+    mClient->uncacheBuffer(1);
+    mClient->uncacheBuffer(5);
+    mClient->uncacheBuffer(1000);
+}
+
+TEST_F(LayerTransactionTest, SetCachedBuffer_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+
+    fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
+
+    int32_t bufferId = -1;
+    ASSERT_EQ(NO_ERROR, mClient->cacheBuffer(buffer, &bufferId));
+    ASSERT_GE(bufferId, 0);
+
+    Transaction().setCachedBuffer(layer, bufferId).apply();
+
+    auto shot = screenshot();
+    shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+
+    ASSERT_EQ(NO_ERROR, mClient->uncacheBuffer(bufferId));
+}
+
+TEST_F(LayerTransactionTest, SetCachedBufferDelayed_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    sp<GraphicBuffer> cachedBuffer =
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+    int32_t bufferId = -1;
+    ASSERT_EQ(NO_ERROR, mClient->cacheBuffer(cachedBuffer, &bufferId));
+    ASSERT_GE(bufferId, 0);
+
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+    fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::BLUE);
+    Transaction().setBuffer(layer, buffer).apply();
+    {
+        SCOPED_TRACE("Uncached buffer");
+
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+        shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+
+    fillGraphicBufferColor(cachedBuffer, Rect(0, 0, 32, 32), Color::RED);
+    Transaction().setCachedBuffer(layer, bufferId).apply();
+    {
+        SCOPED_TRACE("Cached buffer");
+
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+
+    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) {
@@ -2639,6 +2793,17 @@
     }
 }
 
+struct CallbackData {
+    CallbackData() = default;
+    CallbackData(nsecs_t time, const sp<Fence>& fence,
+                 const std::vector<SurfaceControlStats>& stats)
+          : latchTime(time), presentFence(fence), surfaceControlStats(stats) {}
+
+    nsecs_t latchTime;
+    sp<Fence> presentFence;
+    std::vector<SurfaceControlStats> surfaceControlStats;
+};
+
 class ExpectedResult {
 public:
     enum Transaction {
@@ -2654,6 +2819,7 @@
     enum PreviousBuffer {
         NOT_RELEASED = 0,
         RELEASED,
+        UNKNOWN,
     };
 
     void reset() {
@@ -2665,8 +2831,7 @@
                     ExpectedResult::Buffer bufferResult = ACQUIRED,
                     ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) {
         mTransactionResult = transactionResult;
-        mExpectedSurfaceResults.emplace(std::piecewise_construct,
-                                        std::forward_as_tuple(layer->getHandle()),
+        mExpectedSurfaceResults.emplace(std::piecewise_construct, std::forward_as_tuple(layer),
                                         std::forward_as_tuple(bufferResult, previousBufferResult));
     }
 
@@ -2679,24 +2844,38 @@
         }
     }
 
-    void verifyTransactionStats(const TransactionStats& transactionStats) const {
-        const auto& [latchTime, presentFence, surfaceStats] = transactionStats;
+    void addExpectedPresentTime(nsecs_t expectedPresentTime) {
+        mExpectedPresentTime = expectedPresentTime;
+    }
+
+    void verifyCallbackData(const CallbackData& callbackData) const {
+        const auto& [latchTime, presentFence, surfaceControlStats] = callbackData;
         if (mTransactionResult == ExpectedResult::Transaction::PRESENTED) {
             ASSERT_GE(latchTime, 0) << "bad latch time";
             ASSERT_NE(presentFence, nullptr);
+            if (mExpectedPresentTime >= 0) {
+                ASSERT_EQ(presentFence->wait(3000), NO_ERROR);
+                ASSERT_GE(presentFence->getSignalTime(), mExpectedPresentTime - nsecs_t(5 * 1e6));
+                // if the panel is running at 30 hz, at the worst case, our expected time just
+                // misses vsync and we have to wait another 33.3ms
+                ASSERT_LE(presentFence->getSignalTime(),
+                          mExpectedPresentTime + nsecs_t(66.666666 * 1e6));
+            }
         } else {
             ASSERT_EQ(presentFence, nullptr) << "transaction shouldn't have been presented";
             ASSERT_EQ(latchTime, -1) << "unpresented transactions shouldn't be latched";
         }
 
-        ASSERT_EQ(surfaceStats.size(), mExpectedSurfaceResults.size())
+        ASSERT_EQ(surfaceControlStats.size(), mExpectedSurfaceResults.size())
                 << "wrong number of surfaces";
 
-        for (const auto& stats : surfaceStats) {
+        for (const auto& stats : surfaceControlStats) {
+            ASSERT_NE(stats.surfaceControl, nullptr) << "returned null surface control";
+
             const auto& expectedSurfaceResult = mExpectedSurfaceResults.find(stats.surfaceControl);
             ASSERT_NE(expectedSurfaceResult, mExpectedSurfaceResults.end())
                     << "unexpected surface control";
-            expectedSurfaceResult->second.verifySurfaceStats(stats, latchTime);
+            expectedSurfaceResult->second.verifySurfaceControlStats(stats, latchTime);
         }
     }
 
@@ -2707,15 +2886,21 @@
                               ExpectedResult::PreviousBuffer previousBufferResult)
               : mBufferResult(bufferResult), mPreviousBufferResult(previousBufferResult) {}
 
-        void verifySurfaceStats(const SurfaceStats& surfaceStats, nsecs_t latchTime) const {
-            const auto& [surfaceControl, acquireTime, releasePreviousBuffer] = surfaceStats;
+        void verifySurfaceControlStats(const SurfaceControlStats& surfaceControlStats,
+                                       nsecs_t latchTime) const {
+            const auto& [surfaceControl, acquireTime, previousReleaseFence] = surfaceControlStats;
 
             ASSERT_EQ(acquireTime > 0, mBufferResult == ExpectedResult::Buffer::ACQUIRED)
                     << "bad acquire time";
             ASSERT_LE(acquireTime, latchTime) << "acquire time should be <= latch time";
-            ASSERT_EQ(releasePreviousBuffer,
-                      mPreviousBufferResult == ExpectedResult::PreviousBuffer::RELEASED)
-                    << "bad previous buffer released";
+
+            if (mPreviousBufferResult == ExpectedResult::PreviousBuffer::RELEASED) {
+                ASSERT_NE(previousReleaseFence, nullptr)
+                        << "failed to set release prev buffer fence";
+            } else if (mPreviousBufferResult == ExpectedResult::PreviousBuffer::NOT_RELEASED) {
+                ASSERT_EQ(previousReleaseFence, nullptr)
+                        << "should not have set released prev buffer fence";
+            }
         }
 
     private:
@@ -2723,38 +2908,40 @@
         ExpectedResult::PreviousBuffer mPreviousBufferResult;
     };
 
-    struct IBinderHash {
-        std::size_t operator()(const sp<IBinder>& strongPointer) const {
-            return std::hash<IBinder*>{}(strongPointer.get());
+    struct SCHash {
+        std::size_t operator()(const sp<SurfaceControl>& sc) const {
+            return std::hash<IBinder*>{}(sc->getHandle().get());
         }
     };
     ExpectedResult::Transaction mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED;
-    std::unordered_map<sp<IBinder>, ExpectedSurfaceResult, IBinderHash> mExpectedSurfaceResults;
+    nsecs_t mExpectedPresentTime = -1;
+    std::unordered_map<sp<SurfaceControl>, ExpectedSurfaceResult, SCHash> mExpectedSurfaceResults;
 };
 
 class CallbackHelper {
 public:
-    static void function(void* callbackContext, const TransactionStats& transactionStats) {
+    static void function(void* callbackContext, nsecs_t latchTime, const sp<Fence>& presentFence,
+                         const std::vector<SurfaceControlStats>& stats) {
         if (!callbackContext) {
             ALOGE("failed to get callback context");
         }
         CallbackHelper* helper = static_cast<CallbackHelper*>(callbackContext);
         std::lock_guard lock(helper->mMutex);
-        helper->mTransactionStatsQueue.push(transactionStats);
+        helper->mCallbackDataQueue.emplace(latchTime, presentFence, stats);
         helper->mConditionVariable.notify_all();
     }
 
-    void getTransactionStats(TransactionStats* outStats) {
+    void getCallbackData(CallbackData* outData) {
         std::unique_lock lock(mMutex);
 
-        if (mTransactionStatsQueue.empty()) {
+        if (mCallbackDataQueue.empty()) {
             ASSERT_NE(mConditionVariable.wait_for(lock, std::chrono::seconds(3)),
                       std::cv_status::timeout)
                     << "did not receive callback";
         }
 
-        *outStats = std::move(mTransactionStatsQueue.front());
-        mTransactionStatsQueue.pop();
+        *outData = std::move(mCallbackDataQueue.front());
+        mCallbackDataQueue.pop();
     }
 
     void verifyFinalState() {
@@ -2762,15 +2949,15 @@
         std::this_thread::sleep_for(500ms);
 
         std::lock_guard lock(mMutex);
-        EXPECT_EQ(mTransactionStatsQueue.size(), 0) << "extra callbacks received";
-        mTransactionStatsQueue = {};
+        EXPECT_EQ(mCallbackDataQueue.size(), 0) << "extra callbacks received";
+        mCallbackDataQueue = {};
     }
 
     void* getContext() { return static_cast<void*>(this); }
 
     std::mutex mMutex;
     std::condition_variable mConditionVariable;
-    std::queue<TransactionStats> mTransactionStatsQueue;
+    std::queue<CallbackData> mCallbackDataQueue;
 };
 
 class LayerCallbackTest : public LayerTransactionTest {
@@ -2799,9 +2986,9 @@
 
     static void waitForCallback(CallbackHelper& helper, const ExpectedResult& expectedResult,
                                 bool finalState = false) {
-        TransactionStats transactionStats;
-        ASSERT_NO_FATAL_FAILURE(helper.getTransactionStats(&transactionStats));
-        EXPECT_NO_FATAL_FAILURE(expectedResult.verifyTransactionStats(transactionStats));
+        CallbackData callbackData;
+        ASSERT_NO_FATAL_FAILURE(helper.getCallbackData(&callbackData));
+        EXPECT_NO_FATAL_FAILURE(expectedResult.verifyCallbackData(callbackData));
 
         if (finalState) {
             ASSERT_NO_FATAL_FAILURE(helper.verifyFinalState());
@@ -3289,13 +3476,11 @@
     Transaction transaction;
     CallbackHelper callback;
     std::vector<ExpectedResult> expectedResults(50);
-    ExpectedResult::PreviousBuffer previousBufferResult =
-            ExpectedResult::PreviousBuffer::NOT_RELEASED;
     for (auto& expected : expectedResults) {
         expected.reset();
         expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
-                            ExpectedResult::Buffer::ACQUIRED, previousBufferResult);
-        previousBufferResult = ExpectedResult::PreviousBuffer::RELEASED;
+                            ExpectedResult::Buffer::ACQUIRED,
+                            ExpectedResult::PreviousBuffer::UNKNOWN);
 
         int err = fillTransaction(transaction, &callback, layer);
         if (err) {
@@ -3380,6 +3565,143 @@
     EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
 }
 
+TEST_F(LayerCallbackTest, DesiredPresentTime) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    int err = fillTransaction(transaction, &callback, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    // Try to present 100ms in the future
+    nsecs_t time = systemTime() + (100 * 1e6);
+
+    transaction.setDesiredPresentTime(time);
+    transaction.apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    expected.addExpectedPresentTime(time);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, DesiredPresentTime_Multiple) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback1;
+    int err = fillTransaction(transaction, &callback1, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    // Try to present 100ms in the future
+    nsecs_t time = systemTime() + (100 * 1e6);
+
+    transaction.setDesiredPresentTime(time);
+    transaction.apply();
+
+    ExpectedResult expected1;
+    expected1.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    expected1.addExpectedPresentTime(time);
+
+    CallbackHelper callback2;
+    err = fillTransaction(transaction, &callback2, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    // Try to present 33ms after the first frame
+    time += (33.3 * 1e6);
+
+    transaction.setDesiredPresentTime(time);
+    transaction.apply();
+
+    ExpectedResult expected2;
+    expected2.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                         ExpectedResult::Buffer::ACQUIRED,
+                         ExpectedResult::PreviousBuffer::RELEASED);
+    expected2.addExpectedPresentTime(time);
+
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected1, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected2, true));
+}
+
+TEST_F(LayerCallbackTest, DesiredPresentTime_OutOfOrder) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback1;
+    int err = fillTransaction(transaction, &callback1, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    // Try to present 100ms in the future
+    nsecs_t time = systemTime() + (100 * 1e6);
+
+    transaction.setDesiredPresentTime(time);
+    transaction.apply();
+
+    ExpectedResult expected1;
+    expected1.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    expected1.addExpectedPresentTime(time);
+
+    CallbackHelper callback2;
+    err = fillTransaction(transaction, &callback2, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    // Try to present 33ms before the previous frame
+    time -= (33.3 * 1e6);
+
+    transaction.setDesiredPresentTime(time);
+    transaction.apply();
+
+    ExpectedResult expected2;
+    expected2.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                         ExpectedResult::Buffer::ACQUIRED,
+                         ExpectedResult::PreviousBuffer::RELEASED);
+
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected1, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected2, true));
+}
+
+TEST_F(LayerCallbackTest, DesiredPresentTime_Past) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    int err = fillTransaction(transaction, &callback, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    // Try to present 100ms in the past
+    nsecs_t time = systemTime() - (100 * 1e6);
+
+    transaction.setDesiredPresentTime(time);
+    transaction.apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    expected.addExpectedPresentTime(systemTime());
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
 class LayerUpdateTest : public LayerTransactionTest {
 protected:
     virtual void SetUp() {
diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp
index a5b522e..a2c0611 100644
--- a/services/surfaceflinger/tests/fakehwc/Android.bp
+++ b/services/surfaceflinger/tests/fakehwc/Android.bp
@@ -11,6 +11,7 @@
     shared_libs: [
         "android.hardware.graphics.composer@2.1",
         "android.hardware.graphics.mapper@2.0",
+        "android.hardware.graphics.mapper@3.0",
         "android.hardware.power@1.3",
         "libbase",
         "libbinder",
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
index 973156a..eeb6efe 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
@@ -81,7 +81,7 @@
 
 class DelayedEventGenerator {
 public:
-    DelayedEventGenerator(std::function<void()> onTimerExpired)
+    explicit DelayedEventGenerator(std::function<void()> onTimerExpired)
           : mOnTimerExpired(onTimerExpired), mThread([this]() { loop(); }) {}
 
     ~DelayedEventGenerator() {
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerService.h b/services/surfaceflinger/tests/fakehwc/FakeComposerService.h
index c439b7e..a3fb8a6 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerService.h
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerService.h
@@ -26,7 +26,7 @@
 
 class FakeComposerService : public IComposer {
 public:
-    FakeComposerService(android::sp<ComposerClient>& client);
+    explicit FakeComposerService(android::sp<ComposerClient>& client);
     virtual ~FakeComposerService();
 
     Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h
index 1258a97..7d20d3c 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h
@@ -100,10 +100,7 @@
  */
 class TransactionScope : public android::SurfaceComposerClient::Transaction {
 public:
-    TransactionScope(FakeComposerClient& composer) :
-            Transaction(),
-            mComposer(composer) {
-    }
+    explicit TransactionScope(FakeComposerClient& composer) : Transaction(), mComposer(composer) {}
 
     ~TransactionScope() {
         int frameCount = mComposer.getFrameCount();
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.h b/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.h
index d7082f3..06ae314 100644
--- a/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.h
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.h
@@ -171,7 +171,7 @@
 
 class Hwc2TestBlendMode : public Hwc2TestProperty<hwc2_blend_mode_t> {
 public:
-    Hwc2TestBlendMode(Hwc2TestCoverage coverage);
+    explicit Hwc2TestBlendMode(Hwc2TestCoverage coverage);
 
     std::string dump() const override;
 
@@ -192,8 +192,8 @@
 
 class Hwc2TestColor : public Hwc2TestProperty<hwc_color_t> {
 public:
-    Hwc2TestColor(Hwc2TestCoverage coverage,
-            hwc2_blend_mode_t blendMode = HWC2_BLEND_MODE_NONE);
+    explicit Hwc2TestColor(Hwc2TestCoverage coverage,
+                           hwc2_blend_mode_t blendMode = HWC2_BLEND_MODE_NONE);
 
     std::string dump() const override;
 
@@ -217,7 +217,7 @@
 
 class Hwc2TestComposition : public Hwc2TestProperty<hwc2_composition_t> {
 public:
-    Hwc2TestComposition(Hwc2TestCoverage coverage);
+    explicit Hwc2TestComposition(Hwc2TestCoverage coverage);
 
     std::string dump() const override;
 
@@ -232,7 +232,7 @@
 
 class Hwc2TestDataspace : public Hwc2TestProperty<android::ui::Dataspace> {
 public:
-    Hwc2TestDataspace(Hwc2TestCoverage coverage);
+    explicit Hwc2TestDataspace(Hwc2TestCoverage coverage);
 
     std::string dump() const override;
 
@@ -248,7 +248,7 @@
 
 class Hwc2TestDisplayDimension : public Hwc2TestProperty<UnsignedArea> {
 public:
-    Hwc2TestDisplayDimension(Hwc2TestCoverage coverage);
+    explicit Hwc2TestDisplayDimension(Hwc2TestCoverage coverage);
 
     std::string dump() const;
 
@@ -291,7 +291,7 @@
 
 class Hwc2TestPlaneAlpha : public Hwc2TestProperty<float> {
 public:
-    Hwc2TestPlaneAlpha(Hwc2TestCoverage coverage);
+    explicit Hwc2TestPlaneAlpha(Hwc2TestCoverage coverage);
 
     std::string dump() const override;
 
@@ -306,7 +306,7 @@
 
 class Hwc2TestSourceCrop : public Hwc2TestProperty<hwc_frect_t> {
 public:
-    Hwc2TestSourceCrop(Hwc2TestCoverage coverage, const Area& bufferArea = {0, 0});
+    explicit Hwc2TestSourceCrop(Hwc2TestCoverage coverage, const Area& bufferArea = {0, 0});
 
     std::string dump() const override;
 
@@ -330,7 +330,7 @@
 
 class Hwc2TestSurfaceDamage : public Hwc2TestProperty<hwc_region_t> {
 public:
-    Hwc2TestSurfaceDamage(Hwc2TestCoverage coverage);
+    explicit Hwc2TestSurfaceDamage(Hwc2TestCoverage coverage);
     ~Hwc2TestSurfaceDamage();
 
     std::string dump() const override;
@@ -356,7 +356,7 @@
 
 class Hwc2TestTransform : public Hwc2TestProperty<hwc_transform_t> {
 public:
-    Hwc2TestTransform(Hwc2TestCoverage coverage);
+    explicit Hwc2TestTransform(Hwc2TestCoverage coverage);
 
     std::string dump() const override;
 
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.h b/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.h
index 10c8ef0..5a74a6c 100644
--- a/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.h
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.h
@@ -29,7 +29,7 @@
 
 class Hwc2TestVirtualDisplay {
 public:
-    Hwc2TestVirtualDisplay(Hwc2TestCoverage coverage);
+    explicit Hwc2TestVirtualDisplay(Hwc2TestCoverage coverage);
 
     std::string dump() const;
 
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index f63c911..b2bcb45 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -42,11 +42,13 @@
         "EventThreadTest.cpp",
         "IdleTimerTest.cpp",
         "LayerHistoryTest.cpp",
+        "LayerMetadataTest.cpp",
         "SchedulerTest.cpp",
         "SchedulerUtilsTest.cpp",
+        "RefreshRateStatsTest.cpp",
         "TimeStatsTest.cpp",
         "mock/DisplayHardware/MockComposer.cpp",
-        "mock/DisplayHardware/MockDisplaySurface.cpp",
+        "mock/DisplayHardware/MockDisplay.cpp",
         "mock/DisplayHardware/MockPowerAdvisor.cpp",
         "mock/gui/MockGraphicBufferConsumer.cpp",
         "mock/gui/MockGraphicBufferProducer.cpp",
@@ -63,7 +65,6 @@
         "libcompositionengine",
         "libcompositionengine_mocks",
         "librenderengine_mocks",
-
     ],
     header_libs: [
         "libsurfaceflinger_headers",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index e417350..e972785 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -17,6 +17,7 @@
 #undef LOG_TAG
 #define LOG_TAG "CompositionTest"
 
+#include <compositionengine/mock/DisplaySurface.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <gui/IProducerListener.h>
@@ -33,7 +34,6 @@
 
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
-#include "mock/DisplayHardware/MockDisplaySurface.h"
 #include "mock/MockDispSync.h"
 #include "mock/MockEventControlThread.h"
 #include "mock/MockEventThread.h"
@@ -144,7 +144,8 @@
     TestableSurfaceFlinger mFlinger;
     sp<DisplayDevice> mDisplay;
     sp<DisplayDevice> mExternalDisplay;
-    sp<mock::DisplaySurface> mDisplaySurface = new mock::DisplaySurface();
+    sp<compositionengine::mock::DisplaySurface> mDisplaySurface =
+            new compositionengine::mock::DisplaySurface();
     mock::NativeWindow* mNativeWindow = new mock::NativeWindow();
 
     sp<GraphicBuffer> mBuffer = new GraphicBuffer();
@@ -326,11 +327,14 @@
     }
 
     static void setupHwcCompositionCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_HWC)).Times(1);
+        EXPECT_CALL(*test->mDisplaySurface,
+                    prepareFrame(compositionengine::DisplaySurface::COMPOSITION_HWC))
+                .Times(1);
     }
 
     static void setupRECompositionCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_GLES))
+        EXPECT_CALL(*test->mDisplaySurface,
+                    prepareFrame(compositionengine::DisplaySurface::COMPOSITION_GLES))
                 .Times(1);
         EXPECT_CALL(*test->mDisplaySurface, getClientTargetAcquireFence())
                 .WillRepeatedly(ReturnRef(test->mClientTargetAcquireFence));
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index ff84a62..2c1833b 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -19,6 +19,9 @@
 
 #include <type_traits>
 
+#include <compositionengine/Display.h>
+#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/mock/DisplaySurface.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <log/log.h>
@@ -28,7 +31,6 @@
 #include "DisplayIdentificationTest.h"
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
-#include "mock/DisplayHardware/MockDisplaySurface.h"
 #include "mock/MockDispSync.h"
 #include "mock/MockEventControlThread.h"
 #include "mock/MockEventThread.h"
@@ -116,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();
@@ -159,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);
@@ -317,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(),
@@ -1152,8 +1161,10 @@
         EXPECT_CALL(*mNativeWindow, perform(30)).Times(1);
         auto displayDevice = mInjector.inject();
 
-        displayDevice->getBestColorMode(mInputDataspace, mInputRenderIntent, &mOutDataspace,
-                                        &mOutColorMode, &mOutRenderIntent);
+        displayDevice->getCompositionDisplay()
+                ->getDisplayColorProfile()
+                ->getBestColorMode(mInputDataspace, mInputRenderIntent, &mOutDataspace,
+                                   &mOutColorMode, &mOutRenderIntent);
     }
 
     ui::Dataspace mOutDataspace;
@@ -1227,7 +1238,8 @@
 template <typename Case>
 void SetupNewDisplayDeviceInternalTest::setupNewDisplayDeviceInternalTest() {
     const sp<BBinder> displayToken = new BBinder();
-    const sp<mock::DisplaySurface> displaySurface = new mock::DisplaySurface();
+    const sp<compositionengine::mock::DisplaySurface> displaySurface =
+            new compositionengine::mock::DisplaySurface();
     const sp<mock::GraphicBufferProducer> producer = new mock::GraphicBufferProducer();
 
     // --------------------------------------------------------------------
@@ -1399,23 +1411,17 @@
     Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
 
     EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
-    EXPECT_CALL(*mEventThread,
-                onHotplugReceived(static_cast<bool>(Case::Display::PRIMARY)
-                                          ? EventThread::DisplayType::Primary
-                                          : EventThread::DisplayType::External,
-                                  true))
-            .Times(1);
+
+    EXPECT_CALL(*mEventThread, onHotplugReceived(Case::Display::displayType(), true)).Times(1);
+    EXPECT_CALL(*mSFEventThread, onHotplugReceived(Case::Display::displayType(), true)).Times(1);
 }
 
 template <typename Case>
 void HandleTransactionLockedTest::setupCommonCallExpectationsForDisconnectProcessing() {
     EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
-    EXPECT_CALL(*mEventThread,
-                onHotplugReceived(static_cast<bool>(Case::Display::PRIMARY)
-                                          ? EventThread::DisplayType::Primary
-                                          : EventThread::DisplayType::External,
-                                  false))
-            .Times(1);
+
+    EXPECT_CALL(*mEventThread, onHotplugReceived(Case::Display::displayType(), false)).Times(1);
+    EXPECT_CALL(*mSFEventThread, onHotplugReceived(Case::Display::displayType(), false)).Times(1);
 }
 
 template <typename Case>
@@ -1970,7 +1976,7 @@
 
     // A display is set up
     auto nativeWindow = new mock::NativeWindow();
-    auto displaySurface = new mock::DisplaySurface();
+    auto displaySurface = new compositionengine::mock::DisplaySurface();
     sp<GraphicBuffer> buf = new GraphicBuffer();
     auto display = Case::Display::makeFakeExistingDisplayInjector(this);
     display.setNativeWindow(nativeWindow);
@@ -2014,7 +2020,7 @@
 
     // A display is set up
     auto nativeWindow = new mock::NativeWindow();
-    auto displaySurface = new mock::DisplaySurface();
+    auto displaySurface = new compositionengine::mock::DisplaySurface();
     sp<GraphicBuffer> buf = new GraphicBuffer();
     auto display = Case::Display::makeFakeExistingDisplayInjector(this);
     display.setNativeWindow(nativeWindow);
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 2d26bb3..79fb034 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -47,10 +47,11 @@
 
 class EventThreadTest : public testing::Test {
 protected:
-    class MockEventThreadConnection : public android::EventThreadConnection {
+    class MockEventThreadConnection : public EventThreadConnection {
     public:
-        explicit MockEventThreadConnection(android::impl::EventThread* eventThread)
-              : android::EventThreadConnection(eventThread) {}
+        MockEventThreadConnection(android::impl::EventThread* eventThread,
+                                  ResyncCallback&& resyncCallback)
+              : EventThreadConnection(eventThread, std::move(resyncCallback)) {}
         MOCK_METHOD1(postEvent, status_t(const DisplayEventReceiver::Event& event));
     };
 
@@ -82,6 +83,7 @@
     ConnectionEventRecorder mConnectionEventCallRecorder{0};
 
     MockVSyncSource mVSyncSource;
+    VSyncSource::Callback* mCallback = nullptr;
     std::unique_ptr<android::impl::EventThread> mThread;
     sp<MockEventThreadConnection> mConnection;
 };
@@ -102,25 +104,36 @@
 
     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() {
     mThread =
             std::make_unique<android::impl::EventThread>(&mVSyncSource,
-                                                         mResyncCallRecorder.getInvocable(),
                                                          mInterceptVSyncCallRecorder.getInvocable(),
                                                          "unit-test-event-thread");
+
+    // EventThread should register itself as VSyncSource callback.
+    mCallback = expectVSyncSetCallbackCallReceived();
+    ASSERT_TRUE(mCallback);
 }
 
 sp<EventThreadTest::MockEventThreadConnection> EventThreadTest::createConnection(
         ConnectionEventRecorder& recorder) {
-    sp<MockEventThreadConnection> connection = new MockEventThreadConnection(mThread.get());
+    sp<MockEventThreadConnection> connection =
+            new MockEventThreadConnection(mThread.get(), mResyncCallRecorder.getInvocable());
     EXPECT_CALL(*connection, postEvent(_)).WillRepeatedly(Invoke(recorder.getInvocable()));
     return connection;
 }
@@ -198,6 +211,23 @@
     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());
+
+    // Use the received callback to signal a vsync event.
+    // The event should not be received by the interceptor nor the connection.
+    mCallback->onVSyncEvent(123);
+    EXPECT_FALSE(mInterceptVSyncCallRecorder.waitForCall(0us).has_value());
+    EXPECT_FALSE(mConnectionEventCallRecorder.waitForCall(0us).has_value());
+}
+
 TEST_F(EventThreadTest, requestNextVsyncPostsASingleVSyncEventToTheConnection) {
     // Signal that we want the next vsync event to be posted to the connection
     mThread->requestNextVsync(mConnection, false);
@@ -205,22 +235,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());
 
@@ -245,16 +272,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,
@@ -264,25 +288,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);
 }
@@ -290,29 +311,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);
 }
@@ -320,17 +338,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());
 
@@ -343,21 +358,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());
 
@@ -370,21 +382,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/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/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
new file mode 100644
index 0000000..0384d9d
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
@@ -0,0 +1,212 @@
+/*
+ * 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "SchedulerUnittests"
+
+#include <gmock/gmock.h>
+#include <log/log.h>
+#include <thread>
+
+#include "Scheduler/RefreshRateStats.h"
+#include "mock/DisplayHardware/MockDisplay.h"
+
+using namespace std::chrono_literals;
+
+namespace android {
+namespace scheduler {
+
+class RefreshRateStatsTest : public testing::Test {
+protected:
+    static constexpr int CONFIG_ID_90 = 0;
+    static constexpr int CONFIG_ID_60 = 1;
+    static constexpr int64_t VSYNC_90 = 11111111;
+    static constexpr int64_t VSYNC_60 = 16666667;
+
+    RefreshRateStatsTest();
+    ~RefreshRateStatsTest();
+
+    void init(std::vector<std::shared_ptr<const HWC2::Display::Config>> configs);
+
+    std::unique_ptr<RefreshRateStats> mRefreshRateStats;
+};
+
+RefreshRateStatsTest::RefreshRateStatsTest() {
+    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());
+}
+
+RefreshRateStatsTest::~RefreshRateStatsTest() {
+    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());
+}
+
+void RefreshRateStatsTest::init(std::vector<std::shared_ptr<const HWC2::Display::Config>> configs) {
+    mRefreshRateStats = std::make_unique<RefreshRateStats>(configs);
+}
+
+namespace {
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+TEST_F(RefreshRateStatsTest, canCreateAndDestroyTest) {
+    std::vector<std::shared_ptr<const HWC2::Display::Config>> configs;
+    init(configs);
+
+    // There is one default config, so the refresh rates should have one item.
+    ASSERT_EQ(1, mRefreshRateStats->getTotalTimes().size());
+}
+
+TEST_F(RefreshRateStatsTest, oneConfigTest) {
+    auto display = new Hwc2::mock::Display();
+
+    auto config = HWC2::Display::Config::Builder(*display, CONFIG_ID_90);
+    config.setVsyncPeriod(VSYNC_90);
+    std::vector<std::shared_ptr<const HWC2::Display::Config>> configs;
+    configs.push_back(config.build());
+
+    init(configs);
+
+    std::unordered_map<std::string, int64_t> times = mRefreshRateStats->getTotalTimes();
+    ASSERT_EQ(2, times.size());
+    ASSERT_EQ(0, times["ScreenOff"]);
+    ASSERT_EQ(0, times["90fps"]);
+    // Setting up tests on mobile harness can be flaky with time passing, so testing for
+    // exact time changes can result in flaxy numbers. To avoid that remember old
+    // numbers to make sure the correct values are increasing in the next test.
+    int screenOff = times["ScreenOff"];
+    int ninety = times["90fps"];
+
+    // Screen is off by default.
+    std::this_thread::sleep_for(std::chrono::milliseconds(2));
+    times = mRefreshRateStats->getTotalTimes();
+    ASSERT_LT(screenOff, times["ScreenOff"]);
+    ASSERT_EQ(0, times["90fps"]);
+    screenOff = times["ScreenOff"];
+
+    mRefreshRateStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+    std::this_thread::sleep_for(std::chrono::milliseconds(2));
+    times = mRefreshRateStats->getTotalTimes();
+    ASSERT_EQ(screenOff, times["ScreenOff"]);
+    ASSERT_LT(ninety, times["90fps"]);
+    ninety = times["90fps"];
+
+    mRefreshRateStats->setPowerMode(HWC_POWER_MODE_DOZE);
+    std::this_thread::sleep_for(std::chrono::milliseconds(2));
+    times = mRefreshRateStats->getTotalTimes();
+    ASSERT_LT(screenOff, times["ScreenOff"]);
+    ASSERT_EQ(ninety, times["90fps"]);
+    screenOff = times["ScreenOff"];
+
+    mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+    std::this_thread::sleep_for(std::chrono::milliseconds(2));
+    times = mRefreshRateStats->getTotalTimes();
+    // Because the power mode is not HWC_POWER_MODE_NORMAL, switching the config
+    // does not update refresh rates that come from the config.
+    ASSERT_LT(screenOff, times["ScreenOff"]);
+    ASSERT_EQ(ninety, times["90fps"]);
+}
+
+TEST_F(RefreshRateStatsTest, twoConfigsTest) {
+    auto display = new Hwc2::mock::Display();
+
+    auto config90 = HWC2::Display::Config::Builder(*display, CONFIG_ID_90);
+    config90.setVsyncPeriod(VSYNC_90);
+    std::vector<std::shared_ptr<const HWC2::Display::Config>> configs;
+    configs.push_back(config90.build());
+
+    auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60);
+    config60.setVsyncPeriod(VSYNC_60);
+    configs.push_back(config60.build());
+
+    init(configs);
+
+    std::unordered_map<std::string, int64_t> times = mRefreshRateStats->getTotalTimes();
+    ASSERT_EQ(3, times.size());
+    ASSERT_EQ(0, times["ScreenOff"]);
+    ASSERT_EQ(0, times["60fps"]);
+    ASSERT_EQ(0, times["90fps"]);
+    // Setting up tests on mobile harness can be flaky with time passing, so testing for
+    // exact time changes can result in flaxy numbers. To avoid that remember old
+    // numbers to make sure the correct values are increasing in the next test.
+    int screenOff = times["ScreenOff"];
+    int sixty = times["60fps"];
+    int ninety = times["90fps"];
+
+    // Screen is off by default.
+    std::this_thread::sleep_for(std::chrono::milliseconds(2));
+    times = mRefreshRateStats->getTotalTimes();
+    ASSERT_LT(screenOff, times["ScreenOff"]);
+    ASSERT_EQ(sixty, times["60fps"]);
+    ASSERT_EQ(ninety, times["90fps"]);
+    screenOff = times["ScreenOff"];
+
+    mRefreshRateStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+    std::this_thread::sleep_for(std::chrono::milliseconds(2));
+    times = mRefreshRateStats->getTotalTimes();
+    ASSERT_EQ(screenOff, times["ScreenOff"]);
+    ASSERT_EQ(sixty, times["60fps"]);
+    ASSERT_LT(ninety, times["90fps"]);
+    ninety = times["90fps"];
+
+    // When power mode is normal, time for configs updates.
+    mRefreshRateStats->setConfigMode(CONFIG_ID_60);
+    std::this_thread::sleep_for(std::chrono::milliseconds(2));
+    times = mRefreshRateStats->getTotalTimes();
+    ASSERT_EQ(screenOff, times["ScreenOff"]);
+    ASSERT_EQ(ninety, times["90fps"]);
+    ASSERT_LT(sixty, times["60fps"]);
+    sixty = times["60fps"];
+
+    mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+    std::this_thread::sleep_for(std::chrono::milliseconds(2));
+    times = mRefreshRateStats->getTotalTimes();
+    ASSERT_EQ(screenOff, times["ScreenOff"]);
+    ASSERT_LT(ninety, times["90fps"]);
+    ASSERT_EQ(sixty, times["60fps"]);
+    ninety = times["90fps"];
+
+    mRefreshRateStats->setConfigMode(CONFIG_ID_60);
+    std::this_thread::sleep_for(std::chrono::milliseconds(2));
+    times = mRefreshRateStats->getTotalTimes();
+    ASSERT_EQ(screenOff, times["ScreenOff"]);
+    ASSERT_EQ(ninety, times["90fps"]);
+    ASSERT_LT(sixty, times["60fps"]);
+    sixty = times["60fps"];
+
+    // Because the power mode is not HWC_POWER_MODE_NORMAL, switching the config
+    // does not update refresh rates that come from the config.
+    mRefreshRateStats->setPowerMode(HWC_POWER_MODE_DOZE);
+    mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+    std::this_thread::sleep_for(std::chrono::milliseconds(2));
+    times = mRefreshRateStats->getTotalTimes();
+    ASSERT_LT(screenOff, times["ScreenOff"]);
+    ASSERT_EQ(ninety, times["90fps"]);
+    ASSERT_EQ(sixty, times["60fps"]);
+    screenOff = times["ScreenOff"];
+
+    mRefreshRateStats->setConfigMode(CONFIG_ID_60);
+    std::this_thread::sleep_for(std::chrono::milliseconds(2));
+    times = mRefreshRateStats->getTotalTimes();
+    ASSERT_LT(screenOff, times["ScreenOff"]);
+    ASSERT_EQ(ninety, times["90fps"]);
+    ASSERT_EQ(sixty, times["60fps"]);
+}
+} // namespace
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 4253ad8..4d9aec6 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -8,12 +8,9 @@
 
 #include <mutex>
 
-#include "AsyncCallRecorder.h"
-#include "Scheduler/DispSync.h"
 #include "Scheduler/EventControlThread.h"
 #include "Scheduler/EventThread.h"
 #include "Scheduler/Scheduler.h"
-#include "mock/MockDispSync.h"
 #include "mock/MockEventThread.h"
 
 using testing::_;
@@ -26,7 +23,7 @@
     class MockEventThreadConnection : public android::EventThreadConnection {
     public:
         explicit MockEventThreadConnection(EventThread* eventThread)
-              : EventThreadConnection(eventThread) {}
+              : EventThreadConnection(eventThread, ResyncCallback()) {}
         ~MockEventThreadConnection() = default;
 
         MOCK_METHOD1(stealReceiveChannel, status_t(gui::BitTube* outChannel));
@@ -44,9 +41,8 @@
               : 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::ResyncWithRateLimitCallback /* resyncCallback */,
                 impl::EventThread::InterceptVSyncsCallback /* interceptCallback */) override {
             return std::move(mEventThread);
         }
@@ -61,13 +57,9 @@
     ~SchedulerTest() override;
 
     sp<Scheduler::ConnectionHandle> mConnectionHandle;
-    mock::DispSync* mPrimaryDispSync = new mock::DispSync();
     mock::EventThread* mEventThread;
     std::unique_ptr<MockScheduler> mScheduler;
     sp<MockEventThreadConnection> mEventThreadConnection;
-
-    AsyncCallRecorder<void (*)()> mResyncCallRecorder;
-    AsyncCallRecorder<void (*)(nsecs_t)> mInterceptVSyncCallRecorder;
 };
 
 SchedulerTest::SchedulerTest() {
@@ -84,12 +76,12 @@
 
     // createConnection call to scheduler makes a createEventConnection call to EventThread. Make
     // sure that call gets executed and returns an EventThread::Connection object.
-    EXPECT_CALL(*mEventThread, createEventConnection())
+    EXPECT_CALL(*mEventThread, createEventConnection(_))
             .WillRepeatedly(Return(mEventThreadConnection));
 
-    mConnectionHandle =
-            mScheduler->createConnection("appConnection", 16, mResyncCallRecorder.getInvocable(),
-                                         mInterceptVSyncCallRecorder.getInvocable());
+    mConnectionHandle = mScheduler->createConnection("appConnection", 16, ResyncCallback(),
+                                                     impl::EventThread::InterceptVSyncsCallback());
+    EXPECT_TRUE(mConnectionHandle != nullptr);
 }
 
 SchedulerTest::~SchedulerTest() {
@@ -102,17 +94,13 @@
 /* ------------------------------------------------------------------------
  * Test cases
  */
-TEST_F(SchedulerTest, canCreateAndDestroyTest) {
-    EXPECT_FALSE(mResyncCallRecorder.waitForCall().has_value());
-    EXPECT_FALSE(mInterceptVSyncCallRecorder.waitForCall().has_value());
-    EXPECT_EQ(0, mConnectionHandle->id);
-}
 
 TEST_F(SchedulerTest, testNullPtr) {
     // Passing a null pointer for ConnectionHandle is a valid argument. The code doesn't throw any
     // exceptions, just gracefully continues.
     sp<IDisplayEventConnection> returnedValue;
-    ASSERT_NO_FATAL_FAILURE(returnedValue = mScheduler->createDisplayEventConnection(nullptr));
+    ASSERT_NO_FATAL_FAILURE(
+            returnedValue = mScheduler->createDisplayEventConnection(nullptr, ResyncCallback()));
     EXPECT_TRUE(returnedValue == nullptr);
     EXPECT_TRUE(mScheduler->getEventThread(nullptr) == nullptr);
     EXPECT_TRUE(mScheduler->getEventConnection(nullptr) == nullptr);
@@ -132,8 +120,9 @@
     sp<Scheduler::ConnectionHandle> connectionHandle = new Scheduler::ConnectionHandle(20);
 
     sp<IDisplayEventConnection> returnedValue;
-    ASSERT_NO_FATAL_FAILURE(returnedValue =
-                                    mScheduler->createDisplayEventConnection(connectionHandle));
+    ASSERT_NO_FATAL_FAILURE(
+            returnedValue =
+                    mScheduler->createDisplayEventConnection(connectionHandle, ResyncCallback()));
     EXPECT_TRUE(returnedValue == nullptr);
     EXPECT_TRUE(mScheduler->getEventThread(connectionHandle) == nullptr);
     EXPECT_TRUE(mScheduler->getEventConnection(connectionHandle) == nullptr);
@@ -160,8 +149,9 @@
 
 TEST_F(SchedulerTest, validConnectionHandle) {
     sp<IDisplayEventConnection> returnedValue;
-    ASSERT_NO_FATAL_FAILURE(returnedValue =
-                                    mScheduler->createDisplayEventConnection(mConnectionHandle));
+    ASSERT_NO_FATAL_FAILURE(
+            returnedValue =
+                    mScheduler->createDisplayEventConnection(mConnectionHandle, ResyncCallback()));
     EXPECT_TRUE(returnedValue != nullptr);
     ASSERT_EQ(returnedValue, mEventThreadConnection);
 
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 9ff5e8d..9c5c967 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -218,7 +218,7 @@
     auto setupNewDisplayDeviceInternal(const wp<IBinder>& displayToken,
                                        const std::optional<DisplayId>& displayId,
                                        const DisplayDeviceState& state,
-                                       const sp<DisplaySurface>& dispSurface,
+                                       const sp<compositionengine::DisplaySurface>& dispSurface,
                                        const sp<IGraphicBufferProducer>& producer) {
         return mFlinger->setupNewDisplayDeviceInternal(displayToken, displayId, state, dispSurface,
                                                        producer);
@@ -235,9 +235,14 @@
 
     auto setDisplayStateLocked(const DisplayState& s) { return mFlinger->setDisplayStateLocked(s); }
 
-    auto onInitializeDisplays() { return mFlinger->onInitializeDisplays(); }
+    // Allow reading display state without locking, as if called on the SF main thread.
+    auto onInitializeDisplays() NO_THREAD_SAFETY_ANALYSIS {
+        return mFlinger->onInitializeDisplays();
+    }
 
-    auto setPowerModeInternal(const sp<DisplayDevice>& display, int mode) {
+    // Allow reading display state without locking, as if called on the SF main thread.
+    auto setPowerModeInternal(const sp<DisplayDevice>& display,
+                              int mode) NO_THREAD_SAFETY_ANALYSIS {
         return mFlinger->setPowerModeInternal(display, mode);
     }
 
@@ -285,6 +290,7 @@
     auto& mutableEventControlThread() { return mFlinger->mEventControlThread; }
     auto& mutableEventQueue() { return mFlinger->mEventQueue; }
     auto& mutableEventThread() { return mFlinger->mEventThread; }
+    auto& mutableSFEventThread() { return mFlinger->mSFEventThread; }
     auto& mutableGeometryInvalid() { return mFlinger->mGeometryInvalid; }
     auto& mutableHWVsyncAvailable() { return mFlinger->mHWVsyncAvailable; }
     auto& mutableInterceptor() { return mFlinger->mInterceptor; }
@@ -312,6 +318,7 @@
         mutableEventControlThread().reset();
         mutableEventQueue().reset();
         mutableEventThread().reset();
+        mutableSFEventThread().reset();
         mutableInterceptor().reset();
         mutablePrimaryDispSync().reset();
         mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
@@ -330,11 +337,11 @@
         void setExpensiveRenderingExpected(hwc2_display_t, bool) override {}
     };
 
-    struct HWC2Display : public HWC2::Display {
+    struct HWC2Display : public HWC2::impl::Display {
         HWC2Display(Hwc2::Composer& composer, Hwc2::PowerAdvisor& advisor,
                     const std::unordered_set<HWC2::Capability>& capabilities, hwc2_display_t id,
                     HWC2::DisplayType type)
-              : HWC2::Display(composer, advisor, capabilities, id, type) {}
+              : HWC2::impl::Display(composer, advisor, capabilities, id, type) {}
         ~HWC2Display() {
             // Prevents a call to disable vsyncs.
             mType = HWC2::DisplayType::Invalid;
@@ -487,7 +494,7 @@
             return *this;
         }
 
-        auto& setDisplaySurface(const sp<DisplaySurface>& displaySurface) {
+        auto& setDisplaySurface(const sp<compositionengine::DisplaySurface>& displaySurface) {
             mCreationArgs.displaySurface = displaySurface;
             return *this;
         }
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 86f1a39..0f95cf9 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -131,7 +131,6 @@
 };
 
 std::string TimeStatsTest::inputCommand(InputCommand cmd, bool useProto) {
-    size_t index = 0;
     std::string result;
     Vector<String16> args;
 
@@ -162,7 +161,7 @@
             ALOGD("Invalid control command");
     }
 
-    EXPECT_NO_FATAL_FAILURE(mTimeStats->parseArgs(useProto, args, index, result));
+    EXPECT_NO_FATAL_FAILURE(mTimeStats->parseArgs(useProto, args, result));
     return result;
 }
 
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/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp
similarity index 77%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
rename to services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp
index e6ac6bf..2ec37c1 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -14,14 +14,16 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include "mock/DisplayHardware/MockDisplay.h"
 
 namespace android {
+namespace Hwc2 {
 namespace mock {
 
 // Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+Display::Display() = default;
+Display::~Display() = default;
 
 } // namespace mock
-} // namespace android
+} // namespace Hwc2
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
new file mode 100644
index 0000000..d7e20c4
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
@@ -0,0 +1,86 @@
+/*
+ * 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 "DisplayHardware/HWC2.h"
+
+using HWC2::Error;
+using HWC2::Layer;
+
+namespace android {
+namespace Hwc2 {
+namespace mock {
+
+class Display : public HWC2::Display {
+public:
+    Display();
+    ~Display();
+
+    MOCK_CONST_METHOD0(getId, hwc2_layer_t());
+    MOCK_CONST_METHOD0(isConnected, bool());
+    MOCK_METHOD1(setConnected, void(bool));
+    MOCK_CONST_METHOD0(getCapabilities, const std::unordered_set<HWC2::DisplayCapability>&());
+
+    MOCK_METHOD0(acceptChanges, Error());
+    MOCK_METHOD1(createLayer, Error(Layer**));
+    MOCK_METHOD1(destroyLayer, Error(Layer*));
+    MOCK_CONST_METHOD1(getActiveConfig, Error(std::shared_ptr<const Config>*));
+    MOCK_CONST_METHOD1(getActiveConfigIndex, Error(int* outIndex));
+    MOCK_METHOD1(getChangedCompositionTypes, Error(std::unordered_map<Layer*, HWC2::Composition>*));
+    MOCK_CONST_METHOD1(getColorModes, Error(std::vector<android::ui::ColorMode>*));
+
+    MOCK_CONST_METHOD0(getSupportedPerFrameMetadata, int32_t());
+    MOCK_CONST_METHOD2(getRenderIntents,
+                       Error(android::ui::ColorMode, std::vector<android::ui::RenderIntent>*));
+    MOCK_METHOD2(getDataspaceSaturationMatrix, Error(android::ui::Dataspace, android::mat4*));
+    MOCK_CONST_METHOD0(getConfigs, std::vector<std::shared_ptr<const Config>>());
+
+    MOCK_CONST_METHOD1(getName, Error(std::string*));
+    MOCK_METHOD2(getRequests,
+                 Error(HWC2::DisplayRequest*, std::unordered_map<Layer*, HWC2::LayerRequest>*));
+    MOCK_CONST_METHOD1(getType, Error(HWC2::DisplayType*));
+    MOCK_CONST_METHOD1(supportsDoze, Error(bool*));
+    MOCK_CONST_METHOD1(getHdrCapabilities, Error(android::HdrCapabilities*));
+    MOCK_CONST_METHOD3(getDisplayedContentSamplingAttributes,
+                       Error(android::ui::PixelFormat*, android::ui::Dataspace*, uint8_t*));
+    MOCK_CONST_METHOD3(setDisplayContentSamplingEnabled, Error(bool, uint8_t, uint64_t));
+    MOCK_CONST_METHOD3(getDisplayedContentSample,
+                       Error(uint64_t, uint64_t, android::DisplayedFrameStats*));
+    MOCK_CONST_METHOD1(getReleaseFences,
+                       Error(std::unordered_map<Layer*, android::sp<android::Fence>>* outFences));
+    MOCK_METHOD1(present, Error(android::sp<android::Fence>*));
+    MOCK_METHOD1(setActiveConfig, Error(const std::shared_ptr<const HWC2::Display::Config>&));
+    MOCK_METHOD4(setClientTarget,
+                 Error(uint32_t, const android::sp<android::GraphicBuffer>&,
+                       const android::sp<android::Fence>&, android::ui::Dataspace));
+    MOCK_METHOD2(setColorMode, Error(android::ui::ColorMode, android::ui::RenderIntent));
+    MOCK_METHOD2(setColorTransform, Error(const android::mat4&, android_color_transform_t));
+    MOCK_METHOD2(setOutputBuffer,
+                 Error(const android::sp<android::GraphicBuffer>&,
+                       const android::sp<android::Fence>&));
+    MOCK_METHOD1(setPowerMode, Error(HWC2::PowerMode));
+    MOCK_METHOD1(setVsyncEnabled, Error(HWC2::Vsync));
+    MOCK_METHOD2(validate, Error(uint32_t*, uint32_t*));
+    MOCK_METHOD4(presentOrValidate,
+                 Error(uint32_t*, uint32_t*, android::sp<android::Fence>*, uint32_t*));
+};
+
+} // namespace mock
+} // namespace Hwc2
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index 48d45fa..3242ef1 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -28,7 +28,7 @@
     EventThread();
     ~EventThread() override;
 
-    MOCK_CONST_METHOD0(createEventConnection, sp<EventThreadConnection>());
+    MOCK_CONST_METHOD1(createEventConnection, sp<EventThreadConnection>(ResyncCallback));
     MOCK_METHOD0(onScreenReleased, void());
     MOCK_METHOD0(onScreenAcquired, void());
     MOCK_METHOD2(onHotplugReceived, void(DisplayType, bool));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
index 8c113e2..e22d3e8 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
@@ -30,8 +30,8 @@
     ~MessageQueue() override;
 
     MOCK_METHOD1(init, void(const sp<SurfaceFlinger>&));
-    MOCK_METHOD1(setEventThread, void(android::EventThread*));
-    MOCK_METHOD1(setEventConnection, void(const sp<android::EventThreadConnection>& connection));
+    MOCK_METHOD2(setEventThread, void(android::EventThread*, ResyncCallback));
+    MOCK_METHOD1(setEventConnection, void(const sp<EventThreadConnection>& connection));
     MOCK_METHOD0(waitMessage, void());
     MOCK_METHOD2(postMessage, status_t(const sp<MessageBase>&, nsecs_t));
     MOCK_METHOD0(invalidate, void());
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/Android.bp b/services/vr/bufferhubd/Android.bp
index 7a7e437..4e24a64 100644
--- a/services/vr/bufferhubd/Android.bp
+++ b/services/vr/bufferhubd/Android.bp
@@ -14,7 +14,6 @@
 
 sharedLibraries = [
     "libbase",
-    "libbufferhubservice",
     "libcutils",
     "libgtest_prod",
     "libgui",
@@ -28,7 +27,6 @@
 cc_library_static {
     name: "libbufferhubd",
     srcs: [
-        "buffer_channel.cpp",
         "buffer_hub.cpp",
         "consumer_channel.cpp",
         "consumer_queue_channel.cpp",
diff --git a/services/vr/bufferhubd/buffer_channel.cpp b/services/vr/bufferhubd/buffer_channel.cpp
deleted file mode 100644
index 695396c..0000000
--- a/services/vr/bufferhubd/buffer_channel.cpp
+++ /dev/null
@@ -1,138 +0,0 @@
-#include <errno.h>
-#include <private/dvr/buffer_channel.h>
-#include <private/dvr/producer_channel.h>
-
-using android::pdx::BorrowedHandle;
-using android::pdx::ErrorStatus;
-using android::pdx::Message;
-using android::pdx::RemoteChannelHandle;
-using android::pdx::Status;
-using android::pdx::rpc::DispatchRemoteMethod;
-
-namespace android {
-namespace dvr {
-
-BufferChannel::BufferChannel(BufferHubService* service, int buffer_id,
-                             uint32_t width, uint32_t height,
-                             uint32_t layer_count, uint32_t format,
-                             uint64_t usage, size_t user_metadata_size)
-    : BufferHubChannel(service, buffer_id, buffer_id, kDetachedBufferType) {
-  buffer_node_ = std::make_shared<BufferNode>(
-      width, height, layer_count, format, usage, user_metadata_size);
-  if (!buffer_node_->IsValid()) {
-    ALOGE("BufferChannel::BufferChannel: Failed to create BufferNode.");
-    return;
-  }
-  client_state_mask_ = buffer_node_->AddNewActiveClientsBitToMask();
-}
-
-BufferChannel::BufferChannel(BufferHubService* service, int buffer_id,
-                             int channel_id,
-                             std::shared_ptr<BufferNode> buffer_node)
-    : BufferHubChannel(service, buffer_id, channel_id, kDetachedBufferType),
-      buffer_node_(buffer_node) {
-  client_state_mask_ = buffer_node_->AddNewActiveClientsBitToMask();
-  if (client_state_mask_ == 0U) {
-    ALOGE("BufferChannel::BufferChannel: %s", strerror(errno));
-    buffer_node_ = nullptr;
-  }
-}
-
-BufferChannel::~BufferChannel() {
-  ALOGD_IF(TRACE, "BufferChannel::~BufferChannel: channel_id=%d buffer_id=%d.",
-           channel_id(), buffer_id());
-  if (client_state_mask_ != 0U) {
-    buffer_node_->RemoveClientsBitFromMask(client_state_mask_);
-  }
-  Hangup();
-}
-
-BufferHubChannel::BufferInfo BufferChannel::GetBufferInfo() const {
-  return BufferInfo(
-      buffer_id(), /*consumer_count=*/0, buffer_node_->buffer_desc().width,
-      buffer_node_->buffer_desc().height, buffer_node_->buffer_desc().layers,
-      buffer_node_->buffer_desc().format, buffer_node_->buffer_desc().usage,
-      /*state=*/0, /*signaled_mask=*/0, /*index=*/0);
-}
-
-void BufferChannel::HandleImpulse(Message& /*message*/) {
-  ATRACE_NAME("BufferChannel::HandleImpulse");
-}
-
-bool BufferChannel::HandleMessage(Message& message) {
-  ATRACE_NAME("BufferChannel::HandleMessage");
-  switch (message.GetOp()) {
-    case DetachedBufferRPC::Import::Opcode:
-      DispatchRemoteMethod<DetachedBufferRPC::Import>(
-          *this, &BufferChannel::OnImport, message);
-      return true;
-
-    case DetachedBufferRPC::Duplicate::Opcode:
-      DispatchRemoteMethod<DetachedBufferRPC::Duplicate>(
-          *this, &BufferChannel::OnDuplicate, message);
-      return true;
-
-    default:
-      return false;
-  }
-}
-
-Status<BufferTraits<BorrowedHandle>> BufferChannel::OnImport(
-    Message& /*message*/) {
-  ATRACE_NAME("BufferChannel::OnImport");
-  ALOGD_IF(TRACE, "BufferChannel::OnImport: buffer=%d.", buffer_id());
-
-  BorrowedHandle ashmem_handle =
-      BorrowedHandle(buffer_node_->metadata().ashmem_fd().get());
-
-  // TODO(b/112057680) Move away from the GraphicBuffer-based IonBuffer.
-  return BufferTraits<BorrowedHandle>{
-      /*buffer_handle=*/buffer_node_->buffer_handle(),
-      /*metadata_handle=*/ashmem_handle,
-      /*id=*/buffer_id(),
-      /*client_state_mask=*/client_state_mask_,
-      /*metadata_size=*/buffer_node_->metadata().metadata_size(),
-      /*width=*/buffer_node_->buffer_desc().width,
-      /*height=*/buffer_node_->buffer_desc().height,
-      /*layer_count=*/buffer_node_->buffer_desc().layers,
-      /*format=*/buffer_node_->buffer_desc().format,
-      /*usage=*/buffer_node_->buffer_desc().usage,
-      /*stride=*/buffer_node_->buffer_desc().stride,
-      /*acquire_fence_fd=*/BorrowedHandle{},
-      /*released_fence_fd=*/BorrowedHandle{}};
-}
-
-Status<RemoteChannelHandle> BufferChannel::OnDuplicate(Message& message) {
-  ATRACE_NAME("BufferChannel::OnDuplicate");
-  ALOGD_IF(TRACE, "BufferChannel::OnDuplicate: buffer=%d.", buffer_id());
-
-  int channel_id;
-  auto status = message.PushChannel(0, nullptr, &channel_id);
-  if (!status.ok()) {
-    ALOGE("BufferChannel::OnDuplicate: Failed to push buffer channel: %s",
-          status.GetErrorMessage().c_str());
-    return ErrorStatus(ENOMEM);
-  }
-
-  auto channel = std::shared_ptr<BufferChannel>(
-      new BufferChannel(service(), buffer_id(), channel_id, buffer_node_));
-  if (!channel->IsValid()) {
-    ALOGE("BufferChannel::OnDuplicate: Invalid buffer. %s", strerror(errno));
-    return ErrorStatus(EINVAL);
-  }
-
-  const auto channel_status =
-      service()->SetChannel(channel_id, std::move(channel));
-  if (!channel_status) {
-    // Technically, this should never fail, as we just pushed the channel. Note
-    // that LOG_FATAL will be stripped out in non-debug build.
-    LOG_FATAL(
-        "BufferChannel::OnDuplicate: Failed to set new buffer channel: %s.",
-        channel_status.GetErrorMessage().c_str());
-  }
-
-  return status;
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/services/vr/bufferhubd/buffer_hub.cpp b/services/vr/bufferhubd/buffer_hub.cpp
index f50d292..6409265 100644
--- a/services/vr/bufferhubd/buffer_hub.cpp
+++ b/services/vr/bufferhubd/buffer_hub.cpp
@@ -10,7 +10,6 @@
 #include <log/log.h>
 #include <pdx/default_transport/service_endpoint.h>
 #include <private/dvr/bufferhub_rpc.h>
-#include <private/dvr/buffer_channel.h>
 #include <private/dvr/buffer_hub.h>
 #include <private/dvr/consumer_channel.h>
 #include <private/dvr/producer_channel.h>
@@ -242,11 +241,6 @@
           *this, &BufferHubService::OnCreateBuffer, message);
       return {};
 
-    case DetachedBufferRPC::Create::Opcode:
-      DispatchRemoteMethod<DetachedBufferRPC::Create>(
-          *this, &BufferHubService::OnCreateDetachedBuffer, message);
-      return {};
-
     case BufferHubRPC::CreateProducerQueue::Opcode:
       DispatchRemoteMethod<BufferHubRPC::CreateProducerQueue>(
           *this, &BufferHubService::OnCreateProducerQueue, message);
@@ -288,43 +282,6 @@
   }
 }
 
-pdx::Status<void> BufferHubService::OnCreateDetachedBuffer(
-    pdx::Message& message, uint32_t width, uint32_t height,
-    uint32_t layer_count, uint32_t format, uint64_t usage,
-    size_t user_metadata_size) {
-  // Use the producer channel id as the global buffer id.
-  const int buffer_id = message.GetChannelId();
-  ALOGD_IF(TRACE,
-           "BufferHubService::OnCreateDetachedBuffer: buffer_id=%d width=%u "
-           "height=%u layer_count=%u format=%u usage=%" PRIx64
-           " user_metadata_size=%zu",
-           buffer_id, width, height, layer_count, format, usage,
-           user_metadata_size);
-
-  // See if this channel is already attached to a buffer.
-  if (const auto channel = message.GetChannel<BufferHubChannel>()) {
-    ALOGE(
-        "BufferHubService::OnCreateDetachedBuffer: Buffer already created: "
-        "buffer=%d",
-        buffer_id);
-    return ErrorStatus(EALREADY);
-  }
-
-  std::unique_ptr<BufferChannel> channel =
-      BufferChannel::Create(this, buffer_id, width, height, layer_count, format,
-                            usage, user_metadata_size);
-  if (!channel) {
-    ALOGE(
-        "BufferHubService::OnCreateDetachedBuffer: Failed to allocate buffer, "
-        "buffer=%d.",
-        buffer_id);
-    return ErrorStatus(ENOMEM);
-  }
-
-  message.SetChannel(std::move(channel));
-  return {};
-}
-
 Status<QueueInfo> BufferHubService::OnCreateProducerQueue(
     pdx::Message& message, const ProducerQueueConfig& producer_config,
     const UsagePolicy& usage_policy) {
diff --git a/services/vr/bufferhubd/include/private/dvr/buffer_channel.h b/services/vr/bufferhubd/include/private/dvr/buffer_channel.h
deleted file mode 100644
index 9888db6..0000000
--- a/services/vr/bufferhubd/include/private/dvr/buffer_channel.h
+++ /dev/null
@@ -1,61 +0,0 @@
-#ifndef ANDROID_DVR_BUFFERHUBD_BUFFER_CHANNEL_H_
-#define ANDROID_DVR_BUFFERHUBD_BUFFER_CHANNEL_H_
-
-#include <pdx/channel_handle.h>
-#include <pdx/file_handle.h>
-#include <private/dvr/buffer_hub.h>
-#include <private/dvr/buffer_hub_defs.h>
-#include <private/dvr/buffer_node.h>
-
-namespace android {
-namespace dvr {
-
-class BufferChannel : public BufferHubChannel {
- public:
-  ~BufferChannel() override;
-
-  template <typename... Args>
-  static std::unique_ptr<BufferChannel> Create(Args&&... args) {
-    auto buffer = std::unique_ptr<BufferChannel>(
-        new BufferChannel(std::forward<Args>(args)...));
-    return buffer->IsValid() ? std::move(buffer) : nullptr;
-  }
-
-  // Returns whether the object holds a valid graphic buffer.
-  bool IsValid() const {
-    return buffer_node_ != nullptr && buffer_node_->IsValid();
-  }
-
-  // Captures buffer info for use by BufferHubService::DumpState().
-  BufferInfo GetBufferInfo() const override;
-
-  bool HandleMessage(pdx::Message& message) override;
-  void HandleImpulse(pdx::Message& message) override;
-
- private:
-
-  // Allocates a new detached buffer.
-  BufferChannel(BufferHubService* service, int buffer_id, uint32_t width,
-                uint32_t height, uint32_t layer_count, uint32_t format,
-                uint64_t usage, size_t user_metadata_size);
-
-  // Creates a detached buffer from an existing BufferNode. This method is used
-  // in OnDuplicate method.
-  BufferChannel(BufferHubService* service, int buffer_id, int channel_id,
-                std::shared_ptr<BufferNode> buffer_node);
-
-  pdx::Status<BufferTraits<pdx::BorrowedHandle>> OnImport(
-      pdx::Message& message);
-  pdx::Status<pdx::RemoteChannelHandle> OnDuplicate(pdx::Message& message);
-
-  // The concrete implementation of the Buffer object.
-  std::shared_ptr<BufferNode> buffer_node_ = nullptr;
-
-  // The state bit of this buffer.
-  uint32_t client_state_mask_ = 0U;
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_BUFFERHUBD_BUFFER_CHANNEL_H_
diff --git a/services/vr/bufferhubd/include/private/dvr/buffer_hub.h b/services/vr/bufferhubd/include/private/dvr/buffer_hub.h
index 01520fc..909d69b 100644
--- a/services/vr/bufferhubd/include/private/dvr/buffer_hub.h
+++ b/services/vr/bufferhubd/include/private/dvr/buffer_hub.h
@@ -144,11 +144,6 @@
   pdx::Status<void> OnCreateBuffer(pdx::Message& message, uint32_t width,
                                    uint32_t height, uint32_t format,
                                    uint64_t usage, size_t meta_size_bytes);
-  pdx::Status<void> OnCreateDetachedBuffer(pdx::Message& message,
-                                           uint32_t width, uint32_t height,
-                                           uint32_t layer_count,
-                                           uint32_t format, uint64_t usage,
-                                           size_t user_metadata_size);
   pdx::Status<QueueInfo> OnCreateProducerQueue(
       pdx::Message& message, const ProducerQueueConfig& producer_config,
       const UsagePolicy& usage_policy);
diff --git a/services/vr/bufferhubd/include/private/dvr/buffer_node.h b/services/vr/bufferhubd/include/private/dvr/buffer_node.h
deleted file mode 100644
index 997aeda..0000000
--- a/services/vr/bufferhubd/include/private/dvr/buffer_node.h
+++ /dev/null
@@ -1,16 +0,0 @@
-#ifndef ANDROID_DVR_BUFFERHUBD_BUFFER_NODE_H_
-#define ANDROID_DVR_BUFFERHUBD_BUFFER_NODE_H_
-// TODO(b/118891412) Remove this file
-
-#include <bufferhub/BufferNode.h>
-
-namespace android {
-namespace dvr {
-
-typedef android::frameworks::bufferhub::V1_0::implementation::BufferNode
-    BufferNode;
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_BUFFERHUBD_BUFFER_NODE_H_
diff --git a/services/vr/bufferhubd/producer_channel.cpp b/services/vr/bufferhubd/producer_channel.cpp
index 409bce9..895dee0 100644
--- a/services/vr/bufferhubd/producer_channel.cpp
+++ b/services/vr/bufferhubd/producer_channel.cpp
@@ -7,7 +7,6 @@
 #include <thread>
 
 #include <log/log.h>
-#include <private/dvr/buffer_channel.h>
 #include <private/dvr/bufferhub_rpc.h>
 #include <private/dvr/consumer_channel.h>
 #include <private/dvr/producer_channel.h>
diff --git a/services/vr/hardware_composer/Android.bp b/services/vr/hardware_composer/Android.bp
index 258b45b..d9df204 100644
--- a/services/vr/hardware_composer/Android.bp
+++ b/services/vr/hardware_composer/Android.bp
@@ -15,6 +15,7 @@
     "android.frameworks.vr.composer@1.0",
     "android.hardware.graphics.composer@2.1",
     "android.hardware.graphics.mapper@2.0",
+    "android.hardware.graphics.mapper@3.0",
     "libbase",
     "libbufferhubqueue",
     "libbinder",
diff --git a/vulkan/libvulkan/layers_extensions.cpp b/vulkan/libvulkan/layers_extensions.cpp
index 009b257..ba4cf00 100644
--- a/vulkan/libvulkan/layers_extensions.cpp
+++ b/vulkan/libvulkan/layers_extensions.cpp
@@ -139,12 +139,12 @@
         auto app_namespace = android::GraphicsEnv::getInstance().getAppNamespace();
         if (app_namespace &&
             !android::base::StartsWith(path_, kSystemLayerLibraryDir)) {
-            std::string error_msg;
-            dlhandle_ = OpenNativeLibrary(
+            char* error_msg = nullptr;
+            dlhandle_ = OpenNativeLibraryInNamespace(
                 app_namespace, path_.c_str(), &native_bridge_, &error_msg);
             if (!dlhandle_) {
-                ALOGE("failed to load layer library '%s': %s", path_.c_str(),
-                      error_msg.c_str());
+                ALOGE("failed to load layer library '%s': %s", path_.c_str(), error_msg);
+                android::NativeLoaderFreeErrorMessage(error_msg);
                 refcount_ = 0;
                 return false;
             }
@@ -165,9 +165,10 @@
     std::lock_guard<std::mutex> lock(mutex_);
     if (--refcount_ == 0) {
         ALOGV("closing layer library '%s'", path_.c_str());
-        std::string error_msg;
+        char* error_msg = nullptr;
         if (!android::CloseNativeLibrary(dlhandle_, native_bridge_, &error_msg)) {
-            ALOGE("failed to unload library '%s': %s", path_.c_str(), error_msg.c_str());
+            ALOGE("failed to unload library '%s': %s", path_.c_str(), error_msg);
+            android::NativeLoaderFreeErrorMessage(error_msg);
             refcount_++;
         } else {
            dlhandle_ = nullptr;
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;