Merge "Revert "SF: accommodate 1.2 format in sampling code""
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index 9aa1075..e938b10 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -99,8 +99,9 @@
         "utils.cpp",
     ],
     static_libs: [
+        "libincidentcompanion",
         "libdumpsys",
-        "libserviceutils"
+        "libserviceutils",
     ],
 }
 
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
index ba9a967..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,14 +49,15 @@
 }
 
 static void* callAndNotify(void* data) {
-    Dumpstate& ds = *static_cast<Dumpstate*>(data);
-    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,15 +106,12 @@
 }
 
 // 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 */,
+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):
-    // 1. check DUMP permission (again)?
-    // 2. check if primary user? If non primary user the consent service will reject anyway.
     MYLOGI("startBugreport() with mode: %d\n", bugreport_mode);
 
     if (bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_FULL &&
@@ -135,6 +140,11 @@
         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_);
     if (err != 0) {
@@ -143,6 +153,14 @@
     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())
diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h
index b6ba32d..faeea53 100644
--- a/cmds/dumpstate/DumpstateService.h
+++ b/cmds/dumpstate/DumpstateService.h
@@ -47,6 +47,9 @@
                                   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/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
index 2635157..f58535e 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
@@ -79,4 +79,9 @@
     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 bcd0cb7..907a67c 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
@@ -38,20 +38,18 @@
     /* 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
-     *
-     * @param durationMs time capturing bugreport took in milliseconds
-     * @param title title for the bugreport; helpful in reminding the user why they took it
-     * @param description detailed description for the bugreport
+     * Called when taking bugreport finishes successfully.
      */
-     oneway void onFinished(long durationMs, @utf8InCpp String title,
-                            @utf8InCpp String description);
+     oneway void onFinished();
 
     // TODO(b/111441001): Remove old methods when not used anymore.
     void onProgressUpdated(int progress);
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 4634ecd..41c42d7 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
 
@@ -165,6 +170,13 @@
     return false;
 }
 
+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;
+    }
+    return true;
+}
 
 }  // namespace
 }  // namespace os
@@ -657,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];
@@ -1660,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");
 }
 
@@ -1891,14 +1930,6 @@
                     ds.path_ = new_path;
                 }
             }
-            // The zip file lives in an internal directory. Copy it over to output.
-            if (ds.options_->bugreport_fd.get() != -1) {
-                bool copy_succeeded =
-                    android::os::CopyFileToFd(ds.path_, ds.options_->bugreport_fd.get());
-                if (!copy_succeeded && remove(ds.path_.c_str())) {
-                    MYLOGE("remove(%s): %s", ds.path_.c_str(), strerror(errno));
-                }
-            }  // else - the file just remains in the internal directory.
         }
     }
     if (do_text_file) {
@@ -2118,7 +2149,7 @@
 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;
@@ -2135,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;
@@ -2188,22 +2222,26 @@
     options_ = std::move(options);
 }
 
-Dumpstate::RunStatus Dumpstate::Run() {
-    Dumpstate::RunStatus status = RunInternal();
+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:
-                // TODO(b/111441001): duration argument does not make sense. Remove.
-                listener_->onFinished(0 /* duration */, options_->notification_title,
-                                      options_->notification_description);
+                listener_->onFinished();
                 break;
             case Dumpstate::RunStatus::HELP:
                 break;
             case Dumpstate::RunStatus::INVALID_INPUT:
-                listener_->onError(android::os::IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
+                listener_->onError(IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
                 break;
             case Dumpstate::RunStatus::ERROR:
-                listener_->onError(android::os::IDumpstateListener::BUGREPORT_ERROR_RUNTIME_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;
         }
     }
@@ -2231,7 +2269,8 @@
  * Bugreports are first generated in a local directory and later copied to the caller's fd if
  * supplied.
  */
-Dumpstate::RunStatus Dumpstate::RunInternal() {
+Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid,
+                                            const std::string& calling_package) {
     LogDumpOptions(*options_);
     if (!options_->ValidateOptions()) {
         MYLOGE("Invalid options specified\n");
@@ -2269,6 +2308,12 @@
         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_->OutputToFile();
 
@@ -2420,11 +2465,24 @@
         TEMP_FAILURE_RETRY(dup2(dup_stdout_fd, fileno(stdout)));
     }
 
-    /* rename or zip the (now complete) .tmp file to its final location */
+    // 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;
+        }
+    }
+
     /* vibrate a few but shortly times to let user know it's finished */
     if (options_->do_vibrate) {
         for (int i = 0; i < 3; i++) {
@@ -2456,7 +2514,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(ds.path_, ds.options_->bugreport_fd.get());
+        if (copy_succeeded && remove(ds.path_.c_str())) {
+            MYLOGE("remove(%s): %s", ds.path_.c_str(), strerror(errno));
+        }
+        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. */
@@ -2465,7 +2589,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) {
@@ -2479,9 +2610,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 3dfe4e9..4766d82 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);
@@ -447,12 +448,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();
+    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 c57775f..570c6c9 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -61,9 +61,8 @@
         dprintf(outFd_, "\rError %d", error_code);
         return binder::Status::ok();
     }
-    binder::Status onFinished(int64_t duration_ms, const ::std::string&,
-                              const ::std::string&) override {
-        dprintf(outFd_, "\rFinished in %lld", (long long) duration_ms);
+    binder::Status onFinished() override {
+        dprintf(outFd_, "\rFinished");
         return binder::Status::ok();
     }
     binder::Status onProgressUpdated(int32_t progress) override {
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 0302c46..eb73d41 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -61,8 +61,7 @@
   public:
     MOCK_METHOD1(onProgress, binder::Status(int32_t progress));
     MOCK_METHOD1(onError, binder::Status(int32_t error_code));
-    MOCK_METHOD3(onFinished, binder::Status(int64_t duration_ms, const ::std::string& title,
-                                            const ::std::string& description));
+    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,
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index a6f77aa..c309364 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -18,6 +18,7 @@
         "dexopt.cpp",
         "globals.cpp",
         "utils.cpp",
+        "utils_default.cpp",
         "view_compiler.cpp",
         ":installd_aidl",
     ],
@@ -138,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",
     ],
 }
 
@@ -190,7 +204,9 @@
         "dexopt.cpp",
         "globals.cpp",
         "otapreopt.cpp",
+        "otapreopt_utils.cpp",
         "utils.cpp",
+        "utils_default.cpp",
         "view_compiler.cpp",
     ],
 
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 51f30da..cbf0e09 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -2377,7 +2377,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/dexopt.cpp b/cmds/installd/dexopt.cpp
index e3a35c7..940ba79 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -333,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() ||
@@ -642,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());
@@ -1459,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..9965d58 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,33 @@
       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
+    //
+    if (mount("tmpfs", kPostinstallApexDir, "tmpfs", MS_NODEV | MS_NOEXEC | MS_NOSUID, nullptr)
+        != 0) {
+        PLOG(ERROR) << "Failed to mount tmpfs in " << kPostinstallApexDir;
+        exit(209);
+    }
+    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);
+    }
+    if (selinux_android_restorecon(kPostinstallApexDir, 0) < 0) {
+        PLOG(ERROR) << "Failed to restorecon " << kPostinstallApexDir;
+        exit(212);
+    }
+
     // Chdir into /postinstall.
     if (chdir("/postinstall") != 0) {
         PLOG(ERROR) << "Unable to chdir into /postinstall.";
@@ -155,22 +186,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/utils.h b/cmds/installd/utils.h
index 430f515..5afe059 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -117,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);
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/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/include/android/sensor.h b/include/android/sensor.h
index 1a1b010..e9d5c16 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -495,6 +495,7 @@
  * - ASensorEventQueue_hasEvents()
  * - ASensorEventQueue_getEvents()
  * - ASensorEventQueue_setEventRate()
+ * - ASensorEventQueue_requestAdditionalInfoEvents()
  */
 typedef struct ASensorEventQueue ASensorEventQueue;
 
@@ -779,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__ */
 
 /*****************************************************************************/
 
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index 13b630b..0573187 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -29,6 +29,7 @@
 #include <sys/cdefs.h>
 
 #include <android/hardware_buffer.h>
+#include <android/hdr_metadata.h>
 #include <android/native_window.h>
 
 __BEGIN_DECLS
@@ -60,10 +61,11 @@
                                         __INTRODUCED_IN(29);
 
 /**
- * Destroys the |surface_control| object. After releasing the ASurfaceControl the caller no longer
- * has ownership of the AsurfaceControl.
+ * 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_destroy(ASurfaceControl* surface_control) __INTRODUCED_IN(29);
+void ASurfaceControl_release(ASurfaceControl* surface_control) __INTRODUCED_IN(29);
 
 struct ASurfaceTransaction;
 
@@ -94,24 +96,94 @@
 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.
- * |present_fence| is the sync fence that signals when the transaction has been presented.
- * The recipient of the callback takes ownership of the present_fence and is responsible for closing
- * it.
  *
- * It is safe to assume that once the present fence singals, that reads for all buffers,
- * submitted in previous transactions, which are not in the surface tree after a transaction is
- * applied, are finished and the buffers may be reused.
+ * |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, int32_t present_fence);
+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
@@ -121,6 +193,16 @@
 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,
@@ -148,15 +230,26 @@
 
 /**
  * Updates the AHardwareBuffer displayed for |surface_control|. If not -1, the
- * fence_fd should be a file descriptor that is signaled when all pending work
+ * 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 |fence_fd| passed and is responsible
+ * 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 fence_fd = -1) __INTRODUCED_IN(29);
+                                   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
@@ -189,8 +282,9 @@
  * opaque or visual errors can occur.
  */
 void ASurfaceTransaction_setBufferTransparency(ASurfaceTransaction* transaction,
-                                         ASurfaceControl* surface_control, int8_t transparency)
-                                         __INTRODUCED_IN(29);
+                                               ASurfaceControl* surface_control,
+                                               int8_t transparency)
+                                               __INTRODUCED_IN(29);
 
 /**
  * Updates the region for the content on this surface updated in this
@@ -200,6 +294,50 @@
                                          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
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/libs/cputimeinstate/cputimeinstate.h b/libs/cputimeinstate/cputimeinstate.h
new file mode 100644
index 0000000..0205452
--- /dev/null
+++ b/libs/cputimeinstate/cputimeinstate.h
@@ -0,0 +1,29 @@
+/*
+ * 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 <unordered_map>
+#include <vector>
+
+namespace android {
+namespace bpf {
+
+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 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/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 40b55fa..ab92973 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -97,6 +97,9 @@
     output.writeStrongBinder(cachedBuffer.token);
     output.writeInt32(cachedBuffer.bufferId);
 
+    output.writeFloat(colorAlpha);
+    output.writeUint32(static_cast<uint32_t>(colorDataspace));
+
     return NO_ERROR;
 }
 
@@ -170,6 +173,9 @@
     cachedBuffer.token = input.readStrongBinder();
     cachedBuffer.bufferId = input.readInt32();
 
+    colorAlpha = input.readFloat();
+    colorDataspace = static_cast<ui::Dataspace>(input.readUint32());
+
     return NO_ERROR;
 }
 
@@ -382,6 +388,14 @@
         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 & what) != other.what) {
         ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
               "other.what=0x%" PRIu64 " what=0x%" PRIu64,
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index a82054b..6c1c52e 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);
         }
     }
@@ -329,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(
@@ -589,6 +664,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);
@@ -770,9 +875,12 @@
         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);
@@ -872,6 +980,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) {
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index af31420..9063e7f 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -85,6 +85,8 @@
         eCornerRadiusChanged = 0x80000000,
         eFrameChanged = 0x1'00000000,
         eCachedBufferChanged = 0x2'00000000,
+        eColorAlphaChanged = 0x4'00000000,
+        eColorDataspaceChanged = 0x8'00000000,
     };
 
     layer_state_t()
@@ -110,7 +112,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;
@@ -180,6 +184,9 @@
 #endif
 
     cached_buffer_t cachedBuffer;
+
+    float colorAlpha;
+    ui::Dataspace colorDataspace;
 };
 
 struct ComposerState {
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index db315c2..24b656b 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
@@ -167,6 +156,8 @@
     // ------------------------------------------------------------------------
     // surface creation / destruction
 
+    static sp<SurfaceComposerClient> getDefault();
+
     //! Create a surface
     sp<SurfaceControl> createSurface(
             const String8& name,// name of the surface
@@ -335,6 +326,12 @@
 
         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);
@@ -387,6 +384,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);
 
@@ -465,6 +465,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/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/libs/incidentcompanion/binder/android/os/IIncidentAuthListener.aidl b/libs/incidentcompanion/binder/android/os/IIncidentAuthListener.aidl
new file mode 100644
index 0000000..5484be8
--- /dev/null
+++ b/libs/incidentcompanion/binder/android/os/IIncidentAuthListener.aidl
@@ -0,0 +1,34 @@
+/**
+ * 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;
+
+/**
+ * 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/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 f57bf9c..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);
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/SensorEventQueue.h b/libs/sensor/include/sensor/SensorEventQueue.h
index 8176578..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;
 };
 
 // ----------------------------------------------------------------------------
@@ -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/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/Gralloc2.cpp b/libs/ui/Gralloc2.cpp
index ea7321e..2c4b5f3 100644
--- a/libs/ui/Gralloc2.cpp
+++ b/libs/ui/Gralloc2.cpp
@@ -227,7 +227,14 @@
 }
 
 status_t Gralloc2Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
-                              int acquireFence, void** outData) const {
+                              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);
diff --git a/libs/ui/Gralloc3.cpp b/libs/ui/Gralloc3.cpp
index 128200e..acb6b01 100644
--- a/libs/ui/Gralloc3.cpp
+++ b/libs/ui/Gralloc3.cpp
@@ -192,7 +192,8 @@
 }
 
 status_t Gralloc3Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
-                              int acquireFence, void** outData) const {
+                              int acquireFence, void** outData, int32_t* outBytesPerPixel,
+                              int32_t* outBytesPerStride) const {
     auto buffer = const_cast<native_handle_t*>(bufferHandle);
 
     IMapper::Rect accessRegion = sGralloc3Rect(bounds);
@@ -208,12 +209,19 @@
 
     Error error;
     auto ret = mMapper->lock(buffer, usage, accessRegion, acquireFenceHandle,
-                             [&](const auto& tmpError, const auto& tmpData) {
+                             [&](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
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/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp
index b049329..9e36377 100644
--- a/libs/ui/GraphicBufferMapper.cpp
+++ b/libs/ui/GraphicBufferMapper.cpp
@@ -102,10 +102,10 @@
     return NO_ERROR;
 }
 
-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,
@@ -125,21 +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));
-    return mMapper->lock(handle, usage, bounds, fenceFd, vaddr);
+    return mMapper->lock(handle, usage, bounds, fenceFd, vaddr, outBytesPerPixel,
+                         outBytesPerStride);
 }
 
 status_t GraphicBufferMapper::lockAsyncYCbCr(buffer_handle_t handle,
diff --git a/libs/ui/include/ui/Gralloc.h b/libs/ui/include/ui/Gralloc.h
index 92bf043..a484bce 100644
--- a/libs/ui/include/ui/Gralloc.h
+++ b/libs/ui/include/ui/Gralloc.h
@@ -56,7 +56,8 @@
     // 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) const = 0;
+                          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.
diff --git a/libs/ui/include/ui/Gralloc2.h b/libs/ui/include/ui/Gralloc2.h
index e03cb43..b23d8f7 100644
--- a/libs/ui/include/ui/Gralloc2.h
+++ b/libs/ui/include/ui/Gralloc2.h
@@ -53,7 +53,8 @@
                           uint32_t* outNumInts) const override;
 
     status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
-                  int acquireFence, void** outData) const override;
+                  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;
diff --git a/libs/ui/include/ui/Gralloc3.h b/libs/ui/include/ui/Gralloc3.h
index 510ce4a..b0cbcc1 100644
--- a/libs/ui/include/ui/Gralloc3.h
+++ b/libs/ui/include/ui/Gralloc3.h
@@ -52,7 +52,8 @@
                           uint32_t* outNumInts) const override;
 
     status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
-                  int acquireFence, void** outData) const override;
+                  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;
diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h
index 156bd7a..072926f 100644
--- a/libs/ui/include/ui/GraphicBufferMapper.h
+++ b/libs/ui/include/ui/GraphicBufferMapper.h
@@ -56,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,
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 843eb37..e3a237e 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -76,7 +76,8 @@
         "libutils",
         "libui",
         "libhardware_legacy",
-        "libutils"
+        "libstatslog",
+        "libutils",
     ],
 
     header_libs: [
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index 9d92435..57181c2 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>";
@@ -2722,8 +2718,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) {
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index 2b31f6e..64070e3 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 ---
 
@@ -4287,12 +4291,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);
     }
 }
diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h
index 35f3c23..aaffce2 100644
--- a/services/inputflinger/InputReader.h
+++ b/services/inputflinger/InputReader.h
@@ -567,6 +567,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 {
@@ -1511,6 +1574,9 @@
     VelocityControl mWheelXVelocityControl;
     VelocityControl mWheelYVelocityControl;
 
+    // Latency statistics for touch events
+    struct LatencyStatistics mStatistics;
+
     std::optional<DisplayViewport> findViewport();
 
     void resetExternalStylus();
@@ -1580,6 +1646,8 @@
 
     static void assignPointerIds(const RawState* last, RawState* current);
 
+    void reportEventForStatistics(nsecs_t evdevTime);
+
     const char* modeToString(DeviceMode deviceMode);
 };
 
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 3edec40..ed177fb 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;
+    }
 };
 
 
@@ -404,16 +472,6 @@
     void setFocus() {
         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.";
-    }
 protected:
     virtual bool handled() {
         return true;
@@ -469,6 +527,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",
@@ -740,4 +832,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/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 01c60d8..2a70293 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -58,6 +58,7 @@
         "libinput",
         "libutils",
         "libutilscallstack",
+        "libSurfaceFlingerProperties",
     ],
     static_libs: [
         "libcompositionengine",
@@ -221,7 +222,10 @@
     defaults: ["libsurfaceflinger_binary"],
     init_rc: ["surfaceflinger.rc"],
     srcs: [":surfaceflinger_binary_sources"],
-    shared_libs: ["libsurfaceflinger"],
+    shared_libs: [
+        "libsurfaceflinger",
+        "libSurfaceFlingerProperties",
+    ],
 }
 
 cc_library_shared {
@@ -247,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/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 2d00f0c..69b5728 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -52,11 +52,9 @@
     // Sets the bounds to use
     virtual void setBounds(const ui::Size&) = 0;
 
-    // Sets the layer stack filter for this output. If singleLayerStack is true,
-    // this output displays just the single layer stack specified by
-    // singleLayerStackId. Otherwise all layer stacks will be visible on this
-    // output.
-    virtual void setLayerStackFilter(bool singleLayerStack, uint32_t singleLayerStackId) = 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;
@@ -96,8 +94,14 @@
     // logical (aka layer stack) space.
     virtual Region getPhysicalSpaceDirtyRegion(bool repaintEverything) const = 0;
 
-    // Tests whether a given layerStackId belongs in this output
-    virtual bool belongsInOutput(uint32_t layerStackId) 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;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 521e1d7..180c40b 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -38,7 +38,7 @@
     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(bool singleLayerStack, uint32_t singleLayerStackId) override;
+    void setLayerStackFilter(uint32_t layerStackId, bool isInternal) override;
 
     void setColorTransform(const mat4&) override;
     void setColorMode(ui::ColorMode, ui::Dataspace, ui::RenderIntent) override;
@@ -58,7 +58,7 @@
     OutputCompositionState& editState() override;
 
     Region getPhysicalSpaceDirtyRegion(bool repaintEverything) const override;
-    bool belongsInOutput(uint32_t) const override;
+    bool belongsInOutput(uint32_t, bool) const override;
 
     // Testing
     void setDisplayColorProfileForTest(std::unique_ptr<compositionengine::DisplayColorProfile>);
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index 96a0e0f..024ed45 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -34,12 +34,11 @@
     // If false, this output is not considered secure
     bool isSecure{false};
 
-    // If true, only layer stacks with a matching layerStack value will be
-    // displayed. If false, all layer stacks will be displayed.
-    bool singleLayerStack{true};
+    // If true, this output displays layers that are internal-only
+    bool layerStackInternal{false};
 
     // The layer stack to display on this display
-    uint32_t singleLayerStackId{~0u};
+    uint32_t layerStackId{~0u};
 
     // The physical space screen bounds
     Rect bounds;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 37f7007..445f0bb 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -35,7 +35,7 @@
     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(bool, uint32_t));
+    MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool));
 
     MOCK_METHOD1(setColorTransform, void(const mat4&));
     MOCK_METHOD3(setColorMode, void(ui::ColorMode, ui::Dataspace, ui::RenderIntent));
@@ -54,7 +54,7 @@
     MOCK_METHOD0(editState, OutputCompositionState&());
 
     MOCK_CONST_METHOD1(getPhysicalSpaceDirtyRegion, Region(bool));
-    MOCK_CONST_METHOD1(belongsInOutput, bool(uint32_t));
+    MOCK_CONST_METHOD2(belongsInOutput, bool(uint32_t, bool));
 };
 
 } // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index dbf77b0..7fb67bd 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -75,9 +75,9 @@
     dirtyEntireOutput();
 }
 
-void Output::setLayerStackFilter(bool singleLayerStack, uint32_t singleLayerStackId) {
-    mState.singleLayerStack = singleLayerStack;
-    mState.singleLayerStackId = singleLayerStackId;
+void Output::setLayerStackFilter(uint32_t layerStackId, bool isInternal) {
+    mState.layerStackId = layerStackId;
+    mState.layerStackInternal = isInternal;
 
     dirtyEntireOutput();
 }
@@ -175,8 +175,10 @@
     return dirty;
 }
 
-bool Output::belongsInOutput(uint32_t layerStackId) const {
-    return !mState.singleLayerStack || (layerStackId == mState.singleLayerStackId);
+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() {
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index 7d8765f..78807ff 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -22,11 +22,9 @@
 void OutputCompositionState::dump(std::string& out) const {
     dumpVal(out, "isEnabled", isEnabled);
     dumpVal(out, "isSecure", isSecure);
-    if (singleLayerStack) {
-        out.append("layerStack=<any>");
-    } else {
-        dumpVal(out, "layerStack", singleLayerStackId);
-    }
+
+    dumpVal(out, "layerStack", layerStackId);
+    dumpVal(out, "layerStackInternal", layerStackInternal);
 
     out.append("\n   ");
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index f060cff..fe12825 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -153,10 +153,10 @@
     mOutput.editState().bounds = displaySize;
 
     const uint32_t layerStack = 123u;
-    mOutput.setLayerStackFilter(true, layerStack);
+    mOutput.setLayerStackFilter(layerStack, true);
 
-    EXPECT_TRUE(mOutput.getState().singleLayerStack);
-    EXPECT_EQ(layerStack, mOutput.getState().singleLayerStackId);
+    EXPECT_TRUE(mOutput.getState().layerStackInternal);
+    EXPECT_EQ(layerStack, mOutput.getState().layerStackId);
 
     EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(displaySize)));
 }
@@ -260,5 +260,32 @@
     }
 }
 
+/* ------------------------------------------------------------------------
+ * 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/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 54111de..4a13bfb 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -166,7 +166,7 @@
 // ----------------------------------------------------------------------------
 
 void DisplayDevice::setLayerStack(uint32_t stack) {
-    mCompositionDisplay->setLayerStackFilter(!isPrimary(), stack);
+    mCompositionDisplay->setLayerStackFilter(stack, isPrimary());
 }
 
 // ----------------------------------------------------------------------------
@@ -330,7 +330,7 @@
 }
 
 uint32_t DisplayDevice::getLayerStack() const {
-    return mCompositionDisplay->getState().singleLayerStackId;
+    return mCompositionDisplay->getState().layerStackId;
 }
 
 const ui::Transform& DisplayDevice::getTransform() const {
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/Layer.cpp b/services/surfaceflinger/Layer.cpp
index aa9bc15..ef77590 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -109,6 +109,7 @@
     mCurrentState.cornerRadius = 0.0f;
     mCurrentState.api = -1;
     mCurrentState.hasColorTransform = false;
+    mCurrentState.colorDataspace = ui::Dataspace::UNKNOWN;
 
     // drawing state & current state are identical
     mDrawingState = mCurrentState;
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 95a8630..685f419 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -202,6 +202,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 +213,7 @@
     virtual ~Layer();
 
     void setPrimaryDisplayOnly() { mPrimaryDisplayOnly = true; }
+    bool getPrimaryDisplayOnly() const { return mPrimaryDisplayOnly; }
 
     // ------------------------------------------------------------------------
     // Geometry setting functions.
@@ -308,6 +311,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 +333,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);
     }
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 7b25adb..281f6b7 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,18 +38,66 @@
 #include "EventThread.h"
 
 using namespace std::chrono_literals;
-using android::base::StringAppendF;
-
-// ---------------------------------------------------------------------------
 
 namespace android {
 
-// ---------------------------------------------------------------------------
+using base::StringAppendF;
+using base::StringPrintf;
+
+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)),
-        count(-1),
         mEventThread(eventThread),
         mChannel(gui::BitTube::DefaultSize) {}
 
@@ -65,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;
 }
 
@@ -107,18 +158,16 @@
                          InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
       : mVSyncSource(src),
         mVSyncSourceUnique(std::move(uniqueSrc)),
-        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;
-    }
 
-    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);
 
@@ -178,14 +227,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();
     }
 }
 
@@ -201,164 +253,91 @@
 
     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;
+    if (!mVSyncState.synthetic) {
+        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;
+    if (mVSyncState.synthetic) {
+        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++;
+
+    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);
+void EventThread::threadMain(std::unique_lock<std::mutex>& lock) {
+    DisplayEventConsumers consumers;
+
     while (mKeepRunning) {
-        DisplayEventReceiver::Event event;
-        std::vector<sp<EventThreadConnection>> signalConnections;
-        signalConnections = waitForEventLocked(&lock, &event);
+        std::optional<DisplayEventReceiver::Event> event;
 
-        // 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);
-            }
-        }
-    }
-}
-
-// 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;
-
-    while (signalConnections.empty() && mKeepRunning) {
-        bool eventPending = false;
-        bool waitForVSync = false;
-
-        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;
-            }
+        // Determine next event to dispatch.
+        if (!mPendingEvents.empty()) {
+            event = mPendingEvents.front();
+            mPendingEvents.pop_front();
         }
 
-        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();
-            }
+        const bool vsyncPending =
+                event && event->header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
+
+        if (mInterceptVSyncsCallback && vsyncPending) {
+            mInterceptVSyncsCallback(event->header.timestamp);
         }
 
-        // find out connections waiting for events
+        bool vsyncRequested = false;
+
+        // 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);
             }
         }
 
+        if (!consumers.empty()) {
+            dispatchEvent(*event, consumers);
+            consumers.clear();
+        }
+
         // Here we figure out if we need to enable or disable vsyncs
-        if (timestamp && !waitForVSync) {
+        if (vsyncPending && !vsyncRequested) {
             // we received a VSYNC but we have no clients
             // don't report it, and disable VSYNC events
             disableVSyncLocked();
-        } else if (!timestamp && waitForVSync) {
+        } else if (!vsyncPending && vsyncRequested) {
             // we have at least one client, so we want vsync enabled
             // (TODO: this function is called right after we finish
             // notifying clients of a vsync, so this call will be made
@@ -368,53 +347,74 @@
             enableVSyncLocked();
         }
 
-        // 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);
+        if (event) {
+            continue;
+        }
+
+        // Wait for event or client registration/request.
+        if (vsyncRequested) {
+            // Generate a fake VSYNC after a long timeout in case the driver stalls. When the
+            // display is off, keep feeding clients at 60 Hz.
+            const auto timeout = mVSyncState.synthetic ? 16ms : 1000ms;
+            if (mCondition.wait_for(lock, timeout) == std::cv_status::timeout) {
+                ALOGW_IF(!mVSyncState.synthetic, "Faking VSYNC due to driver stall");
+
+                mPendingEvents.push_back(makeVSync(mVSyncState.displayId,
+                                                   systemTime(SYSTEM_TIME_MONOTONIC),
+                                                   ++mVSyncState.count));
             }
+        } else {
+            mCondition.wait(lock);
         }
     }
+}
 
-    // 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;
+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;
+    }
+}
+
+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::enableVSyncLocked() {
-    if (!mUseSoftwareVSync) {
-        // never enable h/w VSYNC when screen is off
+    if (!mVSyncState.synthetic) {
         if (!mVsyncEnabled) {
             mVsyncEnabled = true;
             mVSyncSource->setCallback(this);
@@ -434,16 +434,22 @@
 
 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: VSYNC %s\n", mThreadName, mDebugVsyncEnabled ? "on" : "off");
+    StringAppendF(&result, "  VSyncState{displayId=%u, count=%u%s}\n", mVSyncState.displayId,
+                  mVSyncState.count, mVSyncState.synthetic ? ", synthetic" : "");
+
+    StringAppendF(&result, "  pending events (count=%zu):\n", mPendingEvents.size());
+    for (const auto& event : mPendingEvents) {
+        StringAppendF(&result, "    %s\n", toString(event).c_str());
     }
-    StringAppendF(&result, "  other-events-pending: %zu\n", mPendingEvents.size());
+
+    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());
+        }
+    }
 }
 
 } // namespace impl
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index a411885..1a8ebb7 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -18,11 +18,10 @@
 
 #include <sys/types.h>
 
-#include <array>
 #include <condition_variable>
 #include <cstdint>
+#include <deque>
 #include <mutex>
-#include <queue>
 #include <thread>
 #include <vector>
 
@@ -46,6 +45,13 @@
 
 using ResyncCallback = std::function<void()>;
 
+enum class VSyncRequest {
+    None = -1,
+    Single = 0,
+    Periodic = 1,
+    // Subsequent values are periods.
+};
+
 class VSyncSource {
 public:
     class Callback {
@@ -68,7 +74,7 @@
     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.
@@ -77,10 +83,7 @@
     // Called in response to requestNextVsync.
     const ResyncCallback resyncCallback;
 
-    // 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;
+    VSyncRequest vsyncRequest = VSyncRequest::None;
 
 private:
     virtual void onFirstRef();
@@ -113,7 +116,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;
@@ -137,7 +140,7 @@
     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;
 
@@ -157,18 +160,22 @@
 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,
                 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);
 
@@ -181,18 +188,30 @@
     // 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 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;
+    std::deque<DisplayEventReceiver::Event> mPendingEvents GUARDED_BY(mMutex);
+
+    // VSYNC state of connected display.
+    struct VSyncState {
+        uint32_t displayId = 0;
+
+        // 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;
+    };
+
+    VSyncState mVSyncState GUARDED_BY(mMutex);
+
     bool mVsyncEnabled GUARDED_BY(mMutex) = false;
     bool mKeepRunning GUARDED_BY(mMutex) = true;
 
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 0d587dd..2ed2866 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
@@ -352,4 +350,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 ba18d21..e992398 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -118,6 +118,8 @@
     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(
@@ -147,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/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 5df2876..5a866b2 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -28,6 +28,7 @@
 #include <functional>
 #include <mutex>
 #include <optional>
+#include <unordered_map>
 
 #include <cutils/properties.h>
 #include <log/log.h>
@@ -111,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;
@@ -281,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:
@@ -737,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");
@@ -987,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);
@@ -2226,7 +2217,8 @@
                 mDrawingState.traverseInZOrder([&](Layer* layer) {
                     bool hwcLayerDestroyed = false;
                     const auto displayId = displayDevice->getId();
-                    if (display->belongsInOutput(layer->getLayerStack())) {
+                    if (display->belongsInOutput(layer->getLayerStack(),
+                                                 layer->getPrimaryDisplayOnly())) {
                         Region drawRegion(tr.transform(
                                 layer->visibleNonTransparentRegion));
                         drawRegion.andSelf(bounds);
@@ -2879,7 +2871,9 @@
                 // if not, pick the only display it's on.
                 hintDisplay = nullptr;
                 for (const auto& [token, display] : mDisplays) {
-                    if (display->getCompositionDisplay()->belongsInOutput(layer->getLayerStack())) {
+                    if (display->getCompositionDisplay()
+                                ->belongsInOutput(layer->getLayerStack(),
+                                                  layer->getPrimaryDisplayOnly())) {
                         if (hintDisplay) {
                             hintDisplay = nullptr;
                             break;
@@ -3063,7 +3057,7 @@
         const Layer::State& s(layer->getDrawingState());
 
         // only consider the layers on the given layer stack
-        if (!display->belongsInOutput(layer->getLayerStack())) {
+        if (!display->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) {
             return;
         }
 
@@ -3189,7 +3183,7 @@
 void SurfaceFlinger::invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty) {
     for (const auto& [token, displayDevice] : mDisplays) {
         auto display = displayDevice->getCompositionDisplay();
-        if (display->belongsInOutput(layer->getLayerStack())) {
+        if (display->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) {
             display->editState().dirtyRegion.orSelf(dirty);
         }
     }
@@ -3828,6 +3822,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;
@@ -3961,8 +3961,12 @@
         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");
+        }
     }
     std::vector<sp<CallbackHandle>> callbackHandles;
     if ((what & layer_state_t::eListenerCallbacksChanged) && (!s.listenerCallbacks.empty())) {
@@ -4323,6 +4327,10 @@
 
     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());
@@ -4344,8 +4352,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();
@@ -4369,114 +4377,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);
             }
         }
 
@@ -4488,43 +4418,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 (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();
         }
     });
@@ -4532,6 +4448,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() {
@@ -4557,6 +4477,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) {
@@ -4760,15 +4700,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
@@ -4798,27 +4731,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();
-    StringAppendF(&result,
-                  "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)\n",
-                  vsyncPhaseOffsetNs, sfVsyncPhaseOffsetNs, appEarlyOffset, sfEarlyOffset,
-                  appEarlyGlOffset, sfEarlyGlOffset, dispSyncPresentTimeOffset, getVsyncPeriod());
-
-    // Dump static screen stats
+    dumpVSync(result);
     result.append("\n");
+
     dumpStaticScreenStats(result);
     result.append("\n");
 
@@ -4892,17 +4812,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
      */
@@ -4948,6 +4857,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) {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 499ebcd..68a602c 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -59,6 +59,7 @@
 #include "Scheduler/DispSync.h"
 #include "Scheduler/EventThread.h"
 #include "Scheduler/MessageQueue.h"
+#include "Scheduler/RefreshRateStats.h"
 #include "Scheduler/Scheduler.h"
 #include "Scheduler/VSyncModulator.h"
 #include "SurfaceFlingerFactory.h"
@@ -68,6 +69,7 @@
 
 #include <atomic>
 #include <cstdint>
+#include <functional>
 #include <map>
 #include <memory>
 #include <mutex>
@@ -75,6 +77,7 @@
 #include <set>
 #include <string>
 #include <thread>
+#include <type_traits>
 #include <unordered_map>
 #include <utility>
 
@@ -786,19 +789,9 @@
         };
     }
 
-    /* ------------------------------------------------------------------------
-     * Debugging & dumpsys
+    /*
+     * 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;
@@ -830,17 +823,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
-            REQUIRES(mStateLock);
-    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
-            REQUIRES(mStateLock);
+    /*
+     * 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);
@@ -857,7 +881,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
@@ -1037,11 +1070,16 @@
     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;
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
new file mode 100644
index 0000000..43eebd7
--- /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_1::PixelFormat;
+using ::android::hardware::graphics::common::V1_2::Dataspace;
+
+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..92ce5c2
--- /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_1::PixelFormat defaultValue);
+
+int64_t wcg_composition_dataspace(
+        android::hardware::graphics::common::V1_2::Dataspace defaultValue);
+
+int32_t wcg_composition_pixel_format(
+        android::hardware::graphics::common::V1_1::PixelFormat defaultValue);
+} // 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/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index 3ad6ec3..e7986d3 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -31,14 +31,14 @@
 #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;
     }
 
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/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index 216532a..b95c0ec 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -2664,6 +2664,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 {
@@ -2691,8 +2702,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));
     }
 
@@ -2709,8 +2719,8 @@
         mExpectedPresentTime = expectedPresentTime;
     }
 
-    void verifyTransactionStats(const TransactionStats& transactionStats) const {
-        const auto& [latchTime, presentFence, surfaceStats] = transactionStats;
+    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);
@@ -2727,14 +2737,16 @@
             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);
         }
     }
 
@@ -2745,8 +2757,9 @@
                               ExpectedResult::PreviousBuffer previousBufferResult)
               : mBufferResult(bufferResult), mPreviousBufferResult(previousBufferResult) {}
 
-        void verifySurfaceStats(const SurfaceStats& surfaceStats, nsecs_t latchTime) const {
-            const auto& [surfaceControl, acquireTime, previousReleaseFence] = 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";
@@ -2766,39 +2779,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;
     nsecs_t mExpectedPresentTime = -1;
-    std::unordered_map<sp<IBinder>, ExpectedSurfaceResult, IBinderHash> mExpectedSurfaceResults;
+    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() {
@@ -2806,15 +2820,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 {
@@ -2843,9 +2857,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());
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index ad31a40..e34e568 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -44,8 +44,10 @@
         "LayerHistoryTest.cpp",
         "SchedulerTest.cpp",
         "SchedulerUtilsTest.cpp",
+        "RefreshRateStatsTest.cpp",
         "TimeStatsTest.cpp",
         "mock/DisplayHardware/MockComposer.cpp",
+        "mock/DisplayHardware/MockDisplay.cpp",
         "mock/DisplayHardware/MockPowerAdvisor.cpp",
         "mock/gui/MockGraphicBufferConsumer.cpp",
         "mock/gui/MockGraphicBufferProducer.cpp",
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/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 687941a..2a8dda6 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -335,11 +335,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;
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/MockDisplay.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp
new file mode 100644
index 0000000..2ec37c1
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mock/DisplayHardware/MockDisplay.h"
+
+namespace android {
+namespace Hwc2 {
+namespace mock {
+
+// Explicit default instantiation is recommended.
+Display::Display() = default;
+Display::~Display() = default;
+
+} // namespace mock
+} // 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/vr/bufferhubd/Android.bp b/services/vr/bufferhubd/Android.bp
index ba7d7f9..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",
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_