Merge "Add flag to cmd for dynamic services" into rvc-dev
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index cf75bba..544e26c 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -571,81 +571,6 @@
     return true;
 }
 
-// Poke all the binder-enabled processes in the system to get them to re-read
-// their system properties.
-static bool pokeBinderServices()
-{
-    sp<IServiceManager> sm = defaultServiceManager();
-    Vector<String16> services = sm->listServices();
-    for (size_t i = 0; i < services.size(); i++) {
-        sp<IBinder> obj = sm->checkService(services[i]);
-        if (obj != nullptr) {
-            Parcel data;
-            if (obj->transact(IBinder::SYSPROPS_TRANSACTION, data,
-                    nullptr, 0) != OK) {
-                if (false) {
-                    // XXX: For some reason this fails on tablets trying to
-                    // poke the "phone" service.  It's not clear whether some
-                    // are expected to fail.
-                    String8 svc(services[i]);
-                    fprintf(stderr, "error poking binder service %s\n",
-                        svc.string());
-                    return false;
-                }
-            }
-        }
-    }
-    return true;
-}
-
-// Poke all the HAL processes in the system to get them to re-read
-// their system properties.
-static void pokeHalServices()
-{
-    using ::android::hidl::base::V1_0::IBase;
-    using ::android::hidl::manager::V1_0::IServiceManager;
-    using ::android::hardware::hidl_string;
-    using ::android::hardware::Return;
-
-    sp<IServiceManager> sm = ::android::hardware::defaultServiceManager();
-
-    if (sm == nullptr) {
-        fprintf(stderr, "failed to get IServiceManager to poke hal services\n");
-        return;
-    }
-
-    auto listRet = sm->list([&](const auto &interfaces) {
-        for (size_t i = 0; i < interfaces.size(); i++) {
-            string fqInstanceName = interfaces[i];
-            string::size_type n = fqInstanceName.find('/');
-            if (n == std::string::npos || interfaces[i].size() == n+1)
-                continue;
-            hidl_string fqInterfaceName = fqInstanceName.substr(0, n);
-            hidl_string instanceName = fqInstanceName.substr(n+1, std::string::npos);
-            Return<sp<IBase>> interfaceRet = sm->get(fqInterfaceName, instanceName);
-            if (!interfaceRet.isOk()) {
-                // ignore
-                continue;
-            }
-
-            sp<IBase> interface = interfaceRet;
-            if (interface == nullptr) {
-                // ignore
-                continue;
-            }
-
-            auto notifyRet = interface->notifySyspropsChanged();
-            if (!notifyRet.isOk()) {
-                // ignore
-            }
-        }
-    });
-    if (!listRet.isOk()) {
-        // TODO(b/34242478) fix this when we determine the correct ACL
-        //fprintf(stderr, "failed to list services: %s\n", listRet.description().c_str());
-    }
-}
-
 // Set the trace tags that userland tracing uses, and poke the running
 // processes to pick up the new value.
 static bool setTagsProperty(uint64_t tags)
@@ -876,10 +801,6 @@
     }
     ok &= setAppCmdlineProperty(&packageList[0]);
     ok &= setTagsProperty(tags);
-#if !ATRACE_SHMEM
-    ok &= pokeBinderServices();
-    pokeHalServices();
-#endif
     if (g_tracePdx) {
         ok &= ServiceUtility::PokeServices();
     }
@@ -891,8 +812,6 @@
 {
     setTagsProperty(0);
     clearAppProperties();
-    pokeBinderServices();
-    pokeHalServices();
 
     if (g_tracePdx) {
         ServiceUtility::PokeServices();
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
index 87ea520..a0b9cbb 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -84,7 +84,8 @@
                                                 android::base::unique_fd bugreport_fd,
                                                 android::base::unique_fd screenshot_fd,
                                                 int bugreport_mode,
-                                                const sp<IDumpstateListener>& listener) {
+                                                const sp<IDumpstateListener>& listener,
+                                                bool is_screenshot_requested) {
     MYLOGI("startBugreport() with mode: %d\n", bugreport_mode);
 
     // Ensure there is only one bugreport in progress at a time.
@@ -118,9 +119,9 @@
 
     std::unique_ptr<Dumpstate::DumpOptions> options = std::make_unique<Dumpstate::DumpOptions>();
     options->Initialize(static_cast<Dumpstate::BugreportMode>(bugreport_mode), bugreport_fd,
-                        screenshot_fd);
+                        screenshot_fd, is_screenshot_requested);
 
-    if (bugreport_fd.get() == -1 || (options->do_fb && screenshot_fd.get() == -1)) {
+    if (bugreport_fd.get() == -1 || (options->do_screenshot && screenshot_fd.get() == -1)) {
         MYLOGE("Invalid filedescriptor");
         signalErrorAndExit(listener, IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
     }
diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h
index 6dc0225..ac8d3ac 100644
--- a/cmds/dumpstate/DumpstateService.h
+++ b/cmds/dumpstate/DumpstateService.h
@@ -41,7 +41,8 @@
     binder::Status startBugreport(int32_t calling_uid, const std::string& calling_package,
                                   android::base::unique_fd bugreport_fd,
                                   android::base::unique_fd screenshot_fd, int bugreport_mode,
-                                  const sp<IDumpstateListener>& listener) override;
+                                  const sp<IDumpstateListener>& listener,
+                                  bool is_screenshot_requested) override;
 
     // No-op
     binder::Status cancelBugreport();
diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
index 3f359c8..ba008bb 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
@@ -64,10 +64,12 @@
      * @param screenshotFd the file to which screenshot should be written
      * @param bugreportMode the mode that specifies other run time options; must be one of above
      * @param listener callback for updates; optional
+     * @param isScreenshotRequested indicates screenshot is requested or not
      */
     void startBugreport(int callingUid, @utf8InCpp String callingPackage,
                         FileDescriptor bugreportFd, FileDescriptor screenshotFd,
-                        int bugreportMode, IDumpstateListener listener);
+                        int bugreportMode, IDumpstateListener listener,
+                        boolean isScreenshotRequested);
 
     /*
      * Cancels the bugreport currently in progress.
diff --git a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
index e486460..e17f18e 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
@@ -61,4 +61,9 @@
      * Called when taking bugreport finishes successfully.
      */
     void onFinished();
+
+    /**
+     * Called when screenshot is taken.
+     */
+    void onScreenshotTaken(boolean success);
 }
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 814a4ed..01b7e63 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -647,6 +647,24 @@
     std::lock_guard<std::mutex> lock(lock_);
     result_ = APPROVED;
     MYLOGD("User approved consent to share bugreport\n");
+
+    // Maybe copy screenshot so calling app can display the screenshot to the user as soon as
+    // consent is granted.
+    if (ds.options_->is_screenshot_copied) {
+        return android::binder::Status::ok();
+    }
+
+    if (!ds.options_->do_screenshot || ds.options_->screenshot_fd.get() == -1 ||
+        !ds.do_early_screenshot_) {
+        return android::binder::Status::ok();
+    }
+
+    bool copy_succeeded = android::os::CopyFileToFd(ds.screenshot_path_,
+                                                    ds.options_->screenshot_fd.get());
+    ds.options_->is_screenshot_copied = copy_succeeded;
+    if (copy_succeeded) {
+        android::os::UnlinkAndLogOnError(ds.screenshot_path_);
+    }
     return android::binder::Status::ok();
 }
 
@@ -1407,7 +1425,7 @@
     /* Dump Bluetooth HCI logs */
     ds.AddDir("/data/misc/bluetooth/logs", true);
 
-    if (ds.options_->do_fb && !ds.do_early_screenshot_) {
+    if (ds.options_->do_screenshot && !ds.do_early_screenshot_) {
         MYLOGI("taking late screenshot\n");
         ds.TakeScreenshot();
     }
@@ -1459,6 +1477,8 @@
         ds.AddDir(WMTRACE_DATA_DIR, false);
     }
 
+    ds.AddDir(SNAPSHOTCTL_LOG_DIR, false);
+
     RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(ds.DumpstateBoard);
 
     /* Migrate the ril_dumpstate to a device specific dumpstate? */
@@ -1587,7 +1607,6 @@
     ds.AddDir(RECOVERY_DATA_DIR, true);
     ds.AddDir(UPDATE_ENGINE_LOG_DIR, true);
     ds.AddDir(LOGPERSIST_DATA_DIR, false);
-    ds.AddDir(SNAPSHOTCTL_LOG_DIR, false);
     if (!PropertiesHelper::IsUserBuild()) {
         ds.AddDir(PROFILE_DATA_DIR_CUR, true);
         ds.AddDir(PROFILE_DATA_DIR_REF, true);
@@ -1710,6 +1729,8 @@
     RunDumpsys("DUMPSYS", {"netpolicy"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10));
     RunDumpsys("DUMPSYS", {"network_management"}, CommandOptions::WithTimeout(90).Build(),
                SEC_TO_MSEC(10));
+    RunDumpsys("DUMPSYS", {"telephony.registry"}, CommandOptions::WithTimeout(90).Build(),
+               SEC_TO_MSEC(10));
     if (include_sensitive_info) {
         // Contains raw IP addresses, omit from reports on user builds.
         RunDumpsys("DUMPSYS", {"netd"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10));
@@ -2149,8 +2170,8 @@
         ds.base_name_ += "-wifi";
     }
 
-    if (ds.options_->do_fb) {
-        ds.screenshot_path_ = ds.GetPath(ds.CalledByApi() ? "-tmp.png" : ".png");
+    if (ds.options_->do_screenshot) {
+        ds.screenshot_path_ = ds.GetPath(ds.CalledByApi() ? "-png.tmp" : ".png");
     }
     ds.tmp_path_ = ds.GetPath(".tmp");
     ds.log_path_ = ds.GetPath("-dumpstate_log-" + std::to_string(ds.pid_) + ".txt");
@@ -2169,7 +2190,7 @@
         ds.tmp_path_.c_str(), ds.screenshot_path_.c_str());
 
     if (ds.options_->do_zip_file) {
-        ds.path_ = ds.GetPath(ds.CalledByApi() ? "-tmp.zip" : ".zip");
+        ds.path_ = ds.GetPath(ds.CalledByApi() ? "-zip.tmp" : ".zip");
         MYLOGD("Creating initial .zip file (%s)\n", ds.path_.c_str());
         create_parent_dirs(ds.path_.c_str());
         ds.zip_file.reset(fopen(ds.path_.c_str(), "wb"));
@@ -2228,44 +2249,47 @@
     }
 }
 
-static void SetOptionsFromMode(Dumpstate::BugreportMode mode, Dumpstate::DumpOptions* options) {
+static void SetOptionsFromMode(Dumpstate::BugreportMode mode, Dumpstate::DumpOptions* options,
+                               bool is_screenshot_requested) {
+    // Modify com.android.shell.BugreportProgressService#isDefaultScreenshotRequired as well for
+    // default system screenshots.
     options->bugreport_mode = ModeToString(mode);
     switch (mode) {
         case Dumpstate::BugreportMode::BUGREPORT_FULL:
-            options->do_fb = true;
+            options->do_screenshot = is_screenshot_requested;
             options->dumpstate_hal_mode = DumpstateMode::FULL;
             break;
         case Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE:
             // Currently, the dumpstate binder is only used by Shell to update progress.
             options->do_start_service = true;
             options->do_progress_updates = true;
-            options->do_fb = false;
+            options->do_screenshot = is_screenshot_requested;
             options->dumpstate_hal_mode = DumpstateMode::INTERACTIVE;
             break;
         case Dumpstate::BugreportMode::BUGREPORT_REMOTE:
             options->do_vibrate = false;
             options->is_remote_mode = true;
-            options->do_fb = false;
+            options->do_screenshot = false;
             options->dumpstate_hal_mode = DumpstateMode::REMOTE;
             break;
         case Dumpstate::BugreportMode::BUGREPORT_WEAR:
             options->do_start_service = true;
             options->do_progress_updates = true;
             options->do_zip_file = true;
-            options->do_fb = true;
+            options->do_screenshot = is_screenshot_requested;
             options->dumpstate_hal_mode = DumpstateMode::WEAR;
             break;
         // TODO(b/148168577) rename TELEPHONY everywhere to CONNECTIVITY.
         case Dumpstate::BugreportMode::BUGREPORT_TELEPHONY:
             options->telephony_only = true;
             options->do_progress_updates = true;
-            options->do_fb = false;
+            options->do_screenshot = false;
             options->dumpstate_hal_mode = DumpstateMode::CONNECTIVITY;
             break;
         case Dumpstate::BugreportMode::BUGREPORT_WIFI:
             options->wifi_only = true;
             options->do_zip_file = true;
-            options->do_fb = false;
+            options->do_screenshot = false;
             options->dumpstate_hal_mode = DumpstateMode::WIFI;
             break;
         case Dumpstate::BugreportMode::BUGREPORT_DEFAULT:
@@ -2275,12 +2299,13 @@
 
 static void LogDumpOptions(const Dumpstate::DumpOptions& options) {
     MYLOGI(
-        "do_zip_file: %d do_vibrate: %d use_socket: %d use_control_socket: %d do_fb: %d "
+        "do_zip_file: %d do_vibrate: %d use_socket: %d use_control_socket: %d do_screenshot: %d "
         "is_remote_mode: %d show_header_only: %d do_start_service: %d telephony_only: %d "
         "wifi_only: %d do_progress_updates: %d fd: %d bugreport_mode: %s dumpstate_hal_mode: %s "
         "args: %s\n",
         options.do_zip_file, options.do_vibrate, options.use_socket, options.use_control_socket,
-        options.do_fb, options.is_remote_mode, options.show_header_only, options.do_start_service,
+        options.do_screenshot, options.is_remote_mode, options.show_header_only,
+        options.do_start_service,
         options.telephony_only, options.wifi_only, options.do_progress_updates,
         options.bugreport_fd.get(), options.bugreport_mode.c_str(),
         toString(options.dumpstate_hal_mode).c_str(), options.args.c_str());
@@ -2288,7 +2313,8 @@
 
 void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode,
                                         const android::base::unique_fd& bugreport_fd_in,
-                                        const android::base::unique_fd& screenshot_fd_in) {
+                                        const android::base::unique_fd& screenshot_fd_in,
+                                        bool is_screenshot_requested) {
     // In the new API world, date is always added; output is always a zip file.
     // TODO(111441001): remove these options once they are obsolete.
     do_add_date = true;
@@ -2298,7 +2324,7 @@
     bugreport_fd.reset(dup(bugreport_fd_in.get()));
     screenshot_fd.reset(dup(screenshot_fd_in.get()));
 
-    SetOptionsFromMode(bugreport_mode, this);
+    SetOptionsFromMode(bugreport_mode, this, is_screenshot_requested);
 }
 
 Dumpstate::RunStatus Dumpstate::DumpOptions::Initialize(int argc, char* argv[]) {
@@ -2313,7 +2339,7 @@
             case 'S': use_control_socket = true;     break;
             case 'v': show_header_only = true;       break;
             case 'q': do_vibrate = false;            break;
-            case 'p': do_fb = true;                  break;
+            case 'p': do_screenshot = true;          break;
             case 'P': do_progress_updates = true;    break;
             case 'R': is_remote_mode = true;         break;
             case 'V':                                break;  // compatibility no-op
@@ -2543,11 +2569,6 @@
         Vibrate(150);
     }
 
-    if (options_->do_fb && do_early_screenshot_) {
-        MYLOGI("taking early screenshot\n");
-        TakeScreenshot();
-    }
-
     if (options_->do_zip_file && zip_file != nullptr) {
         if (chown(path_.c_str(), AID_SHELL, AID_SHELL)) {
             MYLOGE("Unable to change ownership of zip file %s: %s\n", path_.c_str(),
@@ -2593,19 +2614,20 @@
     PrintHeader();
 
     if (options_->telephony_only) {
+        MaybeTakeEarlyScreenshot();
         MaybeCheckUserConsent(calling_uid, calling_package);
         DumpstateTelephonyOnly(calling_package);
         DumpstateBoard();
     } else if (options_->wifi_only) {
+        MaybeTakeEarlyScreenshot();
         MaybeCheckUserConsent(calling_uid, calling_package);
         DumpstateWifiOnly();
     } else {
-        // Invoking the critical dumpsys calls before DumpTraces() to try and
-        // keep the system stats as close to its initial state as possible.
+        // Invoke critical dumpsys first to preserve system state, before doing anything else.
         RunDumpsysCritical();
 
-        // Run consent check only after critical dumpsys has finished -- so the consent
-        // isn't going to pollute the system state / logs.
+        // Take screenshot and get consent only after critical dumpsys has finished.
+        MaybeTakeEarlyScreenshot();
         MaybeCheckUserConsent(calling_uid, calling_package);
 
         // Dump state for the default case. This also drops root.
@@ -2640,7 +2662,9 @@
             MYLOGI("User denied consent. Returning\n");
             return status;
         }
-        if (options_->do_fb && options_->screenshot_fd.get() != -1) {
+        if (options_->do_screenshot &&
+            options_->screenshot_fd.get() != -1 &&
+            !options_->is_screenshot_copied) {
             bool copy_succeeded = android::os::CopyFileToFd(screenshot_path_,
                                                             options_->screenshot_fd.get());
             if (copy_succeeded) {
@@ -2694,6 +2718,14 @@
                : RunStatus::OK;
 }
 
+void Dumpstate::MaybeTakeEarlyScreenshot() {
+    if (!options_->do_screenshot || !do_early_screenshot_) {
+        return;
+    }
+
+    TakeScreenshot();
+}
+
 void Dumpstate::MaybeCheckUserConsent(int32_t calling_uid, const std::string& calling_package) {
     if (calling_uid == AID_SHELL || !CalledByApi()) {
         // No need to get consent for shell triggered dumpstates, or not through
@@ -3630,6 +3662,11 @@
     } else {
         MYLOGE("Failed to take screenshot on %s\n", real_path.c_str());
     }
+    if (listener_ != nullptr) {
+        // Show a visual indication to indicate screenshot is taken via
+        // IDumpstateListener.onScreenshotTaken()
+        listener_->onScreenshotTaken(status == 0);
+    }
 }
 
 bool is_dir(const char* pathname) {
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 111c098..7b8d282 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -359,7 +359,8 @@
         // Writes bugreport content to a socket; only flatfile format is supported.
         bool use_socket = false;
         bool use_control_socket = false;
-        bool do_fb = false;
+        bool do_screenshot = false;
+        bool is_screenshot_copied = false;
         bool is_remote_mode = false;
         bool show_header_only = false;
         bool do_start_service = false;
@@ -389,7 +390,8 @@
 
         /* Initializes options from the requested mode. */
         void Initialize(BugreportMode bugreport_mode, const android::base::unique_fd& bugreport_fd,
-                        const android::base::unique_fd& screenshot_fd);
+                        const android::base::unique_fd& screenshot_fd,
+                        bool is_screenshot_requested);
 
         /* Returns true if the options set so far are consistent. */
         bool ValidateOptions() const;
@@ -494,6 +496,8 @@
 
     RunStatus DumpstateDefaultAfterCritical();
 
+    void MaybeTakeEarlyScreenshot();
+
     void MaybeCheckUserConsent(int32_t calling_uid, const std::string& calling_package);
 
     // Removes the in progress files output files (tmp file, zip/txt file, screenshot),
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index dac90d9..047a1c3 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -167,6 +167,12 @@
         return binder::Status::ok();
     }
 
+    binder::Status onScreenshotTaken(bool success) override {
+        std::lock_guard<std::mutex> lock(lock_);
+        dprintf(out_fd_, "\rResult of taking screenshot: %s", success ? "success" : "failure");
+        return binder::Status::ok();
+    }
+
     bool getIsFinished() {
         std::lock_guard<std::mutex> lock(lock_);
         return is_finished_;
@@ -445,7 +451,7 @@
     sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout))));
     android::binder::Status status =
         ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd),
-                                  Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener);
+                                  Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener, true);
     // startBugreport is an async call. Verify binder call succeeded first, then wait till listener
     // gets expected callbacks.
     EXPECT_TRUE(status.isOk());
@@ -482,7 +488,7 @@
     android::binder::Status status =
         ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd),
                                   2000,  // invalid bugreport mode
-                                  listener);
+                                  listener, false);
     EXPECT_EQ(listener->getErrorCode(), IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
 
     // The service should have died, freeing itself up for a new invocation.
@@ -513,13 +519,13 @@
     sp<DumpstateListener> listener1(new DumpstateListener(dup(fileno(stdout))));
     android::binder::Status status =
         ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd),
-                                  Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener1);
+                                  Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener1, true);
     EXPECT_TRUE(status.isOk());
 
     // try to make another call to startBugreport. This should fail.
     sp<DumpstateListener> listener2(new DumpstateListener(dup(fileno(stdout))));
     status = ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd2), std::move(screenshot_fd2),
-                                       Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener2);
+                                       Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener2, true);
     EXPECT_FALSE(status.isOk());
     WaitTillExecutionComplete(listener2.get());
     EXPECT_EQ(listener2->getErrorCode(),
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 76b9960..2efb130 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -65,6 +65,7 @@
     MOCK_METHOD1(onProgress, binder::Status(int32_t progress));
     MOCK_METHOD1(onError, binder::Status(int32_t error_code));
     MOCK_METHOD0(onFinished, binder::Status());
+    MOCK_METHOD1(onScreenshotTaken, binder::Status(bool success));
 
   protected:
     MOCK_METHOD0(onAsBinder, IBinder*());
@@ -173,7 +174,7 @@
     EXPECT_FALSE(options_.use_control_socket);
     EXPECT_FALSE(options_.show_header_only);
     EXPECT_TRUE(options_.do_vibrate);
-    EXPECT_FALSE(options_.do_fb);
+    EXPECT_FALSE(options_.do_screenshot);
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
@@ -199,7 +200,7 @@
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
     EXPECT_FALSE(options_.show_header_only);
-    EXPECT_FALSE(options_.do_fb);
+    EXPECT_FALSE(options_.do_screenshot);
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
     EXPECT_FALSE(options_.use_socket);
@@ -225,16 +226,16 @@
     EXPECT_FALSE(options_.do_zip_file);
     EXPECT_FALSE(options_.use_control_socket);
     EXPECT_FALSE(options_.show_header_only);
-    EXPECT_FALSE(options_.do_fb);
+    EXPECT_FALSE(options_.do_screenshot);
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
 TEST_F(DumpOptionsTest, InitializeFullBugReport) {
-    options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_FULL, fd, fd);
+    options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_FULL, fd, fd, true);
     EXPECT_TRUE(options_.do_add_date);
-    EXPECT_TRUE(options_.do_fb);
+    EXPECT_TRUE(options_.do_screenshot);
     EXPECT_TRUE(options_.do_zip_file);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::FULL);
 
@@ -249,12 +250,12 @@
 }
 
 TEST_F(DumpOptionsTest, InitializeInteractiveBugReport) {
-    options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, fd, fd);
+    options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, fd, fd, true);
     EXPECT_TRUE(options_.do_add_date);
     EXPECT_TRUE(options_.do_zip_file);
     EXPECT_TRUE(options_.do_progress_updates);
     EXPECT_TRUE(options_.do_start_service);
-    EXPECT_FALSE(options_.do_fb);
+    EXPECT_TRUE(options_.do_screenshot);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::INTERACTIVE);
 
     // Other options retain default values
@@ -266,12 +267,12 @@
 }
 
 TEST_F(DumpOptionsTest, InitializeRemoteBugReport) {
-    options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_REMOTE, fd, fd);
+    options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_REMOTE, fd, fd, false);
     EXPECT_TRUE(options_.do_add_date);
     EXPECT_TRUE(options_.do_zip_file);
     EXPECT_TRUE(options_.is_remote_mode);
     EXPECT_FALSE(options_.do_vibrate);
-    EXPECT_FALSE(options_.do_fb);
+    EXPECT_FALSE(options_.do_screenshot);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::REMOTE);
 
     // Other options retain default values
@@ -282,9 +283,9 @@
 }
 
 TEST_F(DumpOptionsTest, InitializeWearBugReport) {
-    options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WEAR, fd, fd);
+    options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WEAR, fd, fd, true);
     EXPECT_TRUE(options_.do_add_date);
-    EXPECT_TRUE(options_.do_fb);
+    EXPECT_TRUE(options_.do_screenshot);
     EXPECT_TRUE(options_.do_zip_file);
     EXPECT_TRUE(options_.do_progress_updates);
     EXPECT_TRUE(options_.do_start_service);
@@ -299,9 +300,9 @@
 }
 
 TEST_F(DumpOptionsTest, InitializeTelephonyBugReport) {
-    options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_TELEPHONY, fd, fd);
+    options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_TELEPHONY, fd, fd, false);
     EXPECT_TRUE(options_.do_add_date);
-    EXPECT_FALSE(options_.do_fb);
+    EXPECT_FALSE(options_.do_screenshot);
     EXPECT_TRUE(options_.do_zip_file);
     EXPECT_TRUE(options_.telephony_only);
     EXPECT_TRUE(options_.do_progress_updates);
@@ -316,9 +317,9 @@
 }
 
 TEST_F(DumpOptionsTest, InitializeWifiBugReport) {
-    options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WIFI, fd, fd);
+    options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WIFI, fd, fd, false);
     EXPECT_TRUE(options_.do_add_date);
-    EXPECT_FALSE(options_.do_fb);
+    EXPECT_FALSE(options_.do_screenshot);
     EXPECT_TRUE(options_.do_zip_file);
     EXPECT_TRUE(options_.wifi_only);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::WIFI);
@@ -346,7 +347,7 @@
 
     EXPECT_EQ(status, Dumpstate::RunStatus::OK);
     EXPECT_TRUE(options_.do_add_date);
-    EXPECT_TRUE(options_.do_fb);
+    EXPECT_TRUE(options_.do_screenshot);
     EXPECT_TRUE(options_.do_zip_file);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 
@@ -384,7 +385,7 @@
     // Other options retain default values
     EXPECT_FALSE(options_.show_header_only);
     EXPECT_TRUE(options_.do_vibrate);
-    EXPECT_FALSE(options_.do_fb);
+    EXPECT_FALSE(options_.do_screenshot);
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
@@ -407,7 +408,7 @@
     EXPECT_EQ(status, Dumpstate::RunStatus::OK);
     EXPECT_TRUE(options_.show_header_only);
     EXPECT_FALSE(options_.do_vibrate);
-    EXPECT_TRUE(options_.do_fb);
+    EXPECT_TRUE(options_.do_screenshot);
     EXPECT_TRUE(options_.do_progress_updates);
     EXPECT_TRUE(options_.is_remote_mode);
 
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 70bbc33..1af6edd 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -1273,11 +1273,6 @@
 Dex2oatFileWrapper maybe_open_app_image(const char* out_oat_path,
         bool generate_app_image, bool is_public, int uid, bool is_secondary_dex) {
 
-    // We don't create an image for secondary dex files.
-    if (is_secondary_dex) {
-        return Dex2oatFileWrapper();
-    }
-
     const std::string image_path = create_image_filename(out_oat_path);
     if (image_path.empty()) {
         // Happens when the out_oat_path has an unknown extension.
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index eefbe4f..d773790 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -138,10 +138,10 @@
             return 4;
         }
 
-        PrepareEnvironment();
+        PrepareEnvironmentVariables();
 
-        if (!PrepareBootImage(/* force */ false)) {
-            LOG(ERROR) << "Failed preparing boot image.";
+        if (!EnsureBootImageAndDalvikCache()) {
+            LOG(ERROR) << "Bad boot image.";
             return 5;
         }
 
@@ -302,7 +302,7 @@
         return parameters_.ReadArguments(argc, const_cast<const char**>(argv));
     }
 
-    void PrepareEnvironment() {
+    void PrepareEnvironmentVariables() {
         environ_.push_back(StringPrintf("BOOTCLASSPATH=%s", boot_classpath_.c_str()));
         environ_.push_back(StringPrintf("ANDROID_DATA=%s", GetOTADataDirectory().c_str()));
         environ_.push_back(StringPrintf("ANDROID_ROOT=%s", android_root_.c_str()));
@@ -312,9 +312,8 @@
         }
     }
 
-    // Ensure that we have the right boot image. The first time any app is
-    // compiled, we'll try to generate it.
-    bool PrepareBootImage(bool force) const {
+    // Ensure that we have the right boot image and cache file structures.
+    bool EnsureBootImageAndDalvikCache() const {
         if (parameters_.instruction_set == nullptr) {
             LOG(ERROR) << "Instruction set missing.";
             return false;
@@ -340,34 +339,19 @@
             }
         }
 
-        // Check whether we have files in /data.
+        // Clear cached artifacts.
+        ClearDirectory(isa_path);
+
+        // Check whether we have a boot image.
         // TODO: check that the files are correct wrt/ jars.
-        std::string art_path = isa_path + "/system@framework@boot.art";
-        std::string oat_path = isa_path + "/system@framework@boot.oat";
-        bool cleared = false;
-        if (access(art_path.c_str(), F_OK) == 0 && access(oat_path.c_str(), F_OK) == 0) {
-            // Files exist, assume everything is alright if not forced. Otherwise clean up.
-            if (!force) {
-                return true;
-            }
-            ClearDirectory(isa_path);
-            cleared = true;
+        std::string preopted_boot_art_path =
+            StringPrintf("/apex/com.android.art/javalib/%s/boot.art", isa);
+        if (access(preopted_boot_art_path.c_str(), F_OK) != 0) {
+            PLOG(ERROR) << "Bad access() to " << preopted_boot_art_path;
+            return false;
         }
 
-        // Check whether we have an image in /system.
-        // TODO: check that the files are correct wrt/ jars.
-        std::string preopted_boot_art_path = StringPrintf("/system/framework/%s/boot.art", isa);
-        if (access(preopted_boot_art_path.c_str(), F_OK) == 0) {
-            // Note: we ignore |force| here.
-            return true;
-        }
-
-
-        if (!cleared) {
-            ClearDirectory(isa_path);
-        }
-
-        return Dex2oatBootImage(boot_classpath_, art_path, oat_path, isa);
+        return true;
     }
 
     static bool CreatePath(const std::string& path) {
@@ -432,77 +416,6 @@
         CHECK_EQ(0, closedir(c_dir)) << "Unable to close directory.";
     }
 
-    bool Dex2oatBootImage(const std::string& boot_cp,
-                          const std::string& art_path,
-                          const std::string& oat_path,
-                          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(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()));
-        }
-        cmd.push_back(StringPrintf("--oat-file=%s", oat_path.c_str()));
-
-        int32_t base_offset = ChooseRelocationOffsetDelta(
-                art::imagevalues::GetImageMinBaseAddressDelta(),
-                art::imagevalues::GetImageMaxBaseAddressDelta());
-        cmd.push_back(StringPrintf("--base=0x%x",
-                art::imagevalues::GetImageBaseAddress() + base_offset));
-
-        cmd.push_back(StringPrintf("--instruction-set=%s", isa));
-
-        // These things are pushed by AndroidRuntime, see frameworks/base/core/jni/AndroidRuntime.cpp.
-        AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-Xms",
-                "-Xms",
-                true,
-                cmd);
-        AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-Xmx",
-                "-Xmx",
-                true,
-                cmd);
-        AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-filter",
-                "--compiler-filter=",
-                false,
-                cmd);
-        cmd.push_back("--profile-file=/system/etc/boot-image.prof");
-        // TODO: Compiled-classes.
-        const std::string* extra_opts =
-                system_properties_.GetProperty("dalvik.vm.image-dex2oat-flags");
-        if (extra_opts != nullptr) {
-            std::vector<std::string> extra_vals = Split(*extra_opts, " ");
-            cmd.insert(cmd.end(), extra_vals.begin(), extra_vals.end());
-        }
-        // TODO: Should we lower this? It's usually set close to max, because
-        //       normally there's not much else going on at boot.
-        AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-threads",
-                "-j",
-                false,
-                cmd);
-        AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-cpu-set",
-                "--cpu-set=",
-                false,
-                cmd);
-        AddCompilerOptionFromSystemProperty(
-                StringPrintf("dalvik.vm.isa.%s.variant", isa).c_str(),
-                "--instruction-set-variant=",
-                false,
-                cmd);
-        AddCompilerOptionFromSystemProperty(
-                StringPrintf("dalvik.vm.isa.%s.features", isa).c_str(),
-                "--instruction-set-features=",
-                false,
-                cmd);
-
-        std::string error_msg;
-        bool result = Exec(cmd, &error_msg);
-        if (!result) {
-            LOG(ERROR) << "Could not generate boot image: " << error_msg;
-        }
-        return result;
-    }
-
     static const char* ParseNull(const char* arg) {
         return (strcmp(arg, "!") == 0) ? nullptr : arg;
     }
@@ -592,22 +505,6 @@
             return 0;
         }
 
-        // If the dexopt failed, we may have a stale boot image from a previous OTA run.
-        // Then regenerate and retry.
-        if (WEXITSTATUS(dexopt_result) ==
-                static_cast<int>(::art::dex2oat::ReturnCode::kCreateRuntime)) {
-            if (!PrepareBootImage(/* force */ true)) {
-                LOG(ERROR) << "Forced boot image creating failed. Original error return was "
-                        << dexopt_result;
-                return dexopt_result;
-            }
-
-            int dexopt_result_boot_image_retry = Dexopt();
-            if (dexopt_result_boot_image_retry == 0) {
-                return 0;
-            }
-        }
-
         // If this was a profile-guided run, we may have profile version issues. Try to downgrade,
         // if possible.
         if ((parameters_.dexopt_flags & DEXOPT_PROFILE_GUIDED) == 0) {
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp
index a7ccf64..fb11cee 100644
--- a/cmds/lshal/ListCommand.cpp
+++ b/cmds/lshal/ListCommand.cpp
@@ -206,9 +206,12 @@
 static bool scanBinderContext(pid_t pid,
         const std::string &contextName,
         std::function<void(const std::string&)> eachLine) {
-    std::ifstream ifs("/d/binder/proc/" + std::to_string(pid));
+    std::ifstream ifs("/dev/binderfs/binder_logs/proc/" + std::to_string(pid));
     if (!ifs.is_open()) {
-        return false;
+        ifs.open("/d/binder/proc/" + std::to_string(pid));
+        if (!ifs.is_open()) {
+            return false;
+        }
     }
 
     static const std::regex kContextLine("^context (\\w+)$");
@@ -403,7 +406,7 @@
         return false;
     }
 
-    if (fqInstance.getPackage() == gIBaseFqName.package()) {
+    if (fqInstance.getPackage() == "android.hidl.base") {
         return true; // always remove IBase from manifest
     }
 
diff --git a/cmds/lshal/ListCommand.h b/cmds/lshal/ListCommand.h
index b3ed23d..acc0dcf 100644
--- a/cmds/lshal/ListCommand.h
+++ b/cmds/lshal/ListCommand.h
@@ -104,7 +104,8 @@
     Status fetchBinderizedEntry(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager,
                                 TableEntry *entry);
 
-    // Get relevant information for a PID by parsing files under /d/binder.
+    // Get relevant information for a PID by parsing files under
+    // /dev/binderfs/binder_logs or /d/binder.
     // It is a virtual member function so that it can be mocked.
     virtual bool getPidInfo(pid_t serverPid, PidInfo *info) const;
     // Retrieve from mCachedPidInfos and call getPidInfo if necessary.
diff --git a/data/etc/android.hardware.device_unique_attestation.xml b/data/etc/android.hardware.device_unique_attestation.xml
new file mode 100644
index 0000000..309be7a
--- /dev/null
+++ b/data/etc/android.hardware.device_unique_attestation.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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 with Keymaster that support unique attestation. -->
+<permissions>
+    <feature name="android.hardware.device_unique_attestation" />
+</permissions>
diff --git a/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-0.5-crop-11.png b/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-0.5-crop-11.png
new file mode 100644
index 0000000..1acc59d
--- /dev/null
+++ b/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-0.5-crop-11.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-169.png b/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-169.png
new file mode 100644
index 0000000..4ab9ca4
--- /dev/null
+++ b/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-169.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-43.png b/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-43.png
new file mode 100644
index 0000000..d74e673
--- /dev/null
+++ b/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-43.png
Binary files differ
diff --git a/include/android/bitmap.h b/include/android/bitmap.h
index 727a4af..2631b14 100644
--- a/include/android/bitmap.h
+++ b/include/android/bitmap.h
@@ -227,6 +227,7 @@
                            AndroidBitmap_CompressWriteFunc fn) __INTRODUCED_IN(30);
 
 struct AHardwareBuffer;
+typedef struct AHardwareBuffer AHardwareBuffer;
 
 /**
  *  Retrieve the native object associated with a HARDWARE Bitmap.
diff --git a/include/android/thermal.h b/include/android/thermal.h
index 0f4b4d9..3247fa1 100644
--- a/include/android/thermal.h
+++ b/include/android/thermal.h
@@ -109,7 +109,7 @@
  * It's passed the updated thermal status as parameter, as well as the
  * pointer provided by the client that registered a callback.
  */
-typedef int (*AThermal_StatusCallback)(void *data, AThermalStatus status);
+typedef void (*AThermal_StatusCallback)(void *data, AThermalStatus status);
 
 /**
   * Acquire an instance of the thermal manager. This must be freed using
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 8ca178c..7ca9031 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -404,6 +404,13 @@
      */
     bool hasPendingBatch() const;
 
+    /* Returns the source of first pending batch if exist.
+     *
+     * Should be called after calling consume() with consumeBatches == false to determine
+     * whether consume() should be called again later on with consumeBatches == true.
+     */
+    int32_t getPendingBatchSource() const;
+
 private:
     // True if touch resampling is enabled.
     const bool mResampleTouch;
diff --git a/include/ui/FatVector.h b/include/ui/FatVector.h
deleted file mode 120000
index c2047c0..0000000
--- a/include/ui/FatVector.h
+++ /dev/null
@@ -1 +0,0 @@
-../../libs/ui/include/ui/FatVector.h
\ No newline at end of file
diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp
index 5e4c98f..4f2709d 100644
--- a/libs/binder/ActivityManager.cpp
+++ b/libs/binder/ActivityManager.cpp
@@ -98,6 +98,15 @@
     return PROCESS_STATE_UNKNOWN;
 }
 
+bool ActivityManager::isUidActiveOrForeground(const uid_t uid, const String16& callingPackage)
+{
+    sp<IActivityManager> service = getService();
+    if (service != nullptr) {
+        return service->isUidActiveOrForeground(uid, callingPackage);
+    }
+    return false;
+}
+
 status_t ActivityManager::linkToDeath(const sp<IBinder::DeathRecipient>& recipient) {
     sp<IActivityManager> service = getService();
     if (service != nullptr) {
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index bc541f4..e6cfeb4 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -158,7 +158,9 @@
 filegroup {
     name: "libbinder_aidl",
     srcs: [
+        "aidl/android/content/pm/IPackageChangeObserver.aidl",
         "aidl/android/content/pm/IPackageManagerNative.aidl",
+        "aidl/android/content/pm/PackageChangeEvent.aidl",
         "aidl/android/os/IClientCallback.aidl",
         "aidl/android/os/IServiceCallback.aidl",
         "aidl/android/os/IServiceManager.aidl",
diff --git a/libs/binder/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp
index aeca12b..2174ce2 100644
--- a/libs/binder/AppOpsManager.cpp
+++ b/libs/binder/AppOpsManager.cpp
@@ -30,24 +30,10 @@
 
 namespace android {
 
-namespace {
-
-#if defined(__BRILLO__)
-// Because Brillo has no application model, security policy is managed
-// statically (at build time) with SELinux controls.
-// As a consequence, it also never runs the AppOpsManager service.
-const int APP_OPS_MANAGER_UNAVAILABLE_MODE = AppOpsManager::MODE_ALLOWED;
-#else
-const int APP_OPS_MANAGER_UNAVAILABLE_MODE = AppOpsManager::MODE_IGNORED;
-#endif  // defined(__BRILLO__)
-
-}  // namespace
-
-static String16 _appops("appops");
-static pthread_mutex_t gClientIdMutex = PTHREAD_MUTEX_INITIALIZER;
-static sp<IBinder> gClientId;
-
 static const sp<IBinder>& getClientId() {
+    static pthread_mutex_t gClientIdMutex = PTHREAD_MUTEX_INITIALIZER;
+    static sp<IBinder> gClientId;
+
     pthread_mutex_lock(&gClientIdMutex);
     if (gClientId == nullptr) {
         gClientId = new BBinder();
@@ -56,22 +42,13 @@
     return gClientId;
 }
 
-thread_local uint64_t notedAppOpsInThisBinderTransaction[2];
-thread_local int32_t uidOfThisBinderTransaction = -1;
-
-// Whether an appop should be collected: 0 == not initialized, 1 == don't note, 2 == note
-uint8_t appOpsToNote[AppOpsManager::_NUM_OP] = {0};
-
 AppOpsManager::AppOpsManager()
 {
 }
 
-#if defined(__BRILLO__)
-// There is no AppOpsService on Brillo
-sp<IAppOpsService> AppOpsManager::getService() { return NULL; }
-#else
 sp<IAppOpsService> AppOpsManager::getService()
 {
+    static String16 _appops("appops");
 
     std::lock_guard<Mutex> scoped_lock(mLock);
     int64_t startTime = 0;
@@ -96,14 +73,13 @@
     }
     return service;
 }
-#endif  // defined(__BRILLO__)
 
 int32_t AppOpsManager::checkOp(int32_t op, int32_t uid, const String16& callingPackage)
 {
     sp<IAppOpsService> service = getService();
     return service != nullptr
             ? service->checkOperation(op, uid, callingPackage)
-            : APP_OPS_MANAGER_UNAVAILABLE_MODE;
+            : AppOpsManager::MODE_IGNORED;
 }
 
 int32_t AppOpsManager::checkAudioOpNoThrow(int32_t op, int32_t usage, int32_t uid,
@@ -111,7 +87,7 @@
     sp<IAppOpsService> service = getService();
     return service != nullptr
            ? service->checkAudioOperation(op, usage, uid, callingPackage)
-           : APP_OPS_MANAGER_UNAVAILABLE_MODE;
+           : AppOpsManager::MODE_IGNORED;
 }
 
 int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage) {
@@ -120,12 +96,12 @@
 }
 
 int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage,
-        const std::unique_ptr<String16>& featureId, const String16& message) {
+        const std::unique_ptr<String16>& attributionTag, const String16& message) {
     sp<IAppOpsService> service = getService();
     int32_t mode = service != nullptr
-            ? service->noteOperation(op, uid, callingPackage, featureId, shouldCollectNotes(op),
-                    message)
-            : APP_OPS_MANAGER_UNAVAILABLE_MODE;
+            ? service->noteOperation(op, uid, callingPackage, attributionTag,
+                    shouldCollectNotes(op), message)
+            : AppOpsManager::MODE_IGNORED;
 
     return mode;
 }
@@ -137,13 +113,13 @@
 }
 
 int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
-        bool startIfModeDefault, const std::unique_ptr<String16>& featureId,
+        bool startIfModeDefault, const std::unique_ptr<String16>& attributionTag,
         const String16& message) {
     sp<IAppOpsService> service = getService();
     int32_t mode = service != nullptr
             ? service->startOperation(getClientId(), op, uid, callingPackage,
-                    featureId, startIfModeDefault, shouldCollectNotes(op), message)
-            : APP_OPS_MANAGER_UNAVAILABLE_MODE;
+                    attributionTag, startIfModeDefault, shouldCollectNotes(op), message)
+            : AppOpsManager::MODE_IGNORED;
 
     return mode;
 }
@@ -153,10 +129,10 @@
 }
 
 void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage,
-        const std::unique_ptr<String16>& callingFeatureId) {
+        const std::unique_ptr<String16>& attributionTag) {
     sp<IAppOpsService> service = getService();
     if (service != nullptr) {
-        service->finishOperation(getClientId(), op, uid, callingPackage, callingFeatureId);
+        service->finishOperation(getClientId(), op, uid, callingPackage, attributionTag);
     }
 }
 
@@ -192,6 +168,9 @@
 
 // check it the appops needs to be collected and cache result
 bool AppOpsManager::shouldCollectNotes(int32_t opcode) {
+    // Whether an appop should be collected: 0 == not initialized, 1 == don't note, 2 == note
+    static uint8_t appOpsToNote[AppOpsManager::_NUM_OP] = {0};
+
     if (appOpsToNote[opcode] == 0) {
         if (getService()->shouldCollectNotes(opcode)) {
             appOpsToNote[opcode] = 2;
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index 2f6e9c3..e0fb543 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -141,7 +141,7 @@
 
 // ---------------------------------------------------------------------------
 
-BBinder::BBinder() : mExtras(nullptr)
+BBinder::BBinder() : mExtras(nullptr), mStability(0)
 {
 }
 
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index f16c39c..d2b9b8f 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -138,6 +138,7 @@
 
 BpBinder::BpBinder(int32_t handle, int32_t trackedUid)
     : mHandle(handle)
+    , mStability(0)
     , mAlive(1)
     , mObitsSent(0)
     , mObituaries(nullptr)
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
index 1eb5363..9e1249b 100644
--- a/libs/binder/IActivityManager.cpp
+++ b/libs/binder/IActivityManager.cpp
@@ -104,6 +104,18 @@
         }
         return reply.readInt32();
     }
+
+    virtual bool isUidActiveOrForeground(const uid_t uid, const String16& callingPackage)
+    {
+         Parcel data, reply;
+         data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
+         data.writeInt32(uid);
+         data.writeString16(callingPackage);
+         remote()->transact(IS_UID_ACTIVE_OR_FOREGROUND_TRANSACTION, data, &reply);
+         // fail on exception
+         if (reply.readExceptionCode() != 0) return false;
+         return reply.readInt32() == 1;
+    }
 };
 
 // ------------------------------------------------------------------------------------
diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp
index a5555a3..0714723 100644
--- a/libs/binder/IAppOpsService.cpp
+++ b/libs/binder/IAppOpsService.cpp
@@ -47,14 +47,14 @@
     }
 
     virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName,
-                const std::unique_ptr<String16>& featureId, bool shouldCollectAsyncNotedOp,
+                const std::unique_ptr<String16>& attributionTag, bool shouldCollectAsyncNotedOp,
                 const String16& message) {
         Parcel data, reply;
         data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
         data.writeInt32(code);
         data.writeInt32(uid);
         data.writeString16(packageName);
-        data.writeString16(featureId);
+        data.writeString16(attributionTag);
         data.writeInt32(shouldCollectAsyncNotedOp ? 1 : 0);
         data.writeString16(message);
         remote()->transact(NOTE_OPERATION_TRANSACTION, data, &reply);
@@ -64,7 +64,7 @@
     }
 
     virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
-                const String16& packageName, const std::unique_ptr<String16>& featureId,
+                const String16& packageName, const std::unique_ptr<String16>& attributionTag,
                 bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message) {
         Parcel data, reply;
         data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
@@ -72,7 +72,7 @@
         data.writeInt32(code);
         data.writeInt32(uid);
         data.writeString16(packageName);
-        data.writeString16(featureId);
+        data.writeString16(attributionTag);
         data.writeInt32(startIfModeDefault ? 1 : 0);
         data.writeInt32(shouldCollectAsyncNotedOp ? 1 : 0);
         data.writeString16(message);
@@ -83,14 +83,14 @@
     }
 
     virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
-            const String16& packageName, const std::unique_ptr<String16>& featureId) {
+            const String16& packageName, const std::unique_ptr<String16>& attributionTag) {
         Parcel data, reply;
         data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
         data.writeStrongBinder(token);
         data.writeInt32(code);
         data.writeInt32(uid);
         data.writeString16(packageName);
-        data.writeString16(featureId);
+        data.writeString16(attributionTag);
         remote()->transact(FINISH_OPERATION_TRANSACTION, data, &reply);
     }
 
@@ -182,11 +182,11 @@
             int32_t code = data.readInt32();
             int32_t uid = data.readInt32();
             String16 packageName = data.readString16();
-            std::unique_ptr<String16> featureId;
-            data.readString16(&featureId);
+            std::unique_ptr<String16> attributionTag;
+            data.readString16(&attributionTag);
             bool shouldCollectAsyncNotedOp = data.readInt32() == 1;
             String16 message = data.readString16();
-            int32_t res = noteOperation(code, uid, packageName, featureId,
+            int32_t res = noteOperation(code, uid, packageName, attributionTag,
                     shouldCollectAsyncNotedOp, message);
             reply->writeNoException();
             reply->writeInt32(res);
@@ -198,12 +198,12 @@
             int32_t code = data.readInt32();
             int32_t uid = data.readInt32();
             String16 packageName = data.readString16();
-            std::unique_ptr<String16> featureId;
-            data.readString16(&featureId);
+            std::unique_ptr<String16> attributionTag;
+            data.readString16(&attributionTag);
             bool startIfModeDefault = data.readInt32() == 1;
             bool shouldCollectAsyncNotedOp = data.readInt32() == 1;
             String16 message = data.readString16();
-            int32_t res = startOperation(token, code, uid, packageName, featureId,
+            int32_t res = startOperation(token, code, uid, packageName, attributionTag,
                     startIfModeDefault, shouldCollectAsyncNotedOp, message);
             reply->writeNoException();
             reply->writeInt32(res);
@@ -215,9 +215,9 @@
             int32_t code = data.readInt32();
             int32_t uid = data.readInt32();
             String16 packageName = data.readString16();
-            std::unique_ptr<String16> featureId;
-            data.readString16(&featureId);
-            finishOperation(token, code, uid, packageName, featureId);
+            std::unique_ptr<String16> attributionTag;
+            data.readString16(&attributionTag);
+            finishOperation(token, code, uid, packageName, attributionTag);
             reply->writeNoException();
             return NO_ERROR;
         } break;
diff --git a/libs/binder/Stability.cpp b/libs/binder/Stability.cpp
index 7ce5e36..e1565fa 100644
--- a/libs/binder/Stability.cpp
+++ b/libs/binder/Stability.cpp
@@ -15,6 +15,9 @@
  */
 #include <binder/Stability.h>
 
+#include <binder/BpBinder.h>
+#include <binder/Binder.h>
+
 namespace android {
 namespace internal {
 
@@ -78,11 +81,12 @@
 
     if (currentStability == stability) return OK;
 
-    binder->attachObject(
-        reinterpret_cast<void*>(&Stability::get),
-        reinterpret_cast<void*>(stability),
-        nullptr /*cleanupCookie*/,
-        nullptr /*cleanup function*/);
+    BBinder* local = binder->localBinder();
+    if (local != nullptr) {
+        local->mStability = static_cast<int32_t>(stability);
+    } else {
+        binder->remoteBinder()->mStability = static_cast<int32_t>(stability);
+    }
 
     return OK;
 }
@@ -90,8 +94,12 @@
 Stability::Level Stability::get(IBinder* binder) {
     if (binder == nullptr) return UNDECLARED;
 
-    return static_cast<Level>(reinterpret_cast<intptr_t>(
-        binder->findObject(reinterpret_cast<void*>(&Stability::get))));
+    BBinder* local = binder->localBinder();
+    if (local != nullptr) {
+        return static_cast<Stability::Level>(local->mStability);
+    }
+
+    return static_cast<Stability::Level>(binder->remoteBinder()->mStability);
 }
 
 bool Stability::check(int32_t provided, Level required) {
diff --git a/libs/binder/aidl/android/content/pm/IPackageChangeObserver.aidl b/libs/binder/aidl/android/content/pm/IPackageChangeObserver.aidl
new file mode 100644
index 0000000..6929a6c
--- /dev/null
+++ b/libs/binder/aidl/android/content/pm/IPackageChangeObserver.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 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.content.pm;
+
+import android.content.pm.PackageChangeEvent;
+
+/**
+ * This is a non-blocking notification when a package has changed.
+ *
+ * @hide
+ */
+oneway interface IPackageChangeObserver {
+  void onPackageChanged(in PackageChangeEvent event);
+}
diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
index 618f88c..dc8d74c 100644
--- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
+++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
@@ -17,6 +17,8 @@
 
 package android.content.pm;
 
+import android.content.pm.IPackageChangeObserver;
+
 /**
  * Parallel implementation of certain {@link PackageManager} APIs that need to
  * be exposed to native code.
@@ -90,4 +92,13 @@
 
     /* Returns the names of all packages. */
     @utf8InCpp String[] getAllPackages();
+
+    /** Register an extra package change observer to receive the multi-cast. */
+    void registerPackageChangeObserver(in IPackageChangeObserver observer);
+
+    /**
+     * Unregister an existing package change observer.
+     * This does nothing if this observer was not already registered.
+     */
+    void unregisterPackageChangeObserver(in IPackageChangeObserver observer);
 }
diff --git a/libs/binder/aidl/android/content/pm/PackageChangeEvent.aidl b/libs/binder/aidl/android/content/pm/PackageChangeEvent.aidl
new file mode 100644
index 0000000..e30e907
--- /dev/null
+++ b/libs/binder/aidl/android/content/pm/PackageChangeEvent.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 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.content.pm;
+
+/**
+ * This event is designed for notification to native code listener about
+ * any changes on a package including update, deletion and etc.
+ *
+ * @hide
+ */
+parcelable PackageChangeEvent {
+  @utf8InCpp String packageName;
+  long version;
+  long lastUpdateTimeMillis;
+  boolean newInstalled;
+  boolean dataRemoved;
+  boolean isDeleted;
+}
diff --git a/libs/binder/include/binder/ActivityManager.h b/libs/binder/include/binder/ActivityManager.h
index 9108e31..0bb6d28 100644
--- a/libs/binder/include/binder/ActivityManager.h
+++ b/libs/binder/include/binder/ActivityManager.h
@@ -46,25 +46,24 @@
         PROCESS_STATE_PERSISTENT = 0,
         PROCESS_STATE_PERSISTENT_UI = 1,
         PROCESS_STATE_TOP = 2,
-        PROCESS_STATE_FOREGROUND_SERVICE_LOCATION = 3,
-        PROCESS_STATE_BOUND_TOP = 4,
-        PROCESS_STATE_FOREGROUND_SERVICE = 5,
-        PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 6,
-        PROCESS_STATE_IMPORTANT_FOREGROUND = 7,
-        PROCESS_STATE_IMPORTANT_BACKGROUND = 8,
-        PROCESS_STATE_TRANSIENT_BACKGROUND = 9,
-        PROCESS_STATE_BACKUP = 10,
-        PROCESS_STATE_SERVICE = 11,
-        PROCESS_STATE_RECEIVER = 12,
-        PROCESS_STATE_TOP_SLEEPING = 13,
-        PROCESS_STATE_HEAVY_WEIGHT = 14,
-        PROCESS_STATE_HOME = 15,
-        PROCESS_STATE_LAST_ACTIVITY = 16,
-        PROCESS_STATE_CACHED_ACTIVITY = 17,
-        PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 18,
-        PROCESS_STATE_CACHED_RECENT = 19,
-        PROCESS_STATE_CACHED_EMPTY = 20,
-        PROCESS_STATE_NONEXISTENT = 21,
+        PROCESS_STATE_BOUND_TOP = 3,
+        PROCESS_STATE_FOREGROUND_SERVICE = 4,
+        PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 5,
+        PROCESS_STATE_IMPORTANT_FOREGROUND = 6,
+        PROCESS_STATE_IMPORTANT_BACKGROUND = 7,
+        PROCESS_STATE_TRANSIENT_BACKGROUND = 8,
+        PROCESS_STATE_BACKUP = 9,
+        PROCESS_STATE_SERVICE = 10,
+        PROCESS_STATE_RECEIVER = 11,
+        PROCESS_STATE_TOP_SLEEPING = 12,
+        PROCESS_STATE_HEAVY_WEIGHT = 13,
+        PROCESS_STATE_HOME = 14,
+        PROCESS_STATE_LAST_ACTIVITY = 15,
+        PROCESS_STATE_CACHED_ACTIVITY = 16,
+        PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 17,
+        PROCESS_STATE_CACHED_RECENT = 18,
+        PROCESS_STATE_CACHED_EMPTY = 19,
+        PROCESS_STATE_NONEXISTENT = 20,
     };
 
     ActivityManager();
@@ -77,6 +76,7 @@
     void unregisterUidObserver(const sp<IUidObserver>& observer);
     bool isUidActive(const uid_t uid, const String16& callingPackage);
     int getUidProcessState(const uid_t uid, const String16& callingPackage);
+    bool isUidActiveOrForeground(const uid_t uid, const String16& callingPackage);
 
 
   status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient);
diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h
index 5b6eb68..2ee5930 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -134,18 +134,18 @@
     //              const String16&) instead
     int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage);
     int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage,
-            const std::unique_ptr<String16>& featureId, const String16& message);
+            const std::unique_ptr<String16>& attributionTag, const String16& message);
     // @Deprecated, use startOpNoThrow(int32_t, int32_t, const String16&, bool, const String16&,
     //              const String16&) instead
     int32_t startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
             bool startIfModeDefault);
     int32_t startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
-            bool startIfModeDefault, const std::unique_ptr<String16>& featureId,
+            bool startIfModeDefault, const std::unique_ptr<String16>& attributionTag,
             const String16& message);
     // @Deprecated, use finishOp(int32_t, int32_t, const String16&, bool, const String16&) instead
     void finishOp(int32_t op, int32_t uid, const String16& callingPackage);
     void finishOp(int32_t op, int32_t uid, const String16& callingPackage,
-            const std::unique_ptr<String16>& featureId);
+            const std::unique_ptr<String16>& attributionTag);
     void startWatchingMode(int32_t op, const String16& packageName,
             const sp<IAppOpsCallback>& callback);
     void stopWatchingMode(const sp<IAppOpsCallback>& callback);
diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h
index 3be61f9..74e52db 100644
--- a/libs/binder/include/binder/Binder.h
+++ b/libs/binder/include/binder/Binder.h
@@ -24,6 +24,10 @@
 // ---------------------------------------------------------------------------
 namespace android {
 
+namespace internal {
+class Stability;
+}
+
 class BBinder : public IBinder
 {
 public:
@@ -88,7 +92,12 @@
     Extras*             getOrCreateExtras();
 
     std::atomic<Extras*> mExtras;
-            void*       mReserved0;
+
+    friend ::android::internal::Stability;
+    union {
+        int32_t mStability;
+        void* mReserved0;
+    };
 };
 
 // ---------------------------------------------------------------------------
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index 7dca733..8e871b8 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -27,6 +27,10 @@
 // ---------------------------------------------------------------------------
 namespace android {
 
+namespace internal {
+class Stability;
+};
+
 using binder_proxy_limit_callback = void(*)(int);
 
 class BpBinder : public IBinder
@@ -116,6 +120,9 @@
 private:
     const   int32_t             mHandle;
 
+    friend ::android::internal::Stability;
+            int32_t             mStability;
+
     struct Obituary {
         wp<DeathRecipient> recipient;
         void* cookie;
diff --git a/libs/binder/include/binder/IActivityManager.h b/libs/binder/include/binder/IActivityManager.h
index e0248f6..1815ecc 100644
--- a/libs/binder/include/binder/IActivityManager.h
+++ b/libs/binder/include/binder/IActivityManager.h
@@ -39,13 +39,15 @@
     virtual void unregisterUidObserver(const sp<IUidObserver>& observer) = 0;
     virtual bool isUidActive(const uid_t uid, const String16& callingPackage) = 0;
     virtual int32_t getUidProcessState(const uid_t uid, const String16& callingPackage) = 0;
+    virtual bool isUidActiveOrForeground(const uid_t uid, const String16& callingPackage) = 0;
 
     enum {
         OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
         REGISTER_UID_OBSERVER_TRANSACTION,
         UNREGISTER_UID_OBSERVER_TRANSACTION,
         IS_UID_ACTIVE_TRANSACTION,
-        GET_UID_PROCESS_STATE_TRANSACTION
+        GET_UID_PROCESS_STATE_TRANSACTION,
+        IS_UID_ACTIVE_OR_FOREGROUND_TRANSACTION,
     };
 };
 
diff --git a/libs/binder/include/binder/IAppOpsService.h b/libs/binder/include/binder/IAppOpsService.h
index 1b4bcce..1ffb8de 100644
--- a/libs/binder/include/binder/IAppOpsService.h
+++ b/libs/binder/include/binder/IAppOpsService.h
@@ -36,13 +36,13 @@
 
     virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
     virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName,
-            const std::unique_ptr<String16>& featureId, bool shouldCollectAsyncNotedOp,
+            const std::unique_ptr<String16>& attributionTag, bool shouldCollectAsyncNotedOp,
             const String16& message) = 0;
     virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
-            const String16& packageName, const std::unique_ptr<String16>& featureId,
+            const String16& packageName, const std::unique_ptr<String16>& attributionTag,
             bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message) = 0;
     virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
-            const String16& packageName, const std::unique_ptr<String16>& featureId) = 0;
+            const String16& packageName, const std::unique_ptr<String16>& attributionTag) = 0;
     virtual void startWatchingMode(int32_t op, const String16& packageName,
             const sp<IAppOpsCallback>& callback) = 0;
     virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) = 0;
diff --git a/libs/binder/include/binder/Nullable.h b/libs/binder/include/binder/Nullable.h
new file mode 100644
index 0000000..b605bd3
--- /dev/null
+++ b/libs/binder/include/binder/Nullable.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <memory>
+#include <utility>
+
+namespace android {
+
+namespace aidl {
+
+// nullable/make_nullable provide source-level compatibility between std::opional and std::unique_ptr
+// usage:
+//     nullable<Foo> a;
+//     nullable<Foo> b = make_nullable<Foo>(...);
+//     auto c = make_nullable<Foo>(...);
+//     c.reset();
+//     c = make_nullable<Foo>(...);
+//     c = std::move(a);
+
+template <typename T>
+using nullable = std::unique_ptr<T>;
+
+template <typename T, typename... Args>
+inline nullable<T> make_nullable(Args&&... args) {
+    return std::make_unique<T>(std::forward<Args>(args)...);
+}
+
+} // namespace aidl
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 75dcdc8..649faa1 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -486,7 +486,6 @@
 
 void AIBinder_incStrong(AIBinder* binder) {
     if (binder == nullptr) {
-        LOG(ERROR) << __func__ << ": on null binder";
         return;
     }
 
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 1b42b69..b2b74dc 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -57,6 +57,7 @@
         "android.hardware.audio@5.0::IDevicesFactory",
         "android.hardware.audio@6.0::IDevicesFactory",
         "android.hardware.biometrics.face@1.0::IBiometricsFace",
+        "android.hardware.biometrics.fingerprint@2.1::IBiometricsFingerprint",
         "android.hardware.bluetooth@1.0::IBluetoothHci",
         "android.hardware.camera.provider@2.4::ICameraProvider",
         "android.hardware.drm@1.0::IDrmFactory",
@@ -74,6 +75,7 @@
         "android.hardware.automotive.audiocontrol@1.0::IAudioControl",
         "android.hardware.automotive.vehicle@2.0::IVehicle",
         "android.hardware.automotive.evs@1.0::IEvsCamera",
+        "android.hardware.neuralnetworks@1.0::IDevice",
         NULL,
 };
 
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index befabee..4809c1f 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -61,26 +61,25 @@
     VNDKSP = 1,
 };
 
-static constexpr const char* kNativeLibrariesSystemConfigPath[] = {"/etc/llndk.libraries.txt",
-                                                                   "/etc/vndksp.libraries.txt"};
+static constexpr const char* kNativeLibrariesSystemConfigPath[] =
+        {"/apex/com.android.vndk.v{}/etc/llndk.libraries.{}.txt",
+         "/apex/com.android.vndk.v{}/etc/vndksp.libraries.{}.txt"};
 
 static std::string vndkVersionStr() {
 #ifdef __BIONIC__
-    std::string version = android::base::GetProperty("ro.vndk.version", "");
-    if (version != "" && version != "current") {
-        return "." + version;
-    }
+    return android::base::GetProperty("ro.vndk.version", "");
 #endif
     return "";
 }
 
 static void insertVndkVersionStr(std::string* fileName) {
     LOG_ALWAYS_FATAL_IF(!fileName, "fileName should never be nullptr");
-    size_t insertPos = fileName->find_last_of(".");
-    if (insertPos == std::string::npos) {
-        insertPos = fileName->length();
+    std::string version = vndkVersionStr();
+    size_t pos = fileName->find("{}");
+    while (pos != std::string::npos) {
+        fileName->replace(pos, 2, version);
+        pos = fileName->find("{}", pos + version.size());
     }
-    fileName->insert(insertPos, vndkVersionStr());
 }
 
 static bool readConfig(const std::string& configFile, std::vector<std::string>* soNames) {
@@ -103,11 +102,7 @@
 }
 
 static const std::string getSystemNativeLibraries(NativeLibrary type) {
-    static const char* androidRootEnv = getenv("ANDROID_ROOT");
-    static const std::string rootDir = androidRootEnv != nullptr ? androidRootEnv : "/system";
-
-    std::string nativeLibrariesSystemConfig = rootDir + kNativeLibrariesSystemConfigPath[type];
-
+    std::string nativeLibrariesSystemConfig = kNativeLibrariesSystemConfigPath[type];
     insertVndkVersionStr(&nativeLibrariesSystemConfig);
 
     std::vector<std::string> soNames;
diff --git a/libs/gui/BufferHubConsumer.cpp b/libs/gui/BufferHubConsumer.cpp
index 0ddb87e..b5cdeb2 100644
--- a/libs/gui/BufferHubConsumer.cpp
+++ b/libs/gui/BufferHubConsumer.cpp
@@ -147,16 +147,6 @@
     return INVALID_OPERATION;
 }
 
-status_t BufferHubConsumer::setFrameRate(float /*frameRate*/) {
-    ALOGE("BufferHubConsumer::setFrameRate: not implemented.");
-    return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::getFrameRate(float* /*frameRate*/) const {
-    ALOGE("BufferHubConsumer::getFrameRate: not implemented.");
-    return INVALID_OPERATION;
-}
-
 status_t BufferHubConsumer::dumpState(const String8& /*prefix*/, String8* /*outResult*/) const {
     ALOGE("BufferHubConsumer::dumpState: not implemented.");
     return INVALID_OPERATION;
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 4435265..da6143c 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -783,18 +783,6 @@
     return NO_ERROR;
 }
 
-status_t BufferQueueConsumer::setFrameRate(float frameRate) {
-    std::lock_guard<std::mutex> lock(mCore->mMutex);
-    mCore->mFrameRate = frameRate;
-    return NO_ERROR;
-}
-
-status_t BufferQueueConsumer::getFrameRate(float* frameRate) const {
-    std::lock_guard<std::mutex> lock(mCore->mMutex);
-    *frameRate = mCore->mFrameRate;
-    return NO_ERROR;
-}
-
 status_t BufferQueueConsumer::dumpState(const String8& prefix, String8* outResult) const {
     struct passwd* pwd = getpwnam("shell");
     uid_t shellUid = pwd ? pwd->pw_uid : 0;
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index 9e5d681..a1803d8 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -130,8 +130,7 @@
         mLastQueuedSlot(INVALID_BUFFER_SLOT),
         mUniqueId(getUniqueId()),
         mAutoPrerotation(false),
-        mTransformHintInUse(0),
-        mFrameRate(0) {
+        mTransformHintInUse(0) {
     int numStartingBuffers = getMaxBufferCountLocked();
     for (int s = 0; s < numStartingBuffers; s++) {
         mFreeSlots.insert(s);
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 9e86838..3f4c5da 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -1676,14 +1676,4 @@
     return NO_ERROR;
 }
 
-status_t BufferQueueProducer::setFrameRate(float frameRate) {
-    ATRACE_CALL();
-    BQ_LOGV("setFrameRate: %.0f", frameRate);
-
-    std::lock_guard<std::mutex> lock(mCore->mMutex);
-
-    mCore->mFrameRate = frameRate;
-    return NO_ERROR;
-}
-
 } // namespace android
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index 515f45c..9f91d9d 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -363,24 +363,6 @@
     return OK;
 }
 
-status_t ConsumerBase::setFrameRate(float frameRate) {
-    Mutex::Autolock _l(mMutex);
-    if (mAbandoned) {
-        CB_LOGE("setFrameRate: ConsumerBase is abandoned!");
-        return NO_INIT;
-    }
-    return mConsumer->setFrameRate(frameRate);
-}
-
-status_t ConsumerBase::getFrameRate(float* frameRate) {
-    Mutex::Autolock _l(mMutex);
-    if (mAbandoned) {
-        CB_LOGE("getFrameRate: ConsumerBase is abandoned!");
-        return NO_INIT;
-    }
-    return mConsumer->getFrameRate(frameRate);
-}
-
 void ConsumerBase::dumpState(String8& result) const {
     dumpState(result, "");
 }
diff --git a/libs/gui/IGraphicBufferConsumer.cpp b/libs/gui/IGraphicBufferConsumer.cpp
index 2521a7c..c705d39 100644
--- a/libs/gui/IGraphicBufferConsumer.cpp
+++ b/libs/gui/IGraphicBufferConsumer.cpp
@@ -51,8 +51,6 @@
     GET_SIDEBAND_STREAM,
     GET_OCCUPANCY_HISTORY,
     DISCARD_FREE_BUFFERS,
-    SET_FRAME_RATE,
-    GET_FRAME_RATE,
     DUMP_STATE,
     LAST = DUMP_STATE,
 };
@@ -165,16 +163,6 @@
                 Tag::DISCARD_FREE_BUFFERS);
     }
 
-    status_t setFrameRate(float frameRate) override {
-        using Signature = decltype(&IGraphicBufferConsumer::setFrameRate);
-        return callRemote<Signature>(Tag::SET_FRAME_RATE, frameRate);
-    }
-
-    status_t getFrameRate(float* frameRate) const override {
-        using Signature = decltype(&IGraphicBufferConsumer::getFrameRate);
-        return callRemote<Signature>(Tag::GET_FRAME_RATE, frameRate);
-    }
-
     status_t dumpState(const String8& prefix, String8* outResult) const override {
         using Signature = status_t (IGraphicBufferConsumer::*)(const String8&, String8*) const;
         return callRemote<Signature>(Tag::DUMP_STATE, prefix, outResult);
@@ -232,10 +220,6 @@
             return callLocal(data, reply, &IGraphicBufferConsumer::getOccupancyHistory);
         case Tag::DISCARD_FREE_BUFFERS:
             return callLocal(data, reply, &IGraphicBufferConsumer::discardFreeBuffers);
-        case Tag::SET_FRAME_RATE:
-            return callLocal(data, reply, &IGraphicBufferConsumer::setFrameRate);
-        case Tag::GET_FRAME_RATE:
-            return callLocal(data, reply, &IGraphicBufferConsumer::getFrameRate);
         case Tag::DUMP_STATE: {
             using Signature = status_t (IGraphicBufferConsumer::*)(const String8&, String8*) const;
             return callLocal<Signature>(data, reply, &IGraphicBufferConsumer::dumpState);
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 7b5596e..ad00939 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -74,7 +74,6 @@
     GET_CONSUMER_USAGE,
     SET_LEGACY_BUFFER_DROP,
     SET_AUTO_PREROTATION,
-    SET_FRAME_RATE,
 };
 
 class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@@ -560,14 +559,6 @@
         }
         return result;
     }
-
-    virtual status_t setFrameRate(float frameRate) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
-        data.writeFloat(frameRate);
-        status_t result = remote()->transact(SET_FRAME_RATE, data, &reply, IBinder::FLAG_ONEWAY);
-        return result;
-    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -700,8 +691,6 @@
     status_t setAutoPrerotation(bool autoPrerotation) override {
         return mBase->setAutoPrerotation(autoPrerotation);
     }
-
-    status_t setFrameRate(float frameRate) override { return mBase->setFrameRate(frameRate); }
 };
 
 IMPLEMENT_HYBRID_META_INTERFACE(GraphicBufferProducer,
@@ -721,12 +710,6 @@
     return INVALID_OPERATION;
 }
 
-status_t IGraphicBufferProducer::setFrameRate(float frameRate) {
-    // No-op for IGBP other than BufferQueue.
-    (void)frameRate;
-    return INVALID_OPERATION;
-}
-
 status_t IGraphicBufferProducer::exportToParcel(Parcel* parcel) {
     status_t res = OK;
     res = parcel->writeUint32(USE_BUFFER_QUEUE);
@@ -1096,13 +1079,6 @@
             reply->writeInt32(result);
             return NO_ERROR;
         }
-        case SET_FRAME_RATE: {
-            CHECK_INTERFACE(IGraphicBuffer, data, reply);
-            float frameRate = data.readFloat();
-            status_t result = setFrameRate(frameRate);
-            reply->writeInt32(result);
-            return NO_ERROR;
-        }
     }
     return BBinder::onTransact(code, data, reply, flags);
 }
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index ce41eab..8d79cf8 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -1140,8 +1140,7 @@
             return err;
         }
 
-        err = remote()->transact(BnSurfaceComposer::SET_FRAME_RATE, data, &reply,
-                                 IBinder::FLAG_ONEWAY);
+        err = remote()->transact(BnSurfaceComposer::SET_FRAME_RATE, data, &reply);
         if (err != NO_ERROR) {
             ALOGE("setFrameRate: failed to transact: %s (%d)", strerror(-err), err);
             return err;
@@ -1281,6 +1280,9 @@
 
             std::unordered_set<sp<IBinder>, SpHash<IBinder>> excludeHandles;
             int numExcludeHandles = data.readInt32();
+            if (numExcludeHandles >= static_cast<int>(MAX_LAYERS)) {
+                return BAD_VALUE;
+            }
             excludeHandles.reserve(numExcludeHandles);
             for (int i = 0; i < numExcludeHandles; i++) {
                 excludeHandles.emplace(data.readStrongBinder());
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index a9c9b74..f7158d0 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -443,39 +443,18 @@
 // ------------------------------- InputWindowCommands ----------------------------------------
 
 void InputWindowCommands::merge(const InputWindowCommands& other) {
-    transferTouchFocusCommands
-            .insert(transferTouchFocusCommands.end(),
-                    std::make_move_iterator(other.transferTouchFocusCommands.begin()),
-                    std::make_move_iterator(other.transferTouchFocusCommands.end()));
-
     syncInputWindows |= other.syncInputWindows;
 }
 
 void InputWindowCommands::clear() {
-    transferTouchFocusCommands.clear();
     syncInputWindows = false;
 }
 
 void InputWindowCommands::write(Parcel& output) const {
-    output.writeUint32(static_cast<uint32_t>(transferTouchFocusCommands.size()));
-    for (const auto& transferTouchFocusCommand : transferTouchFocusCommands) {
-        output.writeStrongBinder(transferTouchFocusCommand.fromToken);
-        output.writeStrongBinder(transferTouchFocusCommand.toToken);
-    }
-
     output.writeBool(syncInputWindows);
 }
 
 void InputWindowCommands::read(const Parcel& input) {
-    size_t count = input.readUint32();
-    transferTouchFocusCommands.clear();
-    for (size_t i = 0; i < count; i++) {
-        TransferTouchFocusCommand transferTouchFocusCommand;
-        transferTouchFocusCommand.fromToken = input.readStrongBinder();
-        transferTouchFocusCommand.toToken = input.readStrongBinder();
-        transferTouchFocusCommands.emplace_back(transferTouchFocusCommand);
-    }
-
     syncInputWindows = input.readBool();
 }
 
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index dc4860a..2307fbf 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1290,15 +1290,6 @@
     return *this;
 }
 
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::transferTouchFocus(
-        const sp<IBinder>& fromToken, const sp<IBinder>& toToken) {
-    InputWindowCommands::TransferTouchFocusCommand transferTouchFocusCommand;
-    transferTouchFocusCommand.fromToken = fromToken;
-    transferTouchFocusCommand.toToken = toToken;
-    mInputWindowCommands.transferTouchFocusCommands.emplace_back(transferTouchFocusCommand);
-    return *this;
-}
-
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::syncInputWindows() {
     mInputWindowCommands.syncInputWindows = true;
     return *this;
diff --git a/libs/gui/include/gui/BufferHubConsumer.h b/libs/gui/include/gui/BufferHubConsumer.h
index d756203..d380770 100644
--- a/libs/gui/include/gui/BufferHubConsumer.h
+++ b/libs/gui/include/gui/BufferHubConsumer.h
@@ -93,12 +93,6 @@
     // See |IGraphicBufferConsumer::discardFreeBuffers|
     status_t discardFreeBuffers() override;
 
-    // See |IGraphicBufferConsumer::setFrameRate|
-    status_t setFrameRate(float frameRate) override;
-
-    // See |IGraphicBufferConsumer::getFrameRate|
-    status_t getFrameRate(float* frameRate) const override;
-
     // See |IGraphicBufferConsumer::dumpState|
     status_t dumpState(const String8& prefix, String8* outResult) const override;
 
diff --git a/libs/gui/include/gui/BufferQueueConsumer.h b/libs/gui/include/gui/BufferQueueConsumer.h
index e9f0449..7db69ec 100644
--- a/libs/gui/include/gui/BufferQueueConsumer.h
+++ b/libs/gui/include/gui/BufferQueueConsumer.h
@@ -149,12 +149,6 @@
     // See IGraphicBufferConsumer::discardFreeBuffers
     virtual status_t discardFreeBuffers() override;
 
-    // See IGraphicBufferConsumer::setFrameRate.
-    virtual status_t setFrameRate(float frameRate) override;
-
-    // See IGraphicBufferConsumer::getFrameRate.
-    virtual status_t getFrameRate(float* frameRate) const override;
-
     // dump our state in a String
     status_t dumpState(const String8& prefix, String8* outResult) const override;
 
diff --git a/libs/gui/include/gui/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h
index 05c2074..557c28b 100644
--- a/libs/gui/include/gui/BufferQueueCore.h
+++ b/libs/gui/include/gui/BufferQueueCore.h
@@ -354,9 +354,6 @@
     // mTransformHintInUse is to cache the mTransformHint used by the producer.
     uint32_t mTransformHintInUse;
 
-    // The frame rate the app intends to run at.
-    float mFrameRate;
-
 }; // class BufferQueueCore
 
 } // namespace android
diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h
index cbace5b..a7f7d1d 100644
--- a/libs/gui/include/gui/BufferQueueProducer.h
+++ b/libs/gui/include/gui/BufferQueueProducer.h
@@ -198,9 +198,6 @@
     // See IGraphicBufferProducer::setAutoPrerotation
     virtual status_t setAutoPrerotation(bool autoPrerotation);
 
-    // See IGraphicBufferProducer::setFrameRate
-    virtual status_t setFrameRate(float frameRate) override;
-
 private:
     // This is required by the IBinder::DeathRecipient interface
     virtual void binderDied(const wp<IBinder>& who);
diff --git a/libs/gui/include/gui/ConsumerBase.h b/libs/gui/include/gui/ConsumerBase.h
index cfed9aa..8ff0cd0 100644
--- a/libs/gui/include/gui/ConsumerBase.h
+++ b/libs/gui/include/gui/ConsumerBase.h
@@ -111,12 +111,6 @@
     // See IGraphicBufferConsumer::discardFreeBuffers
     status_t discardFreeBuffers();
 
-    // See IGraphicBufferConsumer::setFrameRate
-    status_t setFrameRate(float frameRate);
-
-    // See IGraphicBufferConsumer::getFrameRate
-    status_t getFrameRate(float* frameRate);
-
 private:
     ConsumerBase(const ConsumerBase&);
     void operator=(const ConsumerBase&);
diff --git a/libs/gui/include/gui/IGraphicBufferConsumer.h b/libs/gui/include/gui/IGraphicBufferConsumer.h
index 56fe949..0b92e7d 100644
--- a/libs/gui/include/gui/IGraphicBufferConsumer.h
+++ b/libs/gui/include/gui/IGraphicBufferConsumer.h
@@ -275,16 +275,6 @@
     // call to free up any of its locally cached buffers.
     virtual status_t discardFreeBuffers() = 0;
 
-    // Set the frame rate the producer will run at.
-    //
-    // Return of a value other than NO_ERROR means an unknown error has occurred.
-    virtual status_t setFrameRate(float frameRate) = 0;
-
-    // Get the frame rate the producer will run at.
-    //
-    // Return of a value other than NO_ERROR means an unknown error has occurred.
-    virtual status_t getFrameRate(float* frameRate) const = 0;
-
     // dump state into a string
     virtual status_t dumpState(const String8& prefix, String8* outResult) const = 0;
 
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index 87989da..d7f3492 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -639,9 +639,6 @@
     // the width and height used for dequeueBuffer will be additionally swapped.
     virtual status_t setAutoPrerotation(bool autoPrerotation);
 
-    // Sets the apps intended frame rate.
-    virtual status_t setFrameRate(float frameRate);
-
 #ifndef NO_BINDER
     // Static method exports any IGraphicBufferProducer object to a parcel. It
     // handles null producer as well.
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 0659f0d..09487ea 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -76,6 +76,8 @@
 public:
     DECLARE_META_INTERFACE(SurfaceComposer)
 
+    static constexpr size_t MAX_LAYERS = 4096;
+
     // flags for setTransactionState()
     enum {
         eSynchronous = 0x01,
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 7e3d5d5..2b2f773 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -271,12 +271,6 @@
 };
 
 struct InputWindowCommands {
-    struct TransferTouchFocusCommand {
-        sp<IBinder> fromToken;
-        sp<IBinder> toToken;
-    };
-
-    std::vector<TransferTouchFocusCommand> transferTouchFocusCommands;
     bool syncInputWindows{false};
 
     void merge(const InputWindowCommands& other);
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index ad7cbfe..917c0d4 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -178,7 +178,6 @@
     status_t getUniqueId(uint64_t* outId) const;
     status_t getConsumerUsage(uint64_t* outUsage) const;
 
-    // See IGraphicBufferProducer::setFrameRate
     status_t setFrameRate(float frameRate, int8_t compatibility);
 
 protected:
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 0cf141d..2fb9538 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -507,7 +507,6 @@
 
 #ifndef NO_INPUT
         Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, const InputWindowInfo& info);
-        Transaction& transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken);
         Transaction& syncInputWindows();
 #endif
 
diff --git a/libs/gui/include/gui/mock/GraphicBufferConsumer.h b/libs/gui/include/gui/mock/GraphicBufferConsumer.h
index e940cf3..98f24c2 100644
--- a/libs/gui/include/gui/mock/GraphicBufferConsumer.h
+++ b/libs/gui/include/gui/mock/GraphicBufferConsumer.h
@@ -49,8 +49,6 @@
     MOCK_CONST_METHOD1(getSidebandStream, status_t(sp<NativeHandle>*));
     MOCK_METHOD2(getOccupancyHistory, status_t(bool, std::vector<OccupancyTracker::Segment>*));
     MOCK_METHOD0(discardFreeBuffers, status_t());
-    MOCK_METHOD1(setFrameRate, status_t(float));
-    MOCK_CONST_METHOD1(getFrameRate, status_t(float*));
     MOCK_CONST_METHOD2(dumpState, status_t(const String8&, String8*));
 };
 
diff --git a/libs/gui/tests/AndroidTest.xml b/libs/gui/tests/AndroidTest.xml
index c02e020..5e09fff 100644
--- a/libs/gui/tests/AndroidTest.xml
+++ b/libs/gui/tests/AndroidTest.xml
@@ -18,6 +18,10 @@
         <option name="cleanup" value="true" />
         <option name="push" value="libgui_test->/data/local/tmp/libgui_test" />
     </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+      <option name="force-skip-system-props" value="true" /> <!-- avoid restarting device -->
+      <option name="screen-always-on" value="on" />
+    </target_preparer>
     <option name="test-suite-tag" value="apct" />
     <test class="com.android.tradefed.testtype.GTest" >
         <option name="native-test-device-path" value="/data/local/tmp" />
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 7335b30..ef7cc7d 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -1130,6 +1130,16 @@
     return !mBatches.isEmpty();
 }
 
+int32_t InputConsumer::getPendingBatchSource() const {
+    if (mBatches.isEmpty()) {
+        return AINPUT_SOURCE_CLASS_NONE;
+    }
+
+    const Batch& batch = mBatches.itemAt(0);
+    const InputMessage& head = batch.samples.itemAt(0);
+    return head.body.motion.source;
+}
+
 ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const {
     for (size_t i = 0; i < mBatches.size(); i++) {
         const Batch& batch = mBatches.itemAt(i);
diff --git a/libs/input/TouchVideoFrame.cpp b/libs/input/TouchVideoFrame.cpp
index 145b4ae..c62e098 100644
--- a/libs/input/TouchVideoFrame.cpp
+++ b/libs/input/TouchVideoFrame.cpp
@@ -43,13 +43,13 @@
 void TouchVideoFrame::rotate(int32_t orientation) {
     switch (orientation) {
         case DISPLAY_ORIENTATION_90:
-            rotateQuarterTurn(true /*clockwise*/);
+            rotateQuarterTurn(false /*clockwise*/);
             break;
         case DISPLAY_ORIENTATION_180:
             rotate180();
             break;
         case DISPLAY_ORIENTATION_270:
-            rotateQuarterTurn(false /*clockwise*/);
+            rotateQuarterTurn(true /*clockwise*/);
             break;
     }
 }
diff --git a/libs/input/tests/TouchVideoFrame_test.cpp b/libs/input/tests/TouchVideoFrame_test.cpp
index 1ec9358..654b236 100644
--- a/libs/input/tests/TouchVideoFrame_test.cpp
+++ b/libs/input/tests/TouchVideoFrame_test.cpp
@@ -86,14 +86,14 @@
 
 TEST(TouchVideoFrame, Rotate90_2x2) {
     TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP);
-    TouchVideoFrame frameRotated(2, 2, {3, 1, 4, 2}, TIMESTAMP);
+    TouchVideoFrame frameRotated(2, 2, {2, 4, 1, 3}, TIMESTAMP);
     frame.rotate(DISPLAY_ORIENTATION_90);
     ASSERT_EQ(frame, frameRotated);
 }
 
 TEST(TouchVideoFrame, Rotate90_3x2) {
     TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
-    TouchVideoFrame frameRotated(2, 3, {5, 3, 1, 6, 4, 2}, TIMESTAMP);
+    TouchVideoFrame frameRotated(2, 3, {2, 4, 6, 1, 3, 5}, TIMESTAMP);
     frame.rotate(DISPLAY_ORIENTATION_90);
     ASSERT_EQ(frame, frameRotated);
 }
@@ -171,14 +171,14 @@
 
 TEST(TouchVideoFrame, Rotate270_2x2) {
     TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP);
-    TouchVideoFrame frameRotated(2, 2, {2, 4, 1, 3}, TIMESTAMP);
+    TouchVideoFrame frameRotated(2, 2, {3, 1, 4, 2}, TIMESTAMP);
     frame.rotate(DISPLAY_ORIENTATION_270);
     ASSERT_EQ(frame, frameRotated);
 }
 
 TEST(TouchVideoFrame, Rotate270_3x2) {
     TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
-    TouchVideoFrame frameRotated(2, 3, {2, 4, 6, 1, 3, 5}, TIMESTAMP);
+    TouchVideoFrame frameRotated(2, 3, {5, 3, 1, 6, 4, 2}, TIMESTAMP);
     frame.rotate(DISPLAY_ORIENTATION_270);
     ASSERT_EQ(frame, frameRotated);
 }
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 0f7e2fb..0ff33ac 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -185,9 +185,18 @@
 }
 
 void Choreographer::scheduleCallbacks() {
-    AutoMutex _{mLock};
-    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-    if (mFrameCallbacks.top().dueTime <= now) {
+    const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+    nsecs_t dueTime;
+    {
+        AutoMutex _{mLock};
+        // If there are no pending callbacks then don't schedule a vsync
+        if (mFrameCallbacks.empty()) {
+            return;
+        }
+        dueTime = mFrameCallbacks.top().dueTime;
+    }
+
+    if (dueTime <= now) {
         ALOGV("choreographer %p ~ scheduling vsync", this);
         scheduleVsync();
         return;
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 3d77059..eb6080f 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -55,12 +55,11 @@
         "gl/GLShadowTexture.cpp",
         "gl/GLShadowVertexGenerator.cpp",
         "gl/GLSkiaShadowPort.cpp",
+        "gl/GLVertexBuffer.cpp",
         "gl/ImageManager.cpp",
         "gl/Program.cpp",
         "gl/ProgramCache.cpp",
         "gl/filters/BlurFilter.cpp",
-        "gl/filters/KawaseBlurFilter.cpp",
-        "gl/filters/GaussianBlurFilter.cpp",
         "gl/filters/GenericProgram.cpp",
     ],
 }
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index e11b59f..e73f245 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -50,8 +50,6 @@
 #include "Program.h"
 #include "ProgramCache.h"
 #include "filters/BlurFilter.h"
-#include "filters/GaussianBlurFilter.h"
-#include "filters/KawaseBlurFilter.h"
 
 extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
 
@@ -383,15 +381,6 @@
         LOG_ALWAYS_FATAL_IF(!success, "can't make default context current");
     }
 
-    const uint16_t protTexData[] = {0};
-    glGenTextures(1, &mProtectedTexName);
-    glBindTexture(GL_TEXTURE_2D, mProtectedTexName);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
-    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, protTexData);
-
     // mColorBlindnessCorrection = M;
 
     if (mUseColorManagement) {
@@ -430,17 +419,12 @@
     }
 
     if (args.supportsBackgroundBlur) {
-        char isGaussian[PROPERTY_VALUE_MAX];
-        property_get("debug.sf.gaussianBlur", isGaussian, "0");
-        if (atoi(isGaussian)) {
-            mBlurFilter = new GaussianBlurFilter(*this);
-        } else {
-            mBlurFilter = new KawaseBlurFilter(*this);
-        }
+        mBlurFilter = new BlurFilter(*this);
         checkErrors("BlurFilter creation");
     }
 
     mImageManager = std::make_unique<ImageManager>(this);
+    mImageManager->initThread();
     mDrawingBuffer = createFramebuffer();
 }
 
@@ -973,9 +957,13 @@
         return NO_ERROR;
     }
 
-    if (bufferFence.get() >= 0 && !waitFence(std::move(bufferFence))) {
-        ATRACE_NAME("Waiting before draw");
-        sync_wait(bufferFence.get(), -1);
+    if (bufferFence.get() >= 0) {
+        // Duplicate the fence for passing to waitFence.
+        base::unique_fd bufferFenceDup(dup(bufferFence.get()));
+        if (bufferFenceDup < 0 || !waitFence(std::move(bufferFenceDup))) {
+            ATRACE_NAME("Waiting before draw");
+            sync_wait(bufferFence.get(), -1);
+        }
     }
 
     if (buffer == nullptr) {
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index ebf78fe..32dbad1 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -178,7 +178,6 @@
     EGLSurface mDummySurface;
     EGLContext mProtectedEGLContext;
     EGLSurface mProtectedDummySurface;
-    GLuint mProtectedTexName;
     GLint mMaxViewportDims[2];
     GLint mMaxTextureSize;
     GLuint mVpWidth;
@@ -261,8 +260,6 @@
     friend class ImageManager;
     friend class GLFramebuffer;
     friend class BlurFilter;
-    friend class GaussianBlurFilter;
-    friend class KawaseBlurFilter;
     friend class GenericProgram;
     std::unique_ptr<FlushTracer> mFlushTracer;
     std::unique_ptr<ImageManager> mImageManager = std::make_unique<ImageManager>(this);
diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp
index 153935b..cb0d5cf 100644
--- a/libs/renderengine/gl/GLFramebuffer.cpp
+++ b/libs/renderengine/gl/GLFramebuffer.cpp
@@ -32,23 +32,14 @@
 namespace gl {
 
 GLFramebuffer::GLFramebuffer(GLESRenderEngine& engine)
-      : GLFramebuffer(engine, false /* multiTarget */) {}
-
-GLFramebuffer::GLFramebuffer(GLESRenderEngine& engine, bool multiTarget)
       : mEngine(engine), mEGLDisplay(engine.getEGLDisplay()), mEGLImage(EGL_NO_IMAGE_KHR) {
     glGenTextures(1, &mTextureName);
-    if (multiTarget) {
-        glGenTextures(1, &mSecondaryTextureName);
-    }
     glGenFramebuffers(1, &mFramebufferName);
 }
 
 GLFramebuffer::~GLFramebuffer() {
     glDeleteFramebuffers(1, &mFramebufferName);
     glDeleteTextures(1, &mTextureName);
-    if (mSecondaryTextureName != -1) {
-        glDeleteTextures(1, &mSecondaryTextureName);
-    }
 }
 
 bool GLFramebuffer::setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected,
@@ -87,28 +78,12 @@
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
 
-    const bool multiTarget = mSecondaryTextureName != -1;
-    if (multiTarget) {
-        glBindTexture(GL_TEXTURE_2D, mSecondaryTextureName);
-        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
-    }
-
     mBufferHeight = height;
     mBufferWidth = width;
     mEngine.checkErrors("Allocating Fbo texture");
 
     bind();
     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextureName, 0);
-    if (multiTarget) {
-        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D,
-                               mSecondaryTextureName, 0);
-        GLenum buffers[] = {GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT};
-        glDrawBuffers(2, buffers);
-    }
     mStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
     unbind();
     glBindTexture(GL_TEXTURE_2D, 0);
diff --git a/libs/renderengine/gl/GLFramebuffer.h b/libs/renderengine/gl/GLFramebuffer.h
index 69102d6..b88da3b 100644
--- a/libs/renderengine/gl/GLFramebuffer.h
+++ b/libs/renderengine/gl/GLFramebuffer.h
@@ -42,7 +42,6 @@
     void allocateBuffers(uint32_t width, uint32_t height);
     EGLImageKHR getEGLImage() const { return mEGLImage; }
     uint32_t getTextureName() const { return mTextureName; }
-    uint32_t getSecondaryTextureName() const { return mSecondaryTextureName; }
     uint32_t getFramebufferName() const { return mFramebufferName; }
     int32_t getBufferHeight() const { return mBufferHeight; }
     int32_t getBufferWidth() const { return mBufferWidth; }
@@ -59,7 +58,6 @@
     bool usingFramebufferCache = false;
     GLenum mStatus = GL_FRAMEBUFFER_UNSUPPORTED;
     uint32_t mTextureName, mFramebufferName;
-    uint32_t mSecondaryTextureName = -1;
 
     int32_t mBufferHeight = 0;
     int32_t mBufferWidth = 0;
diff --git a/libs/renderengine/gl/GLVertexBuffer.cpp b/libs/renderengine/gl/GLVertexBuffer.cpp
new file mode 100644
index 0000000..e50c471
--- /dev/null
+++ b/libs/renderengine/gl/GLVertexBuffer.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2020 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "GLVertexBuffer.h"
+
+#include <GLES/gl.h>
+#include <GLES2/gl2.h>
+#include <nativebase/nativebase.h>
+#include <utils/Trace.h>
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+GLVertexBuffer::GLVertexBuffer() {
+    glGenBuffers(1, &mBufferName);
+}
+
+GLVertexBuffer::~GLVertexBuffer() {
+    glDeleteBuffers(1, &mBufferName);
+}
+
+void GLVertexBuffer::allocateBuffers(const GLfloat data[], const GLuint size) {
+    ATRACE_CALL();
+    bind();
+    glBufferData(GL_ARRAY_BUFFER, size * sizeof(GLfloat), data, GL_STATIC_DRAW);
+    unbind();
+}
+
+void GLVertexBuffer::bind() const {
+    glBindBuffer(GL_ARRAY_BUFFER, mBufferName);
+}
+
+void GLVertexBuffer::unbind() const {
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLVertexBuffer.h b/libs/renderengine/gl/GLVertexBuffer.h
new file mode 100644
index 0000000..c0fd0c1
--- /dev/null
+++ b/libs/renderengine/gl/GLVertexBuffer.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+
+struct ANativeWindowBuffer;
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class GLESRenderEngine;
+
+class GLVertexBuffer {
+public:
+    explicit GLVertexBuffer();
+    ~GLVertexBuffer();
+
+    void allocateBuffers(const GLfloat data[], const GLuint size);
+    uint32_t getBufferName() const { return mBufferName; }
+    void bind() const;
+    void unbind() const;
+
+private:
+    uint32_t mBufferName;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/ImageManager.cpp b/libs/renderengine/gl/ImageManager.cpp
index 5af0e4f..6256649 100644
--- a/libs/renderengine/gl/ImageManager.cpp
+++ b/libs/renderengine/gl/ImageManager.cpp
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+//#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "RenderEngine"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include <pthread.h>
@@ -27,7 +30,10 @@
 namespace renderengine {
 namespace gl {
 
-ImageManager::ImageManager(GLESRenderEngine* engine) : mEngine(engine) {
+ImageManager::ImageManager(GLESRenderEngine* engine) : mEngine(engine) {}
+
+void ImageManager::initThread() {
+    mThread = std::thread([this]() { threadMain(); });
     pthread_setname_np(mThread.native_handle(), "ImageManager");
     // Use SCHED_FIFO to minimize jitter
     struct sched_param param = {0};
@@ -133,6 +139,8 @@
             entry.barrier->condition.notify_one();
         }
     }
+
+    ALOGD("Reached end of threadMain, terminating ImageManager thread!");
 }
 
 } // namespace gl
diff --git a/libs/renderengine/gl/ImageManager.h b/libs/renderengine/gl/ImageManager.h
index b5ba554..be67de8 100644
--- a/libs/renderengine/gl/ImageManager.h
+++ b/libs/renderengine/gl/ImageManager.h
@@ -39,6 +39,10 @@
     };
     ImageManager(GLESRenderEngine* engine);
     ~ImageManager();
+    // Starts the background thread for the ImageManager
+    // We need this to guarantee that the class is fully-constructed before the
+    // thread begins running.
+    void initThread();
     void cacheAsync(const sp<GraphicBuffer>& buffer, const std::shared_ptr<Barrier>& barrier)
             EXCLUDES(mMutex);
     status_t cache(const sp<GraphicBuffer>& buffer);
@@ -57,7 +61,7 @@
     void queueOperation(const QueueEntry&& entry);
     void threadMain();
     GLESRenderEngine* const mEngine;
-    std::thread mThread = std::thread([this]() { threadMain(); });
+    std::thread mThread;
     std::condition_variable_any mCondition;
     std::mutex mMutex;
     std::queue<QueueEntry> mQueue GUARDED_BY(mMutex);
diff --git a/libs/renderengine/gl/filters/BlurFilter.cpp b/libs/renderengine/gl/filters/BlurFilter.cpp
index eb66c8f..6ba78dc 100644
--- a/libs/renderengine/gl/filters/BlurFilter.cpp
+++ b/libs/renderengine/gl/filters/BlurFilter.cpp
@@ -31,13 +31,38 @@
 namespace gl {
 
 BlurFilter::BlurFilter(GLESRenderEngine& engine)
-      : mEngine(engine), mCompositionFbo(engine), mBlurredFbo(engine), mMixProgram(engine) {
+      : mEngine(engine),
+        mCompositionFbo(engine),
+        mPingFbo(engine),
+        mPongFbo(engine),
+        mMixProgram(engine),
+        mBlurProgram(engine) {
     mMixProgram.compile(getVertexShader(), getMixFragShader());
     mMPosLoc = mMixProgram.getAttributeLocation("aPosition");
     mMUvLoc = mMixProgram.getAttributeLocation("aUV");
     mMTextureLoc = mMixProgram.getUniformLocation("uTexture");
     mMCompositionTextureLoc = mMixProgram.getUniformLocation("uCompositionTexture");
     mMMixLoc = mMixProgram.getUniformLocation("uMix");
+
+    mBlurProgram.compile(getVertexShader(), getFragmentShader());
+    mBPosLoc = mBlurProgram.getAttributeLocation("aPosition");
+    mBUvLoc = mBlurProgram.getAttributeLocation("aUV");
+    mBTextureLoc = mBlurProgram.getUniformLocation("uTexture");
+    mBOffsetLoc = mBlurProgram.getUniformLocation("uOffset");
+
+    static constexpr auto size = 2.0f;
+    static constexpr auto translation = 1.0f;
+    const GLfloat vboData[] = {
+        // Vertex data
+        translation-size, -translation-size,
+        translation-size, -translation+size,
+        translation+size, -translation+size,
+        // UV data
+        0.0f, 0.0f-translation,
+        0.0f, size-translation,
+        size, size-translation
+    };
+    mMeshBuffer.allocateBuffers(vboData, 12 /* size */);
 }
 
 status_t BlurFilter::setAsDrawTarget(const DisplaySettings& display, uint32_t radius) {
@@ -51,18 +76,26 @@
 
         const uint32_t fboWidth = floorf(mDisplayWidth * kFboScale);
         const uint32_t fboHeight = floorf(mDisplayHeight * kFboScale);
-        mBlurredFbo.allocateBuffers(fboWidth, fboHeight);
-        allocateTextures();
+        mPingFbo.allocateBuffers(fboWidth, fboHeight);
+        mPongFbo.allocateBuffers(fboWidth, fboHeight);
         mTexturesAllocated = true;
-    }
 
-    if (mBlurredFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
-        ALOGE("Invalid blur buffer");
-        return mBlurredFbo.getStatus();
-    }
-    if (mCompositionFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
-        ALOGE("Invalid composition buffer");
-        return mCompositionFbo.getStatus();
+        if (mPingFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
+            ALOGE("Invalid ping buffer");
+            return mPingFbo.getStatus();
+        }
+        if (mPongFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
+            ALOGE("Invalid pong buffer");
+            return mPongFbo.getStatus();
+        }
+        if (mCompositionFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
+            ALOGE("Invalid composition buffer");
+            return mCompositionFbo.getStatus();
+        }
+        if (!mBlurProgram.isValid()) {
+            ALOGE("Invalid shader");
+            return GL_INVALID_OPERATION;
+        }
     }
 
     mCompositionFbo.bind();
@@ -71,29 +104,62 @@
 }
 
 void BlurFilter::drawMesh(GLuint uv, GLuint position) {
-    static constexpr auto size = 2.0f;
-    static constexpr auto translation = 1.0f;
-    GLfloat positions[] = {
-        translation-size, -translation-size,
-        translation-size, -translation+size,
-        translation+size, -translation+size
-    };
-    GLfloat texCoords[] = {
-        0.0f, 0.0f-translation,
-        0.0f, size-translation,
-        size, size-translation
-    };
 
-    // set attributes
     glEnableVertexAttribArray(uv);
-    glVertexAttribPointer(uv, 2 /* size */, GL_FLOAT, GL_FALSE, 0, texCoords);
     glEnableVertexAttribArray(position);
-    glVertexAttribPointer(position, 2 /* size */, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat),
-                          positions);
+    mMeshBuffer.bind();
+    glVertexAttribPointer(position, 2 /* size */, GL_FLOAT, GL_FALSE,
+                          2 * sizeof(GLfloat) /* stride */, 0 /* offset */);
+    glVertexAttribPointer(uv, 2 /* size */, GL_FLOAT, GL_FALSE, 0 /* stride */,
+                          (GLvoid*)(6 * sizeof(GLfloat)) /* offset */);
+    mMeshBuffer.unbind();
 
     // draw mesh
     glDrawArrays(GL_TRIANGLES, 0 /* first */, 3 /* count */);
-    mEngine.checkErrors("Drawing blur mesh");
+}
+
+status_t BlurFilter::prepare() {
+    ATRACE_NAME("BlurFilter::prepare");
+    blit(mCompositionFbo, mPingFbo);
+
+    // Kawase is an approximation of Gaussian, but it behaves differently from it.
+    // A radius transformation is required for approximating them, and also to introduce
+    // non-integer steps, necessary to smoothly interpolate large radii.
+    auto radius = mRadius / 6.0f;
+
+    // Calculate how many passes we'll do, based on the radius.
+    // Too many passes will make the operation expensive.
+    auto passes = min(kMaxPasses, (uint32_t)ceil(radius));
+
+    // We'll ping pong between our textures, to accumulate the result of various offsets.
+    mBlurProgram.useProgram();
+    GLFramebuffer* read = &mPingFbo;
+    GLFramebuffer* draw = &mPongFbo;
+    float stepX = radius / (float)mCompositionFbo.getBufferWidth() / (float)passes;
+    float stepY = radius / (float)mCompositionFbo.getBufferHeight() / (float)passes;
+    glViewport(0, 0, draw->getBufferWidth(), draw->getBufferHeight());
+    glActiveTexture(GL_TEXTURE0);
+    glUniform1i(mBTextureLoc, 0);
+    for (auto i = 0; i < passes; i++) {
+        ATRACE_NAME("BlurFilter::renderPass");
+        draw->bind();
+
+        glBindTexture(GL_TEXTURE_2D, read->getTextureName());
+        glUniform2f(mBOffsetLoc, stepX * i, stepY * i);
+
+        drawMesh(mBUvLoc, mBPosLoc);
+
+        // Swap buffers for next iteration
+        auto tmp = draw;
+        draw = read;
+        read = tmp;
+    }
+    mLastDrawTarget = read;
+
+    // Cleanup
+    glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+    return NO_ERROR;
 }
 
 status_t BlurFilter::render(bool multiPass) {
@@ -107,9 +173,10 @@
     // be writing onto it. Let's disable the crossfade, otherwise we'd need 1 extra frame buffer,
     // as large as the screen size.
     if (mix >= 1 || multiPass) {
-        mBlurredFbo.bindAsReadBuffer();
-        glBlitFramebuffer(0, 0, mBlurredFbo.getBufferWidth(), mBlurredFbo.getBufferHeight(), 0, 0,
-                          mDisplayWidth, mDisplayHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
+        mLastDrawTarget->bindAsReadBuffer();
+        glBlitFramebuffer(0, 0, mLastDrawTarget->getBufferWidth(),
+                          mLastDrawTarget->getBufferHeight(), 0, 0, mDisplayWidth, mDisplayHeight,
+                          GL_COLOR_BUFFER_BIT, GL_LINEAR);
         glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
         return NO_ERROR;
     }
@@ -117,22 +184,23 @@
     mMixProgram.useProgram();
     glUniform1f(mMMixLoc, mix);
     glActiveTexture(GL_TEXTURE0);
-    glBindTexture(GL_TEXTURE_2D, mBlurredFbo.getTextureName());
+    glBindTexture(GL_TEXTURE_2D, mLastDrawTarget->getTextureName());
     glUniform1i(mMTextureLoc, 0);
     glActiveTexture(GL_TEXTURE1);
     glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName());
     glUniform1i(mMCompositionTextureLoc, 1);
-    mEngine.checkErrors("Setting final pass uniforms");
 
     drawMesh(mMUvLoc, mMPosLoc);
 
     glUseProgram(0);
     glActiveTexture(GL_TEXTURE0);
+    mEngine.checkErrors("Drawing blur mesh");
     return NO_ERROR;
 }
 
 string BlurFilter::getVertexShader() const {
     return R"SHADER(#version 310 es
+        precision mediump float;
 
         in vec2 aPosition;
         in highp vec2 aUV;
@@ -145,6 +213,28 @@
     )SHADER";
 }
 
+string BlurFilter::getFragmentShader() const {
+    return R"SHADER(#version 310 es
+        precision mediump float;
+
+        uniform sampler2D uTexture;
+        uniform vec2 uOffset;
+
+        in highp vec2 vUV;
+        out vec4 fragColor;
+
+        void main() {
+            fragColor  = texture(uTexture, vUV, 0.0);
+            fragColor += texture(uTexture, vUV + vec2( uOffset.x,  uOffset.y), 0.0);
+            fragColor += texture(uTexture, vUV + vec2( uOffset.x, -uOffset.y), 0.0);
+            fragColor += texture(uTexture, vUV + vec2(-uOffset.x,  uOffset.y), 0.0);
+            fragColor += texture(uTexture, vUV + vec2(-uOffset.x, -uOffset.y), 0.0);
+
+            fragColor = vec4(fragColor.rgb * 0.2, 1.0);
+        }
+    )SHADER";
+}
+
 string BlurFilter::getMixFragShader() const {
     string shader = R"SHADER(#version 310 es
         precision mediump float;
@@ -165,6 +255,15 @@
     return shader;
 }
 
+void BlurFilter::blit(GLFramebuffer& read, GLFramebuffer& draw) const {
+    read.bindAsReadBuffer();
+    draw.bindAsDrawBuffer();
+    glBlitFramebuffer(0, 0, read.getBufferWidth(), read.getBufferHeight(), 0, 0,
+                      draw.getBufferWidth(), draw.getBufferHeight(), GL_COLOR_BUFFER_BIT,
+                      GL_LINEAR);
+    glBindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
 } // namespace gl
 } // namespace renderengine
 } // namespace android
diff --git a/libs/renderengine/gl/filters/BlurFilter.h b/libs/renderengine/gl/filters/BlurFilter.h
index 52dc8aa..3eb5c96 100644
--- a/libs/renderengine/gl/filters/BlurFilter.h
+++ b/libs/renderengine/gl/filters/BlurFilter.h
@@ -19,6 +19,7 @@
 #include <ui/GraphicTypes.h>
 #include "../GLESRenderEngine.h"
 #include "../GLFramebuffer.h"
+#include "../GLVertexBuffer.h"
 #include "GenericProgram.h"
 
 using namespace std;
@@ -27,10 +28,17 @@
 namespace renderengine {
 namespace gl {
 
+/**
+ * This is an implementation of a Kawase blur, as described in here:
+ * https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/
+ * 00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
+ */
 class BlurFilter {
 public:
     // Downsample FBO to improve performance
     static constexpr float kFboScale = 0.25f;
+    // Maximum number of render passes
+    static constexpr uint32_t kMaxPasses = 6;
     // To avoid downscaling artifacts, we interpolate the blurred fbo with the full composited
     // image, up to this radius.
     static constexpr float kMaxCrossFadeRadius = 30.0f;
@@ -40,36 +48,46 @@
 
     // Set up render targets, redirecting output to offscreen texture.
     status_t setAsDrawTarget(const DisplaySettings&, uint32_t radius);
-    // Allocate any textures needed for the filter.
-    virtual void allocateTextures() = 0;
     // Execute blur passes, rendering to offscreen texture.
-    virtual status_t prepare() = 0;
+    status_t prepare();
     // Render blur to the bound framebuffer (screen).
     status_t render(bool multiPass);
 
-protected:
+private:
     uint32_t mRadius;
     void drawMesh(GLuint uv, GLuint position);
+    void blit(GLFramebuffer& read, GLFramebuffer& draw) const;
     string getVertexShader() const;
+    string getFragmentShader() const;
+    string getMixFragShader() const;
 
     GLESRenderEngine& mEngine;
     // Frame buffer holding the composited background.
     GLFramebuffer mCompositionFbo;
-    // Frame buffer holding the blur result.
-    GLFramebuffer mBlurredFbo;
+    // Frame buffers holding the blur passes.
+    GLFramebuffer mPingFbo;
+    GLFramebuffer mPongFbo;
     uint32_t mDisplayWidth;
     uint32_t mDisplayHeight;
-
-private:
-    string getMixFragShader() const;
+    // Buffer holding the final blur pass.
+    GLFramebuffer* mLastDrawTarget;
     bool mTexturesAllocated = false;
 
+    // VBO containing vertex and uv data of a fullscreen triangle.
+    GLVertexBuffer mMeshBuffer;
+
     GenericProgram mMixProgram;
     GLuint mMPosLoc;
     GLuint mMUvLoc;
     GLuint mMMixLoc;
     GLuint mMTextureLoc;
     GLuint mMCompositionTextureLoc;
+
+    GenericProgram mBlurProgram;
+    GLuint mBPosLoc;
+    GLuint mBUvLoc;
+    GLuint mBTextureLoc;
+    GLuint mBOffsetLoc;
 };
 
 } // namespace gl
diff --git a/libs/renderengine/gl/filters/GaussianBlurFilter.cpp b/libs/renderengine/gl/filters/GaussianBlurFilter.cpp
deleted file mode 100644
index a0d7af8..0000000
--- a/libs/renderengine/gl/filters/GaussianBlurFilter.cpp
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "GaussianBlurFilter.h"
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <GLES3/gl3.h>
-#include <GLES3/gl3ext.h>
-#include <ui/GraphicTypes.h>
-#include <cstdint>
-
-#include <utils/Trace.h>
-
-#define PI 3.14159265359
-#define THETA 0.352
-#define K 1.0 / (2.0 * THETA * THETA)
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-GaussianBlurFilter::GaussianBlurFilter(GLESRenderEngine& engine)
-      : BlurFilter(engine),
-        mVerticalPassFbo(engine),
-        mVerticalProgram(engine),
-        mHorizontalProgram(engine) {
-    mVerticalProgram.compile(getVertexShader(), getFragmentShader(false));
-    mVPosLoc = mVerticalProgram.getAttributeLocation("aPosition");
-    mVUvLoc = mVerticalProgram.getAttributeLocation("aUV");
-    mVTextureLoc = mVerticalProgram.getUniformLocation("uTexture");
-    mVGaussianOffsetLoc = mVerticalProgram.getUniformLocation("uGaussianOffsets");
-    mVNumSamplesLoc = mVerticalProgram.getUniformLocation("uSamples");
-    mVGaussianWeightLoc = mVerticalProgram.getUniformLocation("uGaussianWeights");
-
-    mHorizontalProgram.compile(getVertexShader(), getFragmentShader(true));
-    mHPosLoc = mHorizontalProgram.getAttributeLocation("aPosition");
-    mHUvLoc = mHorizontalProgram.getAttributeLocation("aUV");
-    mHTextureLoc = mHorizontalProgram.getUniformLocation("uTexture");
-    mHGaussianOffsetLoc = mHorizontalProgram.getUniformLocation("uGaussianOffsets");
-    mHNumSamplesLoc = mHorizontalProgram.getUniformLocation("uSamples");
-    mHGaussianWeightLoc = mHorizontalProgram.getUniformLocation("uGaussianWeights");
-}
-
-void GaussianBlurFilter::allocateTextures() {
-    mVerticalPassFbo.allocateBuffers(mBlurredFbo.getBufferWidth(), mBlurredFbo.getBufferHeight());
-}
-
-static void calculateLinearGaussian(uint32_t samples, double dimension,
-                                    GLfloat* gaussianLinearOffsets, GLfloat* gaussianWeights,
-                                    GLfloat* gaussianLinearWeights) {
-    // The central point in the symmetric bell curve is not offset.
-    // This decision allows one less sampling in the GPU.
-    gaussianLinearWeights[0] = gaussianWeights[0];
-    gaussianLinearOffsets[0] = 0.0;
-
-    // Calculate the linear weights.
-    // This is a vector reduction where an element of the packed reduced array
-    // contains the sum of two adjacent members of the original packed array.
-    // We start preserving the element 1 of the array and then perform sum for
-    // every other (i+=2) element of the gaussianWeights array.
-    gaussianLinearWeights[1] = gaussianWeights[1];
-    const auto start = 1 + ((samples - 1) & 0x1);
-    for (size_t i = start; i < samples; i += 2) {
-        gaussianLinearWeights[start + i / 2] = gaussianWeights[i] + gaussianWeights[i + 1];
-    }
-
-    // Calculate the texture coordinates offsets as an average of the initial offsets,
-    // weighted by the Gaussian weights as described in the original article.
-    gaussianLinearOffsets[1] = 1.0 / dimension;
-    for (size_t i = start; i < samples; i += 2) {
-        GLfloat offset_1 = float(i) / dimension;
-        GLfloat offset_2 = float(i + 1) / dimension;
-        gaussianLinearOffsets[start + i / 2] =
-                (offset_1 * gaussianWeights[i] + offset_2 * gaussianWeights[i + 1]) /
-                gaussianLinearWeights[start + i / 2];
-    }
-}
-status_t GaussianBlurFilter::prepare() {
-    ATRACE_NAME("GaussianBlurFilter::prepare");
-
-    if (mVerticalPassFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
-        ALOGE("Invalid vertical FBO");
-        return mVerticalPassFbo.getStatus();
-    }
-    if (!mVerticalProgram.isValid()) {
-        ALOGE("Invalid vertical shader");
-        return GL_INVALID_OPERATION;
-    }
-    if (!mHorizontalProgram.isValid()) {
-        ALOGE("Invalid horizontal shader");
-        return GL_INVALID_OPERATION;
-    }
-
-    mCompositionFbo.bindAsReadBuffer();
-    mBlurredFbo.bindAsDrawBuffer();
-    glBlitFramebuffer(0, 0, mCompositionFbo.getBufferWidth(), mCompositionFbo.getBufferHeight(), 0,
-                      0, mBlurredFbo.getBufferWidth(), mBlurredFbo.getBufferHeight(),
-                      GL_COLOR_BUFFER_BIT, GL_LINEAR);
-    glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
-    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
-
-    // First, we'll apply the vertical pass, that receives the flattened background layers.
-    mVerticalPassFbo.bind();
-    mVerticalProgram.useProgram();
-
-    // Precompute gaussian bell curve, and send it to the shader to avoid unnecessary computations.
-    double radiusD = fmax(1.0, mRadius * kFboScale);
-    auto samples = int(fmin(radiusD, kNumSamples));
-    GLfloat gaussianWeights[kNumSamples] = {};
-
-    gaussianWeights[0] = 1.0f;
-    auto totalWeight = gaussianWeights[0];
-
-    // Gaussian weights calculation.
-    for (size_t i = 1; i < samples; i++) {
-        const double normalized = i / radiusD;
-        gaussianWeights[i] = (float)exp(-K * normalized * normalized);
-        totalWeight += 2.0 * gaussianWeights[i];
-    }
-
-    // Gaussian weights normalization to avoid work in the GPU.
-    for (size_t i = 0; i < samples; i++) {
-        gaussianWeights[i] /= totalWeight;
-    }
-
-    auto width = mVerticalPassFbo.getBufferWidth();
-    auto height = mVerticalPassFbo.getBufferHeight();
-    glViewport(0, 0, width, height);
-
-    // Allocate space for the corrected Gaussian weights and offsets.
-    // We could use less space, but let's keep the code simple.
-    GLfloat gaussianLinearWeights[kNumSamples] = {};
-    GLfloat gaussianLinearOffsets[kNumSamples] = {};
-
-    // Calculate the weights and offsets for the vertical pass.
-    // This only need to be called every time mRadius or height changes, so it could be optimized.
-    calculateLinearGaussian(samples, double(height), gaussianLinearOffsets, gaussianWeights,
-                            gaussianLinearWeights);
-    // set uniforms
-    glActiveTexture(GL_TEXTURE0);
-    glBindTexture(GL_TEXTURE_2D, mBlurredFbo.getTextureName());
-    glUniform1i(mVTextureLoc, 0);
-    glUniform1i(mVNumSamplesLoc, 1 + (samples + 1) / 2);
-    glUniform1fv(mVGaussianWeightLoc, kNumSamples, gaussianLinearWeights);
-    glUniform1fv(mVGaussianOffsetLoc, kNumSamples, gaussianLinearOffsets);
-    mEngine.checkErrors("Setting vertical pass uniforms");
-
-    drawMesh(mVUvLoc, mVPosLoc);
-
-    // Blur vertically on a secondary pass
-    mBlurredFbo.bind();
-    mHorizontalProgram.useProgram();
-
-    // Calculate the weights and offsets for the horizontal pass.
-    // This only needs to be called every time mRadius or width change, so it could be optimized.
-    calculateLinearGaussian(samples, double(width), gaussianLinearOffsets, gaussianWeights,
-                            gaussianLinearWeights);
-    // set uniforms
-    glActiveTexture(GL_TEXTURE0);
-    glBindTexture(GL_TEXTURE_2D, mVerticalPassFbo.getTextureName());
-    glUniform1i(mHTextureLoc, 0);
-    glUniform1i(mHNumSamplesLoc, 1 + (samples + 1) / 2);
-    glUniform1fv(mHGaussianWeightLoc, kNumSamples, gaussianLinearWeights);
-    glUniform1fv(mHGaussianOffsetLoc, kNumSamples, gaussianLinearOffsets);
-    mEngine.checkErrors("Setting horizontal pass uniforms");
-
-    drawMesh(mHUvLoc, mHPosLoc);
-
-    // reset active texture
-    mBlurredFbo.unbind();
-    glActiveTexture(GL_TEXTURE0);
-    glBindTexture(GL_TEXTURE_2D, 0);
-
-    // unbind program
-    glUseProgram(0);
-
-    return NO_ERROR;
-}
-
-string GaussianBlurFilter::getFragmentShader(bool horizontal) const {
-    stringstream shader;
-    shader << "#version 310 es\n"
-           << "#define DIRECTION " << (horizontal ? "1" : "0") << "\n"
-           << "#define NUM_SAMPLES " << 1 + (kNumSamples + 1) / 2 <<
-            R"SHADER(
-        precision mediump float;
-
-        uniform sampler2D uTexture;
-        uniform float[NUM_SAMPLES] uGaussianWeights;
-        uniform float[NUM_SAMPLES] uGaussianOffsets;
-        uniform int uSamples;
-
-        highp in vec2 vUV;
-        out vec4 fragColor;
-
-        void main() {
-            #if DIRECTION == 1
-            const vec2 direction = vec2(1.0, 0.0);
-            #else
-            const vec2 direction = vec2(0.0, 1.0);
-            #endif
-
-            // Iteration zero outside loop to avoid sampling the central point twice.
-            vec4 blurred = uGaussianWeights[0] * (texture(uTexture, vUV, 0.0));
-
-            // Iterate one side of the bell to halve the loop iterations.
-            for (int i = 1; i <= uSamples; i++) {
-                vec2 offset = uGaussianOffsets[i] * direction;
-                blurred += uGaussianWeights[i] * (texture(uTexture, vUV + offset, 0.0));
-                blurred += uGaussianWeights[i] * (texture(uTexture, vUV - offset, 0.0));
-            }
-
-            fragColor = vec4(blurred.rgb, 1.0);
-        }
-    )SHADER";
-    return shader.str();
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/filters/GaussianBlurFilter.h b/libs/renderengine/gl/filters/GaussianBlurFilter.h
deleted file mode 100644
index 44f5fde..0000000
--- a/libs/renderengine/gl/filters/GaussianBlurFilter.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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 <ui/GraphicTypes.h>
-#include "../GLESRenderEngine.h"
-#include "../GLFramebuffer.h"
-#include "BlurFilter.h"
-#include "GenericProgram.h"
-
-using namespace std;
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-// Class that implements a Gaussian Filter that uses Linear Sampling
-// to halve the number of samples and reduce runtime by 40% as described in:
-// http://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling
-class GaussianBlurFilter : public BlurFilter {
-public:
-    static constexpr uint32_t kNumSamples = 22;
-
-    explicit GaussianBlurFilter(GLESRenderEngine& engine);
-    status_t prepare() override;
-    void allocateTextures() override;
-
-private:
-    string getFragmentShader(bool horizontal) const;
-
-    // Initial, vertical render pass
-    GLFramebuffer mVerticalPassFbo;
-
-    // Vertical pass and its uniforms
-    GenericProgram mVerticalProgram;
-    GLuint mVPosLoc;
-    GLuint mVUvLoc;
-    GLuint mVTextureLoc;
-    GLuint mVGaussianOffsetLoc;
-    GLuint mVNumSamplesLoc;
-    GLuint mVGaussianWeightLoc;
-
-    // Horizontal pass and its uniforms
-    GenericProgram mHorizontalProgram;
-    GLuint mHPosLoc;
-    GLuint mHUvLoc;
-    GLuint mHTextureLoc;
-    GLuint mHGaussianOffsetLoc;
-    GLuint mHNumSamplesLoc;
-    GLuint mHGaussianWeightLoc;
-};
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/gl/filters/KawaseBlurFilter.cpp b/libs/renderengine/gl/filters/KawaseBlurFilter.cpp
deleted file mode 100644
index 7524c6d..0000000
--- a/libs/renderengine/gl/filters/KawaseBlurFilter.cpp
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright 2020 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "KawaseBlurFilter.h"
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <GLES3/gl3.h>
-#include <GLES3/gl3ext.h>
-#include <ui/GraphicTypes.h>
-
-#include <utils/Trace.h>
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-KawaseBlurFilter::KawaseBlurFilter(GLESRenderEngine& engine)
-      : BlurFilter(engine), mFbo(engine), mProgram(engine) {
-    mProgram.compile(getVertexShader(), getFragmentShader());
-    mPosLoc = mProgram.getAttributeLocation("aPosition");
-    mUvLoc = mProgram.getAttributeLocation("aUV");
-    mTextureLoc = mProgram.getUniformLocation("uTexture");
-    mOffsetLoc = mProgram.getUniformLocation("uOffset");
-}
-
-void KawaseBlurFilter::allocateTextures() {
-    mFbo.allocateBuffers(mBlurredFbo.getBufferWidth(), mBlurredFbo.getBufferHeight());
-}
-
-status_t KawaseBlurFilter::prepare() {
-    ATRACE_NAME("KawaseBlurFilter::prepare");
-
-    if (mFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
-        ALOGE("Invalid FBO");
-        return mFbo.getStatus();
-    }
-    if (!mProgram.isValid()) {
-        ALOGE("Invalid shader");
-        return GL_INVALID_OPERATION;
-    }
-
-    blit(mCompositionFbo, mBlurredFbo);
-
-    // Kawase is an approximation of Gaussian, but it behaves differently from it.
-    // A radius transformation is required for approximating them, and also to introduce
-    // non-integer steps, necessary to smoothly interpolate large radii.
-    auto radius = mRadius / 6.0f;
-
-    // Calculate how many passes we'll do, based on the radius.
-    // Too many passes will make the operation expensive.
-    auto passes = min(kMaxPasses, (uint32_t)ceil(radius));
-
-    // We'll ping pong between our textures, to accumulate the result of various offsets.
-    mProgram.useProgram();
-    GLFramebuffer* draw = &mFbo;
-    GLFramebuffer* read = &mBlurredFbo;
-    float stepX = radius / (float)mCompositionFbo.getBufferWidth() / (float)passes;
-    float stepY = radius / (float)mCompositionFbo.getBufferHeight() / (float)passes;
-    glActiveTexture(GL_TEXTURE0);
-    glUniform1i(mTextureLoc, 0);
-    for (auto i = 0; i < passes; i++) {
-        ATRACE_NAME("KawaseBlurFilter::renderPass");
-        draw->bind();
-
-        glViewport(0, 0, draw->getBufferWidth(), draw->getBufferHeight());
-        glBindTexture(GL_TEXTURE_2D, read->getTextureName());
-        glUniform2f(mOffsetLoc, stepX * i, stepY * i);
-        mEngine.checkErrors("Setting uniforms");
-
-        drawMesh(mUvLoc, mPosLoc);
-
-        // Swap buffers for next iteration
-        auto tmp = draw;
-        draw = read;
-        read = tmp;
-    }
-
-    // Copy texture, given that we're expected to end on mBlurredFbo.
-    if (draw == &mBlurredFbo) {
-        blit(mFbo, mBlurredFbo);
-    }
-
-    // Cleanup
-    glBindFramebuffer(GL_FRAMEBUFFER, 0);
-
-    return NO_ERROR;
-}
-
-string KawaseBlurFilter::getFragmentShader() const {
-    return R"SHADER(#version 310 es
-        precision mediump float;
-
-        uniform sampler2D uTexture;
-        uniform vec2 uOffset;
-
-        highp in vec2 vUV;
-        out vec4 fragColor;
-
-        void main() {
-            fragColor  = texture(uTexture, vUV, 0.0);
-            fragColor += texture(uTexture, vUV + vec2( uOffset.x,  uOffset.y), 0.0);
-            fragColor += texture(uTexture, vUV + vec2( uOffset.x, -uOffset.y), 0.0);
-            fragColor += texture(uTexture, vUV + vec2(-uOffset.x,  uOffset.y), 0.0);
-            fragColor += texture(uTexture, vUV + vec2(-uOffset.x, -uOffset.y), 0.0);
-
-            fragColor = vec4(fragColor.rgb * 0.2, 1.0);
-        }
-    )SHADER";
-}
-
-void KawaseBlurFilter::blit(GLFramebuffer& read, GLFramebuffer& draw) const {
-    read.bindAsReadBuffer();
-    draw.bindAsDrawBuffer();
-    glBlitFramebuffer(0, 0, read.getBufferWidth(), read.getBufferHeight(), 0, 0,
-                      draw.getBufferWidth(), draw.getBufferHeight(), GL_COLOR_BUFFER_BIT,
-                      GL_LINEAR);
-    glBindFramebuffer(GL_FRAMEBUFFER, 0);
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/filters/KawaseBlurFilter.h b/libs/renderengine/gl/filters/KawaseBlurFilter.h
deleted file mode 100644
index 20009cf..0000000
--- a/libs/renderengine/gl/filters/KawaseBlurFilter.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2020 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 <ui/GraphicTypes.h>
-#include "../GLESRenderEngine.h"
-#include "../GLFramebuffer.h"
-#include "BlurFilter.h"
-#include "GenericProgram.h"
-
-using namespace std;
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-class KawaseBlurFilter : public BlurFilter {
-public:
-    static constexpr uint32_t kMaxPasses = 6;
-
-    explicit KawaseBlurFilter(GLESRenderEngine& engine);
-    status_t prepare() override;
-    void allocateTextures() override;
-
-private:
-    string getFragmentShader() const;
-    void blit(GLFramebuffer& read, GLFramebuffer& draw) const;
-
-    GLFramebuffer mFbo;
-
-    GenericProgram mProgram;
-    GLuint mPosLoc;
-    GLuint mUvLoc;
-    GLuint mTextureLoc;
-    GLuint mOffsetLoc;
-};
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index 30c48c8..d8e4059 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -77,6 +77,7 @@
     outDescriptorInfo->layerCount = layerCount;
     outDescriptorInfo->format = static_cast<hardware::graphics::common::V1_2::PixelFormat>(format);
     outDescriptorInfo->usage = usage;
+    outDescriptorInfo->reservedSize = 0;
 }
 
 } // anonymous namespace
@@ -364,7 +365,7 @@
     }
 
     *outYcbcr = ycbcr;
-    return static_cast<status_t>(Error::UNSUPPORTED);
+    return static_cast<status_t>(Error::NONE);
 }
 
 int Gralloc4Mapper::unlock(buffer_handle_t bufferHandle) const {
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index cd2a448..bf487c4 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -67,20 +67,19 @@
 // ----------------------------------------------------------------------------
 
 Region::Region() {
-    mStorage.push_back(Rect(0, 0));
+    mStorage.add(Rect(0,0));
 }
 
 Region::Region(const Region& rhs)
+    : mStorage(rhs.mStorage)
 {
-    mStorage.clear();
-    mStorage.insert(mStorage.begin(), rhs.mStorage.begin(), rhs.mStorage.end());
 #if defined(VALIDATE_REGIONS)
     validate(rhs, "rhs copy-ctor");
 #endif
 }
 
 Region::Region(const Rect& rhs) {
-    mStorage.push_back(rhs);
+    mStorage.add(rhs);
 }
 
 Region::~Region()
@@ -101,8 +100,8 @@
  * final, correctly ordered region buffer. Each rectangle will be compared with the span directly
  * above it, and subdivided to resolve any remaining T-junctions.
  */
-static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end, FatVector<Rect>& dst,
-                                           int spanDirection) {
+static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end,
+        Vector<Rect>& dst, int spanDirection) {
     dst.clear();
 
     const Rect* current = end - 1;
@@ -110,7 +109,7 @@
 
     // add first span immediately
     do {
-        dst.push_back(*current);
+        dst.add(*current);
         current--;
     } while (current->top == lastTop && current >= begin);
 
@@ -148,12 +147,12 @@
                 if (prev.right <= left) break;
 
                 if (prev.right > left && prev.right < right) {
-                    dst.push_back(Rect(prev.right, top, right, bottom));
+                    dst.add(Rect(prev.right, top, right, bottom));
                     right = prev.right;
                 }
 
                 if (prev.left > left && prev.left < right) {
-                    dst.push_back(Rect(prev.left, top, right, bottom));
+                    dst.add(Rect(prev.left, top, right, bottom));
                     right = prev.left;
                 }
 
@@ -167,12 +166,12 @@
                 if (prev.left >= right) break;
 
                 if (prev.left > left && prev.left < right) {
-                    dst.push_back(Rect(left, top, prev.left, bottom));
+                    dst.add(Rect(left, top, prev.left, bottom));
                     left = prev.left;
                 }
 
                 if (prev.right > left && prev.right < right) {
-                    dst.push_back(Rect(left, top, prev.right, bottom));
+                    dst.add(Rect(left, top, prev.right, bottom));
                     left = prev.right;
                 }
                 // if an entry in the previous span is too far left, nothing further right in the
@@ -184,7 +183,7 @@
         }
 
         if (left < right) {
-            dst.push_back(Rect(left, top, right, bottom));
+            dst.add(Rect(left, top, right, bottom));
         }
 
         current--;
@@ -202,14 +201,13 @@
     if (r.isEmpty()) return r;
     if (r.isRect()) return r;
 
-    FatVector<Rect> reversed;
+    Vector<Rect> reversed;
     reverseRectsResolvingJunctions(r.begin(), r.end(), reversed, direction_RTL);
 
     Region outputRegion;
-    reverseRectsResolvingJunctions(reversed.data(), reversed.data() + reversed.size(),
-                                   outputRegion.mStorage, direction_LTR);
-    outputRegion.mStorage.push_back(
-            r.getBounds()); // to make region valid, mStorage must end with bounds
+    reverseRectsResolvingJunctions(reversed.begin(), reversed.end(),
+            outputRegion.mStorage, direction_LTR);
+    outputRegion.mStorage.add(r.getBounds()); // to make region valid, mStorage must end with bounds
 
 #if defined(VALIDATE_REGIONS)
     validate(outputRegion, "T-Junction free region");
@@ -224,8 +222,7 @@
     validate(*this, "this->operator=");
     validate(rhs, "rhs.operator=");
 #endif
-    mStorage.clear();
-    mStorage.insert(mStorage.begin(), rhs.mStorage.begin(), rhs.mStorage.end());
+    mStorage = rhs.mStorage;
     return *this;
 }
 
@@ -234,7 +231,7 @@
     if (mStorage.size() >= 2) {
         const Rect bounds(getBounds());
         mStorage.clear();
-        mStorage.push_back(bounds);
+        mStorage.add(bounds);
     }
     return *this;
 }
@@ -258,25 +255,25 @@
 void Region::clear()
 {
     mStorage.clear();
-    mStorage.push_back(Rect(0, 0));
+    mStorage.add(Rect(0,0));
 }
 
 void Region::set(const Rect& r)
 {
     mStorage.clear();
-    mStorage.push_back(r);
+    mStorage.add(r);
 }
 
 void Region::set(int32_t w, int32_t h)
 {
     mStorage.clear();
-    mStorage.push_back(Rect(w, h));
+    mStorage.add(Rect(w, h));
 }
 
 void Region::set(uint32_t w, uint32_t h)
 {
     mStorage.clear();
-    mStorage.push_back(Rect(w, h));
+    mStorage.add(Rect(w, h));
 }
 
 bool Region::isTriviallyEqual(const Region& region) const {
@@ -302,7 +299,8 @@
 void Region::addRectUnchecked(int l, int t, int r, int b)
 {
     Rect rect(l,t,r,b);
-    mStorage.insert(mStorage.end() - 1, rect);
+    size_t where = mStorage.size() - 1;
+    mStorage.insertAt(rect, where, 1);
 }
 
 // ----------------------------------------------------------------------------
@@ -352,7 +350,7 @@
 
 Region& Region::scaleSelf(float sx, float sy) {
     size_t count = mStorage.size();
-    Rect* rects = mStorage.data();
+    Rect* rects = mStorage.editArray();
     while (count) {
         rects->left = static_cast<int32_t>(static_cast<float>(rects->left) * sx + 0.5f);
         rects->right = static_cast<int32_t>(static_cast<float>(rects->right) * sx + 0.5f);
@@ -457,10 +455,10 @@
 class Region::rasterizer : public region_operator<Rect>::region_rasterizer
 {
     Rect bounds;
-    FatVector<Rect>& storage;
+    Vector<Rect>& storage;
     Rect* head;
     Rect* tail;
-    FatVector<Rect> span;
+    Vector<Rect> span;
     Rect* cur;
 public:
     explicit rasterizer(Region& reg)
@@ -487,8 +485,8 @@
         flushSpan();
     }
     if (storage.size()) {
-        bounds.top = storage.front().top;
-        bounds.bottom = storage.back().bottom;
+        bounds.top = storage.itemAt(0).top;
+        bounds.bottom = storage.top().bottom;
         if (storage.size() == 1) {
             storage.clear();
         }
@@ -496,7 +494,7 @@
         bounds.left  = 0;
         bounds.right = 0;
     }
-    storage.push_back(bounds);
+    storage.add(bounds);
 }
 
 void Region::rasterizer::operator()(const Rect& rect)
@@ -511,15 +509,15 @@
             return;
         }
     }
-    span.push_back(rect);
-    cur = span.data() + (span.size() - 1);
+    span.add(rect);
+    cur = span.editArray() + (span.size() - 1);
 }
 
 void Region::rasterizer::flushSpan()
 {
     bool merge = false;
     if (tail-head == ssize_t(span.size())) {
-        Rect const* p = span.data();
+        Rect const* p = span.editArray();
         Rect const* q = head;
         if (p->top == q->bottom) {
             merge = true;
@@ -534,17 +532,17 @@
         }
     }
     if (merge) {
-        const int bottom = span.front().bottom;
+        const int bottom = span[0].bottom;
         Rect* r = head;
         while (r != tail) {
             r->bottom = bottom;
             r++;
         }
     } else {
-        bounds.left = min(span.front().left, bounds.left);
-        bounds.right = max(span.back().right, bounds.right);
-        storage.insert(storage.end(), span.begin(), span.end());
-        tail = storage.data() + storage.size();
+        bounds.left = min(span.itemAt(0).left, bounds.left);
+        bounds.right = max(span.top().right, bounds.right);
+        storage.appendVector(span);
+        tail = storage.editArray() + storage.size();
         head = tail - span.size();
     }
     span.clear();
@@ -552,7 +550,7 @@
 
 bool Region::validate(const Region& reg, const char* name, bool silent)
 {
-    if (reg.mStorage.empty()) {
+    if (reg.mStorage.isEmpty()) {
         ALOGE_IF(!silent, "%s: mStorage is empty, which is never valid", name);
         // return immediately as the code below assumes mStorage is non-empty
         return false;
@@ -691,8 +689,9 @@
     }
     sk_dst.op(sk_lhs, sk_rhs, sk_op);
 
-    if (sk_dst.empty() && dst.empty()) return;
-
+    if (sk_dst.isEmpty() && dst.isEmpty())
+        return;
+    
     bool same = true;
     Region::const_iterator head = dst.begin();
     Region::const_iterator const tail = dst.end();
@@ -787,7 +786,7 @@
         validate(reg, "translate (before)");
 #endif
         size_t count = reg.mStorage.size();
-        Rect* rects = reg.mStorage.data();
+        Rect* rects = reg.mStorage.editArray();
         while (count) {
             rects->offsetBy(dx, dy);
             rects++;
@@ -867,25 +866,24 @@
         ALOGE("Region::unflatten() failed, invalid region");
         return BAD_VALUE;
     }
-    mStorage.clear();
-    mStorage.insert(mStorage.begin(), result.mStorage.begin(), result.mStorage.end());
+    mStorage = result.mStorage;
     return NO_ERROR;
 }
 
 // ----------------------------------------------------------------------------
 
 Region::const_iterator Region::begin() const {
-    return mStorage.data();
+    return mStorage.array();
 }
 
 Region::const_iterator Region::end() const {
     // Workaround for b/77643177
     // mStorage should never be empty, but somehow it is and it's causing
     // an abort in ubsan
-    if (mStorage.empty()) return mStorage.data();
+    if (mStorage.isEmpty()) return mStorage.array();
 
     size_t numRects = isRect() ? 1 : mStorage.size() - 1;
-    return mStorage.data() + numRects;
+    return mStorage.array() + numRects;
 }
 
 Rect const* Region::getArray(size_t* count) const {
diff --git a/libs/ui/include/ui/DisplayConfig.h b/libs/ui/include/ui/DisplayConfig.h
index 09b8211..d6fbaab 100644
--- a/libs/ui/include/ui/DisplayConfig.h
+++ b/libs/ui/include/ui/DisplayConfig.h
@@ -33,6 +33,7 @@
     nsecs_t appVsyncOffset = 0;
     nsecs_t sfVsyncOffset = 0;
     nsecs_t presentationDeadline = 0;
+    int configGroup = -1;
 };
 
 static_assert(std::is_trivially_copyable_v<DisplayConfig>);
diff --git a/libs/ui/include/ui/FatVector.h b/libs/ui/include/ui/FatVector.h
deleted file mode 100644
index 25fe3a0..0000000
--- a/libs/ui/include/ui/FatVector.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ANDROID_REGION_FAT_VECTOR_H
-#define ANDROID_REGION_FAT_VECTOR_H
-
-#include <stddef.h>
-#include <stdlib.h>
-#include <utils/Log.h>
-#include <type_traits>
-
-#include <vector>
-
-namespace android {
-
-template <typename T, size_t SIZE = 4>
-class InlineStdAllocator {
-public:
-    struct Allocation {
-    private:
-        Allocation(const Allocation&) = delete;
-        void operator=(const Allocation&) = delete;
-
-    public:
-        Allocation() {}
-        // char array instead of T array, so memory is uninitialized, with no destructors run
-        char array[sizeof(T) * SIZE];
-        bool inUse = false;
-    };
-
-    typedef T value_type; // needed to implement std::allocator
-    typedef T* pointer;   // needed to implement std::allocator
-
-    explicit InlineStdAllocator(Allocation& allocation) : mAllocation(allocation) {}
-    InlineStdAllocator(const InlineStdAllocator& other) : mAllocation(other.mAllocation) {}
-    ~InlineStdAllocator() {}
-
-    T* allocate(size_t num, const void* = 0) {
-        if (!mAllocation.inUse && num <= SIZE) {
-            mAllocation.inUse = true;
-            return static_cast<T*>(static_cast<void*>(mAllocation.array));
-        } else {
-            return static_cast<T*>(static_cast<void*>(malloc(num * sizeof(T))));
-        }
-    }
-
-    void deallocate(pointer p, size_t) {
-        if (p == static_cast<T*>(static_cast<void*>(mAllocation.array))) {
-            mAllocation.inUse = false;
-        } else {
-            // 'free' instead of delete here - destruction handled separately
-            free(p);
-        }
-    }
-    Allocation& mAllocation;
-};
-
-/**
- * std::vector with SIZE elements preallocated into an internal buffer.
- *
- * Useful for avoiding the cost of malloc in cases where only SIZE or
- * fewer elements are needed in the common case.
- */
-template <typename T, size_t SIZE = 4>
-class FatVector : public std::vector<T, InlineStdAllocator<T, SIZE>> {
-public:
-    FatVector()
-          : std::vector<T, InlineStdAllocator<T, SIZE>>(InlineStdAllocator<T, SIZE>(mAllocation)) {
-        this->reserve(SIZE);
-    }
-
-    explicit FatVector(size_t capacity) : FatVector() { this->resize(capacity); }
-
-private:
-    typename InlineStdAllocator<T, SIZE>::Allocation mAllocation;
-};
-
-} // namespace android
-
-#endif // ANDROID_REGION_FAT_VECTOR_H
diff --git a/libs/ui/include/ui/Region.h b/libs/ui/include/ui/Region.h
index 6bb7b8d..2db3b10 100644
--- a/libs/ui/include/ui/Region.h
+++ b/libs/ui/include/ui/Region.h
@@ -21,13 +21,13 @@
 #include <sys/types.h>
 #include <ostream>
 
+#include <utils/Vector.h>
+
 #include <ui/Rect.h>
 #include <utils/Flattenable.h>
 
 #include <android-base/macros.h>
 
-#include "FatVector.h"
-
 #include <string>
 
 namespace android {
@@ -180,7 +180,7 @@
     // with an extra Rect as the last element which is set to the
     // bounds of the region. However, if the region is
     // a simple Rect then mStorage contains only that rect.
-    FatVector<Rect> mStorage;
+    Vector<Rect> mStorage;
 };
 
 
@@ -235,3 +235,4 @@
 }; // namespace android
 
 #endif // ANDROID_UI_REGION_H
+
diff --git a/libs/ui/include/ui/Size.h b/libs/ui/include/ui/Size.h
index d9b713d..f1e8252 100644
--- a/libs/ui/include/ui/Size.h
+++ b/libs/ui/include/ui/Size.h
@@ -19,6 +19,7 @@
 #include <algorithm>
 #include <cstdint>
 #include <limits>
+#include <ostream>
 #include <type_traits>
 #include <utility>
 
@@ -108,31 +109,70 @@
     // Takes a value of type FromType, and ensures it can be represented as a value of type ToType,
     // clamping the input value to the output range if necessary.
     template <typename ToType, typename FromType>
-    static Size::remove_cv_reference_t<ToType> clamp(
-            typename std::enable_if<
-                    std::numeric_limits<Size::remove_cv_reference_t<ToType>>::is_bounded &&
-                            std::numeric_limits<Size::remove_cv_reference_t<FromType>>::is_bounded,
-                    FromType&&>::type v) {
-        static constexpr auto toHighest = std::numeric_limits<remove_cv_reference_t<ToType>>::max();
-        static constexpr auto toLowest =
-                std::numeric_limits<remove_cv_reference_t<ToType>>::lowest();
-        static constexpr auto fromHighest =
-                std::numeric_limits<remove_cv_reference_t<FromType>>::max();
-        static constexpr auto fromLowest =
-                std::numeric_limits<remove_cv_reference_t<FromType>>::lowest();
+    static Size::remove_cv_reference_t<ToType>
+    clamp(typename std::enable_if<
+            std::numeric_limits<Size::remove_cv_reference_t<ToType>>::is_specialized &&
+                    std::numeric_limits<Size::remove_cv_reference_t<FromType>>::is_specialized,
+            FromType>::type v) {
+        using BareToType = remove_cv_reference_t<ToType>;
+        using BareFromType = remove_cv_reference_t<FromType>;
+        static constexpr auto toHighest = std::numeric_limits<BareToType>::max();
+        static constexpr auto toLowest = std::numeric_limits<BareToType>::lowest();
+        static constexpr auto fromHighest = std::numeric_limits<BareFromType>::max();
+        static constexpr auto fromLowest = std::numeric_limits<BareFromType>::lowest();
 
-        // A clamp is needed if the range of FromType is not a subset of the range of ToType
-        static constexpr bool isClampNeeded = (toLowest > fromLowest) || (toHighest < fromHighest);
+        // Get the closest representation of [toLowest, toHighest] in type
+        // FromType to use to clamp the input value before conversion.
+
+        // std::common_type<...> is used to get a value-preserving type for the
+        // top end of the range.
+        using CommonHighestType = std::common_type_t<BareToType, BareFromType>;
+
+        // std::make_signed<std::common_type<...>> is used to get a
+        // value-preserving type for the bottom end of the range, except this is
+        // a bit trickier for non-integer types like float.
+        using CommonLowestType =
+                std::conditional_t<std::numeric_limits<CommonHighestType>::is_integer,
+                                   std::make_signed_t<std::conditional_t<
+                                           std::numeric_limits<CommonHighestType>::is_integer,
+                                           CommonHighestType, int /* not used */>>,
+                                   CommonHighestType>;
+
+        // We can then compute the clamp range in a way that can be later
+        // trivially converted to either the 'from' or 'to' types, and be
+        // representabile in either.
+        static constexpr auto commonClampHighest =
+                std::min(static_cast<CommonHighestType>(fromHighest),
+                         static_cast<CommonHighestType>(toHighest));
+        static constexpr auto commonClampLowest =
+                std::max(static_cast<CommonLowestType>(fromLowest),
+                         static_cast<CommonLowestType>(toLowest));
+
+        static constexpr auto fromClampHighest = static_cast<BareFromType>(commonClampHighest);
+        static constexpr auto fromClampLowest = static_cast<BareFromType>(commonClampLowest);
+
+        // A clamp is needed only if the range we are clamping to is not the
+        // same as the range of the input.
+        static constexpr bool isClampNeeded =
+                (fromLowest != fromClampLowest) || (fromHighest != fromClampHighest);
 
         // If a clamp is not needed, the conversion is just a trivial cast.
         if (!isClampNeeded) {
-            return static_cast<ToType>(v);
+            return static_cast<BareToType>(v);
         }
 
-        // Otherwise we leverage implicit conversion to safely compare values of
-        // different types, to ensure we return a value clamped to the range of
-        // ToType.
-        return v < toLowest ? toLowest : (static_cast<ToType>(v) > toHighest ? toHighest : static_cast<ToType>(v));
+        // Note: Clang complains about the value of INT32_MAX not being
+        // convertible back to int32_t from float if this is made "constexpr",
+        // when clamping a float value to an int32_t value. This is however
+        // covered by a test case to ensure the run-time cast works correctly.
+        const auto toClampHighest = static_cast<BareToType>(commonClampHighest);
+        const auto toClampLowest = static_cast<BareToType>(commonClampLowest);
+
+        // Otherwise clamping is done by using the already computed endpoints
+        // for each type.
+        return (v <= fromClampLowest)
+                ? toClampLowest
+                : ((v >= fromClampHighest) ? toClampHighest : static_cast<BareToType>(v));
     }
 };
 
@@ -154,5 +194,10 @@
     return lhs.height < rhs.height;
 }
 
+// Defining PrintTo helps with Google Tests.
+static inline void PrintTo(const Size& size, ::std::ostream* os) {
+    *os << "Size(" << size.width << ", " << size.height << ")";
+}
+
 } // namespace ui
 } // namespace android
diff --git a/libs/ui/include_vndk/ui/FatVector.h b/libs/ui/include_vndk/ui/FatVector.h
deleted file mode 120000
index bf30166..0000000
--- a/libs/ui/include_vndk/ui/FatVector.h
+++ /dev/null
@@ -1 +0,0 @@
-../../include/ui/FatVector.h
\ No newline at end of file
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index ff55bc2..605c5a9 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -111,6 +111,7 @@
 
 cc_test {
     name: "Size_test",
+    test_suites: ["device-tests"],
     shared_libs: ["libui"],
     srcs: ["Size_test.cpp"],
     cflags: ["-Wall", "-Werror"],
diff --git a/libs/ui/tests/Size_test.cpp b/libs/ui/tests/Size_test.cpp
index 69e1ac8..38f37ad 100644
--- a/libs/ui/tests/Size_test.cpp
+++ b/libs/ui/tests/Size_test.cpp
@@ -19,8 +19,18 @@
 #include <cmath>
 #include <cstdlib>
 
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic error "-Wimplicit-int-float-conversion"
+#pragma clang diagnostic error "-Wconversion"
+#endif // __clang__
+
 #include <ui/Size.h>
 
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif // __clang__
+
 #include <gtest/gtest.h>
 
 namespace android {
@@ -176,9 +186,34 @@
 
 TEST(SizeTest, FloatRangeIsClamped) {
     ClampTest(std::numeric_limits<float>::max(), std::numeric_limits<int32_t>::max());
+    ClampTest(nexttowardf(std::numeric_limits<int32_t>::max(), std::numeric_limits<float>::max()),
+              std::numeric_limits<int32_t>::max());
+    ClampTest(static_cast<float>(std::numeric_limits<int32_t>::max()),
+              std::numeric_limits<int32_t>::max());
+    ClampTest(nexttowardf(std::numeric_limits<int32_t>::max(), 0),
+              static_cast<int32_t>(nexttowardf(std::numeric_limits<int32_t>::max(), 0)));
     ClampTest(float(0), int32_t(0));
+    ClampTest(nexttowardf(std::numeric_limits<int32_t>::lowest(), 0),
+              static_cast<int32_t>(nexttowardf(std::numeric_limits<int32_t>::lowest(), 0)));
+    ClampTest(static_cast<float>(std::numeric_limits<int32_t>::lowest()),
+              std::numeric_limits<int32_t>::lowest());
+    ClampTest(nexttowardf(std::numeric_limits<int32_t>::lowest(),
+                          std::numeric_limits<float>::lowest()),
+              std::numeric_limits<int32_t>::lowest());
     ClampTest(std::numeric_limits<float>::lowest(), std::numeric_limits<int32_t>::lowest());
 }
 
+TEST(SizeTest, Uint32RangeIsClamped) {
+    ClampTest(std::numeric_limits<uint32_t>::max(), std::numeric_limits<int32_t>::max());
+    ClampTest(std::numeric_limits<uint32_t>::max() - 1, std::numeric_limits<int32_t>::max());
+    ClampTest(static_cast<uint32_t>(std::numeric_limits<int32_t>::max()) + 1,
+              std::numeric_limits<int32_t>::max());
+    ClampTest(static_cast<uint32_t>(std::numeric_limits<int32_t>::max()),
+              std::numeric_limits<int32_t>::max());
+    ClampTest(static_cast<uint32_t>(std::numeric_limits<int32_t>::max()) - 1,
+              std::numeric_limits<int32_t>::max() - 1);
+    ClampTest(uint32_t(0), int32_t(0));
+}
+
 } // namespace ui
 } // namespace android
diff --git a/libs/ui/tests/TEST_MAPPING b/libs/ui/tests/TEST_MAPPING
new file mode 100644
index 0000000..7fcd7de
--- /dev/null
+++ b/libs/ui/tests/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "Size_test"
+    }
+  ]
+}
diff --git a/opengl/TEST_MAPPING b/opengl/TEST_MAPPING
new file mode 100644
index 0000000..d391dce
--- /dev/null
+++ b/opengl/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsGpuToolsHostTestCases"
+    }
+  ]
+}
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 67d69b4..a58e87c 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -158,13 +158,16 @@
             // NOTE: This is only valid if the backend is OpenGL
             attrs.push_back(EGL_PLATFORM_ANGLE_EGL_HANDLE_ANGLE);
             attrs.push_back(vendorEGL);
+
+            // Context virtualization is only available on GL back-end.
+            // Needed to support threading with GL back-end
+            attrs.push_back(EGL_PLATFORM_ANGLE_CONTEXT_VIRTUALIZATION_ANGLE);
+            attrs.push_back(EGL_FALSE);
             break;
         default:
-            ALOGV("%s: Requesting Unknown (%d) ANGLE back-end", __FUNCTION__, cnx->angleBackend);
+            ALOGE("%s: Requesting Unknown (%d) ANGLE back-end", __FUNCTION__, cnx->angleBackend);
             break;
     }
-    attrs.push_back(EGL_PLATFORM_ANGLE_CONTEXT_VIRTUALIZATION_ANGLE);
-    attrs.push_back(EGL_FALSE);
 
     return true;
 }
diff --git a/services/automotive/display/Android.bp b/services/automotive/display/Android.bp
index 8ff0711..c3da216 100644
--- a/services/automotive/display/Android.bp
+++ b/services/automotive/display/Android.bp
@@ -40,4 +40,8 @@
     cflags: [
         "-DLOG_TAG=\"AutomotiveDisplayService\""
     ],
+
+    vintf_fragments: [
+        "manifest_android.frameworks.automotive.display@1.0.xml",
+    ],
 }
diff --git a/services/automotive/display/manifest_android.frameworks.automotive.display@1.0.xml b/services/automotive/display/manifest_android.frameworks.automotive.display@1.0.xml
new file mode 100644
index 0000000..464dcac
--- /dev/null
+++ b/services/automotive/display/manifest_android.frameworks.automotive.display@1.0.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="framework">
+    <hal>
+        <name>android.frameworks.automotive.display</name>
+        <transport>hwbinder</transport>
+        <version>1.0</version>
+        <interface>
+            <name>IAutomotiveDisplayProxyService</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/services/gpuservice/gpustats/GpuStats.cpp b/services/gpuservice/gpustats/GpuStats.cpp
index d094532..231d068 100644
--- a/services/gpuservice/gpustats/GpuStats.cpp
+++ b/services/gpuservice/gpustats/GpuStats.cpp
@@ -30,16 +30,11 @@
 
 namespace android {
 
-GpuStats::GpuStats() {
-    AStatsManager_registerPullAtomCallback(android::util::GPU_STATS_GLOBAL_INFO,
-                                           GpuStats::pullAtomCallback, nullptr, this);
-    AStatsManager_registerPullAtomCallback(android::util::GPU_STATS_APP_INFO,
-                                           GpuStats::pullAtomCallback, nullptr, this);
-}
-
 GpuStats::~GpuStats() {
-    AStatsManager_unregisterPullAtomCallback(android::util::GPU_STATS_GLOBAL_INFO);
-    AStatsManager_unregisterPullAtomCallback(android::util::GPU_STATS_APP_INFO);
+    if (mStatsdRegistered) {
+        AStatsManager_clearPullAtomCallback(android::util::GPU_STATS_GLOBAL_INFO);
+        AStatsManager_clearPullAtomCallback(android::util::GPU_STATS_APP_INFO);
+    }
 }
 
 static void addLoadingCount(GpuStatsInfo::Driver driver, bool isDriverLoaded,
@@ -97,6 +92,7 @@
     ATRACE_CALL();
 
     std::lock_guard<std::mutex> lock(mLock);
+    registerStatsdCallbacksIfNeeded();
     ALOGV("Received:\n"
           "\tdriverPackageName[%s]\n"
           "\tdriverVersionName[%s]\n"
@@ -150,6 +146,7 @@
     const std::string appStatsKey = appPackageName + std::to_string(driverVersionCode);
 
     std::lock_guard<std::mutex> lock(mLock);
+    registerStatsdCallbacksIfNeeded();
     if (!mAppStats.count(appStatsKey)) {
         return;
     }
@@ -179,6 +176,16 @@
     mGlobalStats[0].glesVersion = property_get_int32("ro.opengles.version", 0);
 }
 
+void GpuStats::registerStatsdCallbacksIfNeeded() {
+    if (!mStatsdRegistered) {
+        AStatsManager_setPullAtomCallback(android::util::GPU_STATS_GLOBAL_INFO, nullptr,
+                                         GpuStats::pullAtomCallback, this);
+        AStatsManager_setPullAtomCallback(android::util::GPU_STATS_APP_INFO, nullptr,
+                                         GpuStats::pullAtomCallback, this);
+        mStatsdRegistered = true;
+    }
+}
+
 void GpuStats::dump(const Vector<String16>& args, std::string* result) {
     ATRACE_CALL();
 
diff --git a/services/gpuservice/gpustats/include/gpustats/GpuStats.h b/services/gpuservice/gpustats/include/gpustats/GpuStats.h
index 8ca4e4e..55f0da1 100644
--- a/services/gpuservice/gpustats/include/gpustats/GpuStats.h
+++ b/services/gpuservice/gpustats/include/gpustats/GpuStats.h
@@ -30,7 +30,6 @@
 
 class GpuStats {
 public:
-    GpuStats();
     ~GpuStats();
 
     // Insert new gpu driver stats into global stats and app stats.
@@ -66,12 +65,16 @@
     void dumpAppLocked(std::string* result);
     // Append cpuVulkanVersion and glesVersion to system driver stats
     void interceptSystemDriverStatsLocked();
+    // Registers statsd callbacks if they have not already been registered
+    void registerStatsdCallbacksIfNeeded();
 
     // Below limits the memory usage of GpuStats to be less than 10KB. This is
     // the preferred number for statsd while maintaining nice data quality.
     static const size_t MAX_NUM_APP_RECORDS = 100;
     // GpuStats access should be guarded by mLock.
     std::mutex mLock;
+    // True if statsd callbacks have been registered.
+    bool mStatsdRegistered = false;
     // Key is driver version code.
     std::unordered_map<uint64_t, GpuStatsGlobalInfo> mGlobalStats;
     // Key is <app package name>+<driver version code>.
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 4ec4e25..f67c9d0 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -52,7 +52,6 @@
         "libstatslog",
         "libutils",
         "libui",
-        "server_configurable_flags",
     ],
 }
 
diff --git a/services/inputflinger/BlockingQueue.h b/services/inputflinger/BlockingQueue.h
index db9f26e..b612ca7 100644
--- a/services/inputflinger/BlockingQueue.h
+++ b/services/inputflinger/BlockingQueue.h
@@ -45,10 +45,7 @@
     T pop() {
         std::unique_lock lock(mLock);
         android::base::ScopedLockAssertion assumeLock(mLock);
-        mHasElements.wait(lock, [this]{
-                android::base::ScopedLockAssertion assumeLock(mLock);
-                return !this->mQueue.empty();
-        });
+        mHasElements.wait(lock, [this]() REQUIRES(mLock) { return !this->mQueue.empty(); });
         T t = std::move(mQueue.front());
         mQueue.erase(mQueue.begin());
         return t;
diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp
index ae9a348..77a0716 100644
--- a/services/inputflinger/InputClassifier.cpp
+++ b/services/inputflinger/InputClassifier.cpp
@@ -27,7 +27,6 @@
 #if defined(__linux__)
     #include <pthread.h>
 #endif
-#include <server_configurable_flags/get_flags.h>
 #include <unordered_set>
 
 #include <android/hardware/input/classifier/1.0/IInputClassifier.h>
@@ -46,13 +45,6 @@
 
 namespace android {
 
-static constexpr bool DEBUG = false;
-
-// Category (=namespace) name for the input settings that are applied at boot time
-static const char* INPUT_NATIVE_BOOT = "input_native_boot";
-// Feature flag name for the deep press feature
-static const char* DEEP_PRESS_ENABLED = "deep_press_enabled";
-
 //Max number of elements to store in mEvents.
 static constexpr size_t MAX_EVENTS = 5;
 
@@ -79,20 +71,6 @@
     return args.source == AINPUT_SOURCE_TOUCHPAD || args.source == AINPUT_SOURCE_TOUCHSCREEN;
 }
 
-// Check if the "deep touch" feature is on.
-static bool deepPressEnabled() {
-    std::string flag_value = server_configurable_flags::GetServerConfigurableFlag(
-            INPUT_NATIVE_BOOT, DEEP_PRESS_ENABLED, "true");
-    std::transform(flag_value.begin(), flag_value.end(), flag_value.begin(), ::tolower);
-    if (flag_value == "1" || flag_value == "true") {
-        ALOGI("Deep press feature enabled.");
-        return true;
-    }
-    ALOGI("Deep press feature is not enabled.");
-    return false;
-}
-
-
 // --- ClassifierEvent ---
 
 ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args) :
@@ -141,53 +119,40 @@
 
 // --- MotionClassifier ---
 
-MotionClassifier::MotionClassifier(sp<android::hardware::hidl_death_recipient> deathRecipient) :
-        mDeathRecipient(deathRecipient), mEvents(MAX_EVENTS) {
-    mHalThread = std::thread(&MotionClassifier::callInputClassifierHal, this);
-#if defined(__linux__)
-    // Set the thread name for debugging
-    pthread_setname_np(mHalThread.native_handle(), "InputClassifier");
-#endif
-}
-
-/**
- * This function may block for some time to initialize the HAL, so it should only be called
- * from the "InputClassifier HAL" thread.
- */
-bool MotionClassifier::init() {
-    ensureHalThread(__func__);
-    sp<android::hardware::input::classifier::V1_0::IInputClassifier> service =
-            classifier::V1_0::IInputClassifier::getService();
-    if (!service) {
-        // Not really an error, maybe the device does not have this HAL,
-        // but somehow the feature flag is flipped
-        ALOGI("Could not obtain InputClassifier HAL");
-        return false;
-    }
-
-    sp<android::hardware::hidl_death_recipient> recipient = mDeathRecipient.promote();
-    if (recipient != nullptr) {
-        const bool linked = service->linkToDeath(recipient, 0 /* cookie */).withDefault(false);
-        if (!linked) {
-            ALOGE("Could not link MotionClassifier to the HAL death");
-            return false;
-        }
-    }
-
+MotionClassifier::MotionClassifier(
+        sp<android::hardware::input::classifier::V1_0::IInputClassifier> service)
+      : mEvents(MAX_EVENTS), mService(service) {
     // Under normal operation, we do not need to reset the HAL here. But in the case where system
     // crashed, but HAL didn't, we may be connecting to an existing HAL process that might already
     // have received events in the past. That means, that HAL could be in an inconsistent state
     // once it receives events from the newly created MotionClassifier.
     mEvents.push(ClassifierEvent::createHalResetEvent());
 
-    {
-        std::scoped_lock lock(mLock);
-        if (mService) {
-            ALOGE("MotionClassifier::%s should only be called once", __func__);
-        }
-        mService = service;
+    mHalThread = std::thread(&MotionClassifier::processEvents, this);
+#if defined(__linux__)
+    // Set the thread name for debugging
+    pthread_setname_np(mHalThread.native_handle(), "InputClassifier");
+#endif
+}
+
+std::unique_ptr<MotionClassifierInterface> MotionClassifier::create(
+        sp<android::hardware::hidl_death_recipient> deathRecipient) {
+    sp<android::hardware::input::classifier::V1_0::IInputClassifier> service =
+            classifier::V1_0::IInputClassifier::getService();
+    if (!service) {
+        // Not really an error, maybe the device does not have this HAL,
+        // but somehow the feature flag is flipped
+        ALOGI("Could not obtain InputClassifier HAL");
+        return nullptr;
     }
-    return true;
+
+    const bool linked = service->linkToDeath(deathRecipient, 0 /* cookie */).withDefault(false);
+    if (!linked) {
+        ALOGE("Could not link death recipient to the HAL death");
+        return nullptr;
+    }
+    // Using 'new' to access a non-public constructor
+    return std::unique_ptr<MotionClassifier>(new MotionClassifier(service));
 }
 
 MotionClassifier::~MotionClassifier() {
@@ -195,14 +160,6 @@
     mHalThread.join();
 }
 
-void MotionClassifier::ensureHalThread(const char* function) {
-    if (DEBUG) {
-        if (std::this_thread::get_id() != mHalThread.get_id()) {
-            LOG_FATAL("Function %s should only be called from InputClassifier thread", function);
-        }
-    }
-}
-
 /**
  * Obtain the classification from the HAL for a given MotionEvent.
  * Should only be called from the InputClassifier thread (mHalThread).
@@ -213,23 +170,7 @@
  * To remove any possibility of negatively affecting the touch latency, the HAL
  * is called from a dedicated thread.
  */
-void MotionClassifier::callInputClassifierHal() {
-    ensureHalThread(__func__);
-    const bool initialized = init();
-    if (!initialized) {
-        // MotionClassifier no longer useful.
-        // Deliver death notification from a separate thread
-        // because ~MotionClassifier may be invoked, which calls mHalThread.join()
-        std::thread([deathRecipient = mDeathRecipient](){
-                sp<android::hardware::hidl_death_recipient> recipient = deathRecipient.promote();
-                if (recipient != nullptr) {
-                    recipient->serviceDied(0 /*cookie*/, nullptr);
-                }
-        }).detach();
-        return;
-    }
-    // From this point on, mService is guaranteed to be non-null.
-
+void MotionClassifier::processEvents() {
     while (true) {
         ClassifierEvent event = mEvents.pop();
         bool halResponseOk = true;
@@ -250,7 +191,7 @@
             case ClassifierEventType::DEVICE_RESET: {
                 const int32_t deviceId = *(event.getDeviceId());
                 halResponseOk = mService->resetDevice(deviceId).isOk();
-                setClassification(deviceId, MotionClassification::NONE);
+                clearDeviceState(deviceId);
                 break;
             }
             case ClassifierEventType::HAL_RESET: {
@@ -321,6 +262,12 @@
     mClassifications[deviceId] = MotionClassification::NONE;
 }
 
+void MotionClassifier::clearDeviceState(int32_t deviceId) {
+    std::scoped_lock lock(mLock);
+    mClassifications.erase(deviceId);
+    mLastDownTimes.erase(deviceId);
+}
+
 MotionClassification MotionClassifier::classify(const NotifyMotionArgs& args) {
     if ((args.action & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_DOWN) {
         updateLastDownTime(args.deviceId, args.downTime);
@@ -383,24 +330,41 @@
     }
 }
 
+// --- HalDeathRecipient
+
+InputClassifier::HalDeathRecipient::HalDeathRecipient(InputClassifier& parent) : mParent(parent) {}
+
+void InputClassifier::HalDeathRecipient::serviceDied(
+        uint64_t cookie, const wp<android::hidl::base::V1_0::IBase>& who) {
+    sp<android::hidl::base::V1_0::IBase> service = who.promote();
+    if (service) {
+        service->unlinkToDeath(this);
+    }
+    mParent.setMotionClassifier(nullptr);
+}
 
 // --- InputClassifier ---
 
-InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener) :
-        mListener(listener) {
-    // The rest of the initialization is done in onFirstRef, because we need to obtain
-    // an sp to 'this' in order to register for HAL death notifications
-}
+InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener)
+      : mListener(listener), mHalDeathRecipient(new HalDeathRecipient(*this)) {}
 
-void InputClassifier::onFirstRef() {
-    if (!deepPressEnabled()) {
-        // If feature is not enabled, MotionClassifier should stay null to avoid unnecessary work.
-        // When MotionClassifier is null, InputClassifier will forward all events
-        // to the next InputListener, unmodified.
-        return;
+void InputClassifier::setMotionClassifierEnabled(bool enabled) {
+    if (enabled) {
+        ALOGI("Enabling motion classifier");
+        if (mInitializeMotionClassifierThread.joinable()) {
+            mInitializeMotionClassifierThread.join();
+        }
+        mInitializeMotionClassifierThread = std::thread(
+                [this] { setMotionClassifier(MotionClassifier::create(mHalDeathRecipient)); });
+#if defined(__linux__)
+        // Set the thread name for debugging
+        pthread_setname_np(mInitializeMotionClassifierThread.native_handle(),
+                           "Create MotionClassifier");
+#endif
+    } else {
+        ALOGI("Disabling motion classifier");
+        setMotionClassifier(nullptr);
     }
-    std::scoped_lock lock(mLock);
-    mMotionClassifier = std::make_unique<MotionClassifier>(this);
 }
 
 void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
@@ -441,21 +405,15 @@
     mListener->notifyDeviceReset(args);
 }
 
-void InputClassifier::serviceDied(uint64_t /*cookie*/,
-        const wp<android::hidl::base::V1_0::IBase>& who) {
+void InputClassifier::setMotionClassifier(
+        std::unique_ptr<MotionClassifierInterface> motionClassifier) {
     std::scoped_lock lock(mLock);
-    ALOGE("InputClassifier HAL has died. Setting mMotionClassifier to null");
-    mMotionClassifier = nullptr;
-    sp<android::hidl::base::V1_0::IBase> service = who.promote();
-    if (service) {
-        service->unlinkToDeath(this);
-    }
+    mMotionClassifier = std::move(motionClassifier);
 }
 
 void InputClassifier::dump(std::string& dump) {
     std::scoped_lock lock(mLock);
     dump += "Input Classifier State:\n";
-
     dump += INDENT1 "Motion Classifier:\n";
     if (mMotionClassifier) {
         mMotionClassifier->dump(dump);
@@ -465,4 +423,10 @@
     dump += "\n";
 }
 
+InputClassifier::~InputClassifier() {
+    if (mInitializeMotionClassifierThread.joinable()) {
+        mInitializeMotionClassifierThread.join();
+    }
+}
+
 } // namespace android
diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h
index 47e20db..03510a6 100644
--- a/services/inputflinger/InputClassifier.h
+++ b/services/inputflinger/InputClassifier.h
@@ -19,8 +19,8 @@
 
 #include <android-base/thread_annotations.h>
 #include <utils/RefBase.h>
-#include <unordered_map>
 #include <thread>
+#include <unordered_map>
 
 #include "BlockingQueue.h"
 #include "InputListener.h"
@@ -90,6 +90,7 @@
  */
 class InputClassifierInterface : public virtual RefBase, public InputListenerInterface {
 public:
+    virtual void setMotionClassifierEnabled(bool enabled) = 0;
     /**
      * Dump the state of the input classifier.
      * This method may be called on any thread (usually by the input manager).
@@ -113,23 +114,23 @@
  */
 class MotionClassifier final : public MotionClassifierInterface {
 public:
-    /**
-     * The deathRecipient will be subscribed to the HAL death. If the death recipient
-     * owns MotionClassifier and receives HAL death, it should delete its copy of it.
-     * The callback serviceDied will also be sent if the MotionClassifier itself fails
-     * to initialize. If the MotionClassifier fails to initialize, it is not useful, and
-     * should be deleted.
-     * If no death recipient is supplied, then the registration step will be skipped, so there will
-     * be no listeners registered for the HAL death. This is useful for testing
-     * MotionClassifier in isolation.
+    /*
+     * Create an instance of MotionClassifier.
+     * The death recipient, if provided, will be subscribed to the HAL death.
+     * The death recipient could be used to destroy MotionClassifier.
+     *
+     * This function should be called asynchronously, because getService takes a long time.
      */
-    explicit MotionClassifier(sp<android::hardware::hidl_death_recipient> deathRecipient = nullptr);
+    static std::unique_ptr<MotionClassifierInterface> create(
+            sp<android::hardware::hidl_death_recipient> deathRecipient);
+
     ~MotionClassifier();
 
     /**
      * Classifies events asynchronously; that is, it doesn't block events on a classification,
-     * but instead sends them over to the classifier HAL and after a classification is
-     * determined, it then marks the next event it sees in the stream with it.
+     * but instead sends them over to the classifier HAL. After a classification of a specific
+     * event is determined, MotionClassifier then marks the next event in the stream with this
+     * classification.
      *
      * Therefore, it is acceptable to have the classifications be delayed by 1-2 events
      * in a particular gesture.
@@ -141,15 +142,9 @@
     virtual void dump(std::string& dump) override;
 
 private:
-    /**
-     * Initialize MotionClassifier.
-     * Return true if initializaion is successful.
-     */
-    bool init();
-    /**
-     * Entity that will be notified of the HAL death (most likely InputClassifier).
-     */
-    wp<android::hardware::hidl_death_recipient> mDeathRecipient;
+    friend class MotionClassifierTest; // to create MotionClassifier with a test HAL implementation
+    explicit MotionClassifier(
+            sp<android::hardware::input::classifier::V1_0::IInputClassifier> service);
 
     // The events that need to be sent to the HAL.
     BlockingQueue<ClassifierEvent> mEvents;
@@ -164,14 +159,9 @@
      */
     std::thread mHalThread;
     /**
-     * Print an error message if the caller is not on the InputClassifier thread.
-     * Caller must supply the name of the calling function as __func__
+     * Process events and call the InputClassifier HAL
      */
-    void ensureHalThread(const char* function);
-    /**
-     * Call the InputClassifier HAL
-     */
-    void callInputClassifierHal();
+    void processEvents();
     /**
      * Access to the InputClassifier HAL. May be null if init() hasn't completed yet.
      * When init() successfully completes, mService is guaranteed to remain non-null and to not
@@ -212,6 +202,8 @@
 
     void updateLastDownTime(int32_t deviceId, nsecs_t downTime);
 
+    void clearDeviceState(int32_t deviceId);
+
     /**
      * Exit the InputClassifier HAL thread.
      * Useful for tests to ensure proper cleanup.
@@ -223,19 +215,15 @@
     const char* getServiceStatus() REQUIRES(mLock);
 };
 
-
 /**
  * Implementation of the InputClassifierInterface.
  * Represents a separate stage of input processing. All of the input events go through this stage.
  * Acts as a passthrough for all input events except for motion events.
  * The events of motion type are sent to MotionClassifier.
  */
-class InputClassifier : public InputClassifierInterface,
-        public android::hardware::hidl_death_recipient {
+class InputClassifier : public InputClassifierInterface {
 public:
     explicit InputClassifier(const sp<InputListenerInterface>& listener);
-    // Some of the constructor logic is finished in onFirstRef
-    virtual void onFirstRef() override;
 
     virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
     virtual void notifyKey(const NotifyKeyArgs* args) override;
@@ -243,17 +231,47 @@
     virtual void notifySwitch(const NotifySwitchArgs* args) override;
     virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
 
-    virtual void serviceDied(uint64_t cookie,
-            const wp<android::hidl::base::V1_0::IBase>& who) override;
-
     virtual void dump(std::string& dump) override;
 
+    ~InputClassifier();
+
+    // Called from InputManager
+    virtual void setMotionClassifierEnabled(bool enabled) override;
+
 private:
     // Protect access to mMotionClassifier, since it may become null via a hidl callback
     std::mutex mLock;
-    std::unique_ptr<MotionClassifierInterface> mMotionClassifier GUARDED_BY(mLock);
     // The next stage to pass input events to
     sp<InputListenerInterface> mListener;
+
+    std::unique_ptr<MotionClassifierInterface> mMotionClassifier GUARDED_BY(mLock);
+    std::thread mInitializeMotionClassifierThread;
+    /**
+     * Set the value of mMotionClassifier.
+     * This is called from 2 different threads:
+     * 1) mInitializeMotionClassifierThread, when we have constructed a MotionClassifier
+     * 2) A binder thread of the HalDeathRecipient, which is created when HAL dies. This would cause
+     *    mMotionClassifier to become nullptr.
+     */
+    void setMotionClassifier(std::unique_ptr<MotionClassifierInterface> motionClassifier);
+
+    /**
+     * The deathRecipient will call setMotionClassifier(null) when the HAL dies.
+     */
+    class HalDeathRecipient : public android::hardware::hidl_death_recipient {
+    public:
+        explicit HalDeathRecipient(InputClassifier& parent);
+        virtual void serviceDied(uint64_t cookie,
+                                 const wp<android::hidl::base::V1_0::IBase>& who) override;
+
+    private:
+        InputClassifier& mParent;
+    };
+    // We retain a reference to death recipient, because the death recipient will be calling
+    // ~MotionClassifier if the HAL dies.
+    // If we don't retain a reference, and MotionClassifier is the only owner of the death
+    // recipient, the serviceDied call will cause death recipient to call its own destructor.
+    sp<HalDeathRecipient> mHalDeathRecipient;
 };
 
 } // namespace android
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index c7c61cf..fc771a2 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -132,4 +132,8 @@
     mDispatcher->unregisterInputChannel(channel);
 }
 
+void InputManager::setMotionClassifierEnabled(bool enabled) {
+    mClassifier->setMotionClassifierEnabled(enabled);
+}
+
 } // namespace android
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 586097f..0158441 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -100,6 +100,8 @@
     virtual void registerInputChannel(const sp<InputChannel>& channel);
     virtual void unregisterInputChannel(const sp<InputChannel>& channel);
 
+    void setMotionClassifierEnabled(bool enabled);
+
 private:
     sp<InputReaderInterface> mReader;
 
diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputClassifier_test.cpp
index 40086ef..ab74a04 100644
--- a/services/inputflinger/tests/InputClassifier_test.cpp
+++ b/services/inputflinger/tests/InputClassifier_test.cpp
@@ -22,6 +22,9 @@
 #include <android/hardware/input/classifier/1.0/IInputClassifier.h>
 
 using namespace android::hardware::input;
+using android::hardware::Return;
+using android::hardware::Void;
+using android::hardware::input::common::V1_0::Classification;
 
 namespace android {
 
@@ -132,6 +135,49 @@
     ASSERT_EQ(args, outArgs);
 }
 
+TEST_F(InputClassifierTest, SetMotionClassifier_Enabled) {
+    mClassifier->setMotionClassifierEnabled(true);
+}
+
+TEST_F(InputClassifierTest, SetMotionClassifier_Disabled) {
+    mClassifier->setMotionClassifierEnabled(false);
+}
+
+/**
+ * Try to break it by calling setMotionClassifierEnabled multiple times.
+ */
+TEST_F(InputClassifierTest, SetMotionClassifier_Multiple) {
+    mClassifier->setMotionClassifierEnabled(true);
+    mClassifier->setMotionClassifierEnabled(true);
+    mClassifier->setMotionClassifierEnabled(true);
+    mClassifier->setMotionClassifierEnabled(false);
+    mClassifier->setMotionClassifierEnabled(false);
+    mClassifier->setMotionClassifierEnabled(true);
+    mClassifier->setMotionClassifierEnabled(true);
+    mClassifier->setMotionClassifierEnabled(true);
+}
+
+/**
+ * A minimal implementation of IInputClassifier.
+ */
+struct TestHal : public android::hardware::input::classifier::V1_0::IInputClassifier {
+    Return<Classification> classify(
+            const android::hardware::input::common::V1_0::MotionEvent& event) override {
+        return Classification::NONE;
+    };
+    Return<void> reset() override { return Void(); };
+    Return<void> resetDevice(int32_t deviceId) override { return Void(); };
+};
+
+/**
+ * An entity that will be subscribed to the HAL death.
+ */
+class TestDeathRecipient : public android::hardware::hidl_death_recipient {
+public:
+    virtual void serviceDied(uint64_t cookie,
+                             const wp<android::hidl::base::V1_0::IBase>& who) override{};
+};
+
 // --- MotionClassifierTest ---
 
 class MotionClassifierTest : public testing::Test {
@@ -139,7 +185,14 @@
     std::unique_ptr<MotionClassifierInterface> mMotionClassifier;
 
     virtual void SetUp() override {
-        mMotionClassifier = std::make_unique<MotionClassifier>();
+        mMotionClassifier = MotionClassifier::create(new TestDeathRecipient());
+        if (mMotionClassifier == nullptr) {
+            // If the device running this test does not have IInputClassifier service,
+            // use the test HAL instead.
+            // Using 'new' to access non-public constructor
+            mMotionClassifier =
+                    std::unique_ptr<MotionClassifier>(new MotionClassifier(new TestHal()));
+        }
     }
 };
 
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index d30c4f1..1f283b1 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -1445,7 +1445,6 @@
 
     ASSERT_EQ(keyArgs.action, verifiedKey.action);
     ASSERT_EQ(keyArgs.downTime, verifiedKey.downTimeNanos);
-    ASSERT_EQ(keyArgs.eventTime, verifiedKey.eventTimeNanos);
     ASSERT_EQ(keyArgs.flags & VERIFIED_KEY_EVENT_FLAGS, verifiedKey.flags);
     ASSERT_EQ(keyArgs.keyCode, verifiedKey.keyCode);
     ASSERT_EQ(keyArgs.scanCode, verifiedKey.scanCode);
@@ -1453,6 +1452,43 @@
     ASSERT_EQ(0, verifiedKey.repeatCount);
 }
 
+TEST_F(InputDispatcherTest, VerifyInputEvent_MotionEvent) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
+
+    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+    mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+    NotifyMotionArgs motionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyMotion(&motionArgs);
+
+    InputEvent* event = window->consume();
+    ASSERT_NE(event, nullptr);
+
+    std::unique_ptr<VerifiedInputEvent> verified = mDispatcher->verifyInputEvent(*event);
+    ASSERT_NE(verified, nullptr);
+    ASSERT_EQ(verified->type, VerifiedInputEvent::Type::MOTION);
+
+    EXPECT_EQ(motionArgs.eventTime, verified->eventTimeNanos);
+    EXPECT_EQ(motionArgs.deviceId, verified->deviceId);
+    EXPECT_EQ(motionArgs.source, verified->source);
+    EXPECT_EQ(motionArgs.displayId, verified->displayId);
+
+    const VerifiedMotionEvent& verifiedMotion = static_cast<const VerifiedMotionEvent&>(*verified);
+
+    EXPECT_EQ(motionArgs.pointerCoords[0].getX(), verifiedMotion.rawX);
+    EXPECT_EQ(motionArgs.pointerCoords[0].getY(), verifiedMotion.rawY);
+    EXPECT_EQ(motionArgs.action & AMOTION_EVENT_ACTION_MASK, verifiedMotion.actionMasked);
+    EXPECT_EQ(motionArgs.downTime, verifiedMotion.downTimeNanos);
+    EXPECT_EQ(motionArgs.flags & VERIFIED_MOTION_EVENT_FLAGS, verifiedMotion.flags);
+    EXPECT_EQ(motionArgs.metaState, verifiedMotion.metaState);
+    EXPECT_EQ(motionArgs.buttonState, verifiedMotion.buttonState);
+}
+
 class InputDispatcherKeyRepeatTest : public InputDispatcherTest {
 protected:
     static constexpr nsecs_t KEY_REPEAT_TIMEOUT = 40 * 1000000; // 40 ms
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 3ae8b56..618aefc 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -1748,7 +1748,7 @@
 
     virtual void SetUp() override {
         mFakePolicy = new FakeInputReaderPolicy();
-        mTestListener = new TestInputListener();
+        mTestListener = new TestInputListener(50ms);
 
         mReader = new InputReader(std::make_shared<EventHub>(), mFakePolicy, mTestListener);
         ASSERT_EQ(mReader->start(), OK);
@@ -1847,6 +1847,135 @@
     ASSERT_LE(prevTimestamp, keyArgs.eventTime);
 }
 
+// --- TouchProcessTest ---
+class TouchIntegrationTest : public InputReaderIntegrationTest {
+protected:
+    static const int32_t FIRST_SLOT = 0;
+    static const int32_t SECOND_SLOT = 1;
+    static const int32_t FIRST_TRACKING_ID = 0;
+    static const int32_t SECOND_TRACKING_ID = 1;
+    const std::string UNIQUE_ID = "local:0";
+
+    virtual void SetUp() override {
+        InputReaderIntegrationTest::SetUp();
+        // At least add an internal display.
+        setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+                                     DISPLAY_ORIENTATION_0, UNIQUE_ID, NO_PORT,
+                                     ViewportType::VIEWPORT_INTERNAL);
+
+        mDevice = createUinputDevice<UinputTouchScreen>(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT));
+        ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+        ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+    }
+
+    void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
+                                      int32_t orientation, const std::string& uniqueId,
+                                      std::optional<uint8_t> physicalPort,
+                                      ViewportType viewportType) {
+        mFakePolicy->addDisplayViewport(displayId, width, height, orientation, uniqueId,
+                                        physicalPort, viewportType);
+        mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    }
+
+    std::unique_ptr<UinputTouchScreen> mDevice;
+};
+
+TEST_F(TouchIntegrationTest, InputEvent_ProcessSingleTouch) {
+    NotifyMotionArgs args;
+    const Point centerPoint = mDevice->getCenterPoint();
+
+    // ACTION_DOWN
+    mDevice->sendDown(centerPoint);
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
+
+    // ACTION_MOVE
+    mDevice->sendMove(centerPoint + Point(1, 1));
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+
+    // ACTION_UP
+    mDevice->sendUp();
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
+}
+
+TEST_F(TouchIntegrationTest, InputEvent_ProcessMultiTouch) {
+    NotifyMotionArgs args;
+    const Point centerPoint = mDevice->getCenterPoint();
+
+    // ACTION_DOWN
+    mDevice->sendDown(centerPoint);
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
+
+    // ACTION_POINTER_DOWN (Second slot)
+    const Point secondPoint = centerPoint + Point(100, 100);
+    mDevice->sendSlot(SECOND_SLOT);
+    mDevice->sendTrackingId(SECOND_TRACKING_ID);
+    mDevice->sendDown(secondPoint + Point(1, 1));
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+              args.action);
+
+    // ACTION_MOVE (Second slot)
+    mDevice->sendMove(secondPoint);
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+
+    // ACTION_POINTER_UP (Second slot)
+    mDevice->sendUp();
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+              args.action);
+
+    // ACTION_UP
+    mDevice->sendSlot(FIRST_SLOT);
+    mDevice->sendUp();
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
+}
+
+TEST_F(TouchIntegrationTest, InputEvent_ProcessPalm) {
+    NotifyMotionArgs args;
+    const Point centerPoint = mDevice->getCenterPoint();
+
+    // ACTION_DOWN
+    mDevice->sendDown(centerPoint);
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
+
+    // ACTION_POINTER_DOWN (Second slot)
+    const Point secondPoint = centerPoint + Point(100, 100);
+    mDevice->sendSlot(SECOND_SLOT);
+    mDevice->sendTrackingId(SECOND_TRACKING_ID);
+    mDevice->sendDown(secondPoint);
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+              args.action);
+
+    // ACTION_MOVE (Second slot)
+    mDevice->sendMove(secondPoint + Point(1, 1));
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+
+    // Send MT_TOOL_PALM, which indicates that the touch IC has determined this to be a grip event.
+    // Expect to receive ACTION_CANCEL, to abort the entire gesture.
+    mDevice->sendToolType(MT_TOOL_PALM);
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, args.action);
+
+    // ACTION_POINTER_UP (Second slot)
+    mDevice->sendUp();
+
+    // ACTION_UP
+    mDevice->sendSlot(FIRST_SLOT);
+    mDevice->sendUp();
+
+    // Expect no event received after abort the entire gesture.
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled());
+}
+
 // --- InputDeviceTest ---
 class InputDeviceTest : public testing::Test {
 protected:
@@ -7032,5 +7161,4 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId);
 }
-
 } // namespace android
diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp
index 06b05ac..86ff3b1 100644
--- a/services/inputflinger/tests/TestInputListener.cpp
+++ b/services/inputflinger/tests/TestInputListener.cpp
@@ -19,20 +19,11 @@
 
 #include "TestInputListener.h"
 
-namespace {
-
-using std::chrono_literals::operator""ms;
-
-// Timeout for waiting for an expected event
-static constexpr std::chrono::duration WAIT_TIMEOUT = 5ms;
-
-} // namespace
-
 namespace android {
 
 // --- TestInputListener ---
 
-TestInputListener::TestInputListener() { }
+TestInputListener::TestInputListener(const std::chrono::milliseconds timeout) : mTimeout(timeout) {}
 
 TestInputListener::~TestInputListener() { }
 
@@ -95,9 +86,9 @@
 
     std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues);
     if (queue.empty()) {
-        const bool eventReceived =
-                mCondition.wait_for(lock, WAIT_TIMEOUT,
-                                    [&queue]() REQUIRES(mLock) { return !queue.empty(); });
+        const bool eventReceived = mCondition.wait_for(lock, mTimeout, [&queue]() REQUIRES(mLock) {
+            return !queue.empty();
+        });
         if (!eventReceived) {
             FAIL() << "Timed out waiting for event: " << message.c_str();
         }
@@ -114,7 +105,7 @@
     base::ScopedLockAssertion assumeLocked(mLock);
 
     std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues);
-    const bool eventReceived = mCondition.wait_for(lock, WAIT_TIMEOUT, [&queue]() REQUIRES(mLock) {
+    const bool eventReceived = mCondition.wait_for(lock, mTimeout, [&queue]() REQUIRES(mLock) {
         return !queue.empty();
     });
     if (eventReceived) {
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
index 945e2ea..4262f5a 100644
--- a/services/inputflinger/tests/TestInputListener.h
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -21,6 +21,8 @@
 #include <gtest/gtest.h>
 #include "InputListener.h"
 
+using std::chrono_literals::operator""ms;
+
 namespace android {
 
 // --- TestInputListener ---
@@ -30,7 +32,7 @@
     virtual ~TestInputListener();
 
 public:
-    TestInputListener();
+    TestInputListener(const std::chrono::milliseconds timeout = 5ms);
 
     void assertNotifyConfigurationChangedWasCalled(
             NotifyConfigurationChangedArgs* outEventArgs = nullptr);
@@ -73,6 +75,7 @@
 
     std::mutex mLock;
     std::condition_variable mCondition;
+    const std::chrono::milliseconds mTimeout;
 
     std::tuple<std::vector<NotifyConfigurationChangedArgs>, //
                std::vector<NotifyDeviceResetArgs>,          //
diff --git a/services/inputflinger/tests/UinputDevice.cpp b/services/inputflinger/tests/UinputDevice.cpp
index 2775d21..99480b7 100644
--- a/services/inputflinger/tests/UinputDevice.cpp
+++ b/services/inputflinger/tests/UinputDevice.cpp
@@ -119,7 +119,7 @@
     EXPECT_NO_FATAL_FAILURE(releaseKey(key));
 }
 
-// --- UinputHomeKey---
+// --- UinputHomeKey ---
 
 UinputHomeKey::UinputHomeKey() : UinputKeyboard({KEY_HOME}) {}
 
@@ -127,4 +127,71 @@
     EXPECT_NO_FATAL_FAILURE(pressAndReleaseKey(KEY_HOME));
 }
 
+// --- UinputTouchScreen ---
+UinputTouchScreen::UinputTouchScreen(const Rect* size)
+      : UinputDevice(UinputTouchScreen::DEVICE_NAME), mSize(*size) {}
+
+void UinputTouchScreen::configureDevice(int fd, uinput_user_dev* device) {
+    // Setup the touch screen device
+    ioctl(fd, UI_SET_EVBIT, EV_KEY);
+    ioctl(fd, UI_SET_EVBIT, EV_REL);
+    ioctl(fd, UI_SET_EVBIT, EV_ABS);
+    ioctl(fd, UI_SET_ABSBIT, ABS_MT_SLOT);
+    ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOUCH_MAJOR);
+    ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_X);
+    ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_Y);
+    ioctl(fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID);
+    ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOOL_TYPE);
+    ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
+    ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH);
+
+    device->absmin[ABS_MT_SLOT] = RAW_SLOT_MIN;
+    device->absmax[ABS_MT_SLOT] = RAW_SLOT_MAX;
+    device->absmin[ABS_MT_TOUCH_MAJOR] = RAW_TOUCH_MIN;
+    device->absmax[ABS_MT_TOUCH_MAJOR] = RAW_TOUCH_MAX;
+    device->absmin[ABS_MT_POSITION_X] = mSize.left;
+    device->absmax[ABS_MT_POSITION_X] = mSize.right - 1;
+    device->absmin[ABS_MT_POSITION_Y] = mSize.top;
+    device->absmax[ABS_MT_POSITION_Y] = mSize.bottom - 1;
+    device->absmin[ABS_MT_TRACKING_ID] = RAW_ID_MIN;
+    device->absmax[ABS_MT_TRACKING_ID] = RAW_ID_MAX;
+}
+
+void UinputTouchScreen::sendSlot(int32_t slot) {
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_SLOT, slot));
+}
+
+void UinputTouchScreen::sendTrackingId(int32_t trackingId) {
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_TRACKING_ID, trackingId));
+}
+
+void UinputTouchScreen::sendDown(const Point& point) {
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_KEY, BTN_TOUCH, 1));
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_POSITION_X, point.x));
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_POSITION_Y, point.y));
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0));
+}
+
+void UinputTouchScreen::sendMove(const Point& point) {
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_POSITION_X, point.x));
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_POSITION_Y, point.y));
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0));
+}
+
+void UinputTouchScreen::sendUp() {
+    sendTrackingId(0xffffffff);
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_KEY, BTN_TOUCH, 0));
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0));
+}
+
+void UinputTouchScreen::sendToolType(int32_t toolType) {
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_TOOL_TYPE, toolType));
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0));
+}
+
+// Get the center x, y base on the range definition.
+const Point UinputTouchScreen::getCenterPoint() {
+    return Point(mSize.left + mSize.width() / 2, mSize.top + mSize.height() / 2);
+}
+
 } // namespace android
diff --git a/services/inputflinger/tests/UinputDevice.h b/services/inputflinger/tests/UinputDevice.h
index 57d9011..ec3cd9f 100644
--- a/services/inputflinger/tests/UinputDevice.h
+++ b/services/inputflinger/tests/UinputDevice.h
@@ -22,6 +22,8 @@
 #include <inttypes.h>
 #include <linux/uinput.h>
 #include <log/log.h>
+#include <ui/Point.h>
+#include <ui/Rect.h>
 
 #include <memory>
 
@@ -106,6 +108,40 @@
     UinputHomeKey();
 };
 
+// --- UinputTouchScreen ---
+// A touch screen device with specific size.
+class UinputTouchScreen : public UinputDevice {
+public:
+    static constexpr const char* DEVICE_NAME = "Test Touch Screen";
+    static const int32_t RAW_TOUCH_MIN = 0;
+    static const int32_t RAW_TOUCH_MAX = 31;
+    static const int32_t RAW_ID_MIN = 0;
+    static const int32_t RAW_ID_MAX = 9;
+    static const int32_t RAW_SLOT_MIN = 0;
+    static const int32_t RAW_SLOT_MAX = 9;
+    static const int32_t RAW_PRESSURE_MIN = 0;
+    static const int32_t RAW_PRESSURE_MAX = 255;
+
+    template <class D, class... Ts>
+    friend std::unique_ptr<D> createUinputDevice(Ts... args);
+
+    void sendSlot(int32_t slot);
+    void sendTrackingId(int32_t trackingId);
+    void sendDown(const Point& point);
+    void sendMove(const Point& point);
+    void sendUp();
+    void sendToolType(int32_t toolType);
+
+    const Point getCenterPoint();
+
+protected:
+    UinputTouchScreen(const Rect* size);
+
+private:
+    void configureDevice(int fd, uinput_user_dev* device) override;
+    const Rect mSize;
+};
+
 } // namespace android
 
 #endif // _UI_TEST_INPUT_UINPUT_INJECTOR_H
diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp
index 3e0f136..b0d3e3b 100644
--- a/services/powermanager/Android.bp
+++ b/services/powermanager/Android.bp
@@ -9,7 +9,7 @@
     ],
 
     aidl: {
-       local_include_dirs: ["."],
+       local_include_dirs: ["include"],
        include_dirs: [
            "frameworks/base/core/java/android/os",
        ],
@@ -28,6 +28,11 @@
         "-Wunused",
         "-Wunreachable-code",
     ],
+
+    local_include_dirs: ["include"],
+    export_include_dirs: [
+         "include",
+    ],
 }
 
 cc_test {
diff --git a/include/android/CoolingDevice.h b/services/powermanager/include/android/CoolingDevice.h
similarity index 100%
rename from include/android/CoolingDevice.h
rename to services/powermanager/include/android/CoolingDevice.h
diff --git a/include/android/Temperature.h b/services/powermanager/include/android/Temperature.h
similarity index 100%
rename from include/android/Temperature.h
rename to services/powermanager/include/android/Temperature.h
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 08d7d3f..c6f1f7e 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -52,6 +52,7 @@
         "libpdx_default_transport",
         "libprocessgroup",
         "libprotobuf-cpp-lite",
+        "libstatslog",
         "libsync",
         "libtimestats",
         "libui",
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index d7ec868..e4d754c 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -485,6 +485,10 @@
         }
     }
 
+    if (recomputeVisibleRegions == true) {
+        maybeDirtyInput();
+    }
+
     return true;
 }
 
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 5e04d95..e50a909 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -443,10 +443,7 @@
 }
 
 void BufferLayerConsumer::onDisconnect() {
-    sp<Layer> l = mLayer.promote();
-    if (l.get()) {
-        l->onDisconnect();
-    }
+    mLayer->onDisconnect();
 }
 
 void BufferLayerConsumer::onSidebandStreamChanged() {
@@ -480,10 +477,7 @@
 
 void BufferLayerConsumer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
                                                    FrameEventHistoryDelta* outDelta) {
-    sp<Layer> l = mLayer.promote();
-    if (l.get()) {
-        l->addAndGetFrameTimestamps(newTimestamps, outDelta);
-    }
+    mLayer->addAndGetFrameTimestamps(newTimestamps, outDelta);
 }
 
 void BufferLayerConsumer::abandonLocked() {
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
index 39ed370..c71a1d9 100644
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ b/services/surfaceflinger/BufferLayerConsumer.h
@@ -332,7 +332,7 @@
     const uint32_t mTexName;
 
     // The layer for this BufferLayerConsumer
-    const wp<Layer> mLayer;
+    Layer* mLayer;
 
     wp<ContentsChangedListener> mContentsChangedListener;
 
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index f5a99ca..4e5c593 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -346,8 +346,6 @@
     mPreviousBufferId = getCurrentBufferId();
     mBufferInfo.mBuffer =
             mConsumer->getCurrentBuffer(&mBufferInfo.mBufferSlot, &mBufferInfo.mFence);
-    auto* layerCompositionState = editCompositionState();
-    layerCompositionState->buffer = mBufferInfo.mBuffer;
 
     if (mBufferInfo.mBuffer == nullptr) {
         // this can only happen if the very first buffer was rejected.
@@ -441,7 +439,7 @@
         mQueueItemCondition.broadcast();
     }
 
-    mFlinger->mInterceptor->saveBufferUpdate(this, item.mGraphicBuffer->getWidth(),
+    mFlinger->mInterceptor->saveBufferUpdate(layerId, item.mGraphicBuffer->getWidth(),
                                              item.mGraphicBuffer->getHeight(), item.mFrameNumber);
 
     mFlinger->signalLayerUpdate();
@@ -564,8 +562,6 @@
     mBufferInfo.mApi = mConsumer->getCurrentApi();
     mBufferInfo.mPixelFormat = mFormat;
     mBufferInfo.mTransformToDisplayInverse = mConsumer->getTransformToDisplayInverse();
-    float latchedFrameRate;
-    mConsumer->getFrameRate(&latchedFrameRate);
 }
 
 sp<Layer> BufferQueueLayer::createClone() {
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index de5429b..3ed6889 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -616,7 +616,6 @@
     mPreviousBufferId = getCurrentBufferId();
     mBufferInfo.mBuffer = s.buffer;
     mBufferInfo.mFence = s.acquireFence;
-    editCompositionState()->buffer = mBufferInfo.mBuffer;
 
     return NO_ERROR;
 }
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 2792290..fda451b 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -104,6 +104,7 @@
     static_libs: [
         "libcompositionengine",
         "libcompositionengine_mocks",
+        "libgui_mocks",
         "librenderengine_mocks",
         "libgmock",
         "libgtest",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
index ced4227..6bc677d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
@@ -18,6 +18,11 @@
 
 #include <cstdint>
 #include <optional>
+#include <string>
+
+#include <ui/DisplayInfo.h>
+#include <ui/PixelFormat.h>
+#include <ui/Size.h>
 
 #include "DisplayHardware/DisplayIdentification.h"
 #include "DisplayHardware/PowerAdvisor.h"
@@ -30,47 +35,86 @@
  * A parameter object for creating Display instances
  */
 struct DisplayCreationArgs {
-    // True if this display is a virtual display
-    bool isVirtual = false;
+    struct Physical {
+        DisplayId id;
+        DisplayConnectionType type;
+    };
 
-    // Identifies the display to the HWC, if composition is supported by it
-    std::optional<DisplayId> displayId;
+    // Required for physical displays. Gives the HWC display id for the existing
+    // display along with the connection type.
+    std::optional<Physical> physical;
+
+    // Size of the display in pixels
+    ui::Size pixels = ui::Size::INVALID;
+
+    // Pixel format of the display
+    ui::PixelFormat pixelFormat = static_cast<ui::PixelFormat>(PIXEL_FORMAT_UNKNOWN);
+
+    // True if virtual displays should be created with the HWC API if possible
+    bool useHwcVirtualDisplays = false;
+
+    // True if this display should be considered secure
+    bool isSecure = false;
+
+    // Gives the initial layer stack id to be used for the display
+    uint32_t layerStackId = ~0u;
 
     // Optional pointer to the power advisor interface, if one is needed for
     // this display.
     Hwc2::PowerAdvisor* powerAdvisor = nullptr;
+
+    // Debugging. Human readable name for the display.
+    std::string name;
 };
 
 /**
  * A helper for setting up a DisplayCreationArgs value in-line.
  * Prefer this builder over raw structure initialization.
- *
- * Instead of:
- *
- *   DisplayCreationArgs{false, false, displayId}
- *
- * Prefer:
- *
- *  DisplayCreationArgsBuilder().setIsVirtual(false)
- *      .setDisplayId(displayId).build();
  */
 class DisplayCreationArgsBuilder {
 public:
     DisplayCreationArgs build() { return std::move(mArgs); }
 
-    DisplayCreationArgsBuilder& setIsVirtual(bool isVirtual) {
-        mArgs.isVirtual = isVirtual;
+    DisplayCreationArgsBuilder& setPhysical(DisplayCreationArgs::Physical physical) {
+        mArgs.physical = physical;
         return *this;
     }
-    DisplayCreationArgsBuilder& setDisplayId(std::optional<DisplayId> displayId) {
-        mArgs.displayId = displayId;
+
+    DisplayCreationArgsBuilder& setPixels(ui::Size pixels) {
+        mArgs.pixels = pixels;
         return *this;
     }
+
+    DisplayCreationArgsBuilder& setPixelFormat(ui::PixelFormat pixelFormat) {
+        mArgs.pixelFormat = pixelFormat;
+        return *this;
+    }
+
+    DisplayCreationArgsBuilder& setUseHwcVirtualDisplays(bool useHwcVirtualDisplays) {
+        mArgs.useHwcVirtualDisplays = useHwcVirtualDisplays;
+        return *this;
+    }
+
+    DisplayCreationArgsBuilder& setIsSecure(bool isSecure) {
+        mArgs.isSecure = isSecure;
+        return *this;
+    }
+
+    DisplayCreationArgsBuilder& setLayerStackId(uint32_t layerStackId) {
+        mArgs.layerStackId = layerStackId;
+        return *this;
+    }
+
     DisplayCreationArgsBuilder& setPowerAdvisor(Hwc2::PowerAdvisor* powerAdvisor) {
         mArgs.powerAdvisor = powerAdvisor;
         return *this;
     }
 
+    DisplayCreationArgsBuilder& setName(std::string name) {
+        mArgs.name = std::move(name);
+        return *this;
+    }
+
 private:
     DisplayCreationArgs mArgs;
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index fb597ce..9ca7d2f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -16,12 +16,16 @@
 
 #pragma once
 
-#include <compositionengine/Display.h>
-#include <compositionengine/DisplayCreationArgs.h>
-#include <compositionengine/impl/Output.h>
-
 #include <memory>
 
+#include <compositionengine/Display.h>
+#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/DisplayCreationArgs.h>
+#include <compositionengine/RenderSurface.h>
+#include <compositionengine/impl/Output.h>
+#include <ui/PixelFormat.h>
+#include <ui/Size.h>
+
 #include "DisplayHardware/DisplayIdentification.h"
 #include "DisplayHardware/HWComposer.h"
 #include "DisplayHardware/PowerAdvisor.h"
@@ -36,11 +40,11 @@
 // actually contain the final display state.
 class Display : public compositionengine::impl::Output, public virtual compositionengine::Display {
 public:
-    explicit Display(const compositionengine::DisplayCreationArgs&);
     virtual ~Display();
 
     // compositionengine::Output overrides
     std::optional<DisplayId> getDisplayId() const override;
+    bool isValid() const override;
     void dump(std::string&) const override;
     using compositionengine::impl::Output::setReleasedLayers;
     void setReleasedLayers(const CompositionRefreshArgs&) override;
@@ -73,22 +77,33 @@
     virtual void applyLayerRequestsToLayers(const LayerRequests&);
 
     // Internal
+    virtual void setConfiguration(const compositionengine::DisplayCreationArgs&);
+    virtual std::optional<DisplayId> maybeAllocateDisplayIdForVirtualDisplay(ui::Size,
+                                                                             ui::PixelFormat) const;
     std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const;
 
+    // Testing
+    void setDisplayIdForTesting(std::optional<DisplayId> displayId);
+
 private:
-    const bool mIsVirtual;
+    bool mIsVirtual = false;
     std::optional<DisplayId> mId;
-    Hwc2::PowerAdvisor* const mPowerAdvisor{nullptr};
+    Hwc2::PowerAdvisor* mPowerAdvisor = nullptr;
 };
 
 // This template factory function standardizes the implementation details of the
 // final class using the types actually required by the implementation. This is
 // not possible to do in the base class as those types may not even be visible
 // to the base code.
-template <typename BaseDisplay, typename CompositionEngine, typename DisplayCreationArgs>
-std::shared_ptr<BaseDisplay> createDisplayTemplated(const CompositionEngine& compositionEngine,
-                                                    const DisplayCreationArgs& args) {
-    return createOutputTemplated<BaseDisplay>(compositionEngine, args);
+template <typename BaseDisplay, typename CompositionEngine>
+std::shared_ptr<BaseDisplay> createDisplayTemplated(
+        const CompositionEngine& compositionEngine,
+        const compositionengine::DisplayCreationArgs& args) {
+    auto display = createOutputTemplated<BaseDisplay>(compositionEngine);
+
+    display->setConfiguration(args);
+
+    return display;
 }
 
 std::shared_ptr<Display> createDisplay(const compositionengine::CompositionEngine&,
diff --git a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
index acaaf4e..2d9f01b 100644
--- a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
@@ -35,7 +35,8 @@
     return lhs.geometry == rhs.geometry && lhs.alpha == rhs.alpha &&
             lhs.sourceDataspace == rhs.sourceDataspace &&
             lhs.colorTransform == rhs.colorTransform &&
-            lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow;
+            lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow &&
+            lhs.backgroundBlurRadius == rhs.backgroundBlurRadius;
 }
 
 inline bool equalIgnoringBuffer(const renderengine::Buffer& lhs, const renderengine::Buffer& rhs) {
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 1d8a23f..c345405 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -47,11 +47,36 @@
     return createDisplayTemplated<Display>(compositionEngine, args);
 }
 
-Display::Display(const DisplayCreationArgs& args)
-      : mIsVirtual(args.isVirtual), mId(args.displayId), mPowerAdvisor(args.powerAdvisor) {}
-
 Display::~Display() = default;
 
+void Display::setConfiguration(const compositionengine::DisplayCreationArgs& args) {
+    mIsVirtual = !args.physical;
+    mId = args.physical ? std::make_optional(args.physical->id) : std::nullopt;
+    mPowerAdvisor = args.powerAdvisor;
+
+    editState().isSecure = args.isSecure;
+
+    setLayerStackFilter(args.layerStackId,
+                        args.physical ? args.physical->type == DisplayConnectionType::Internal
+                                      : false);
+    setName(args.name);
+
+    if (!args.physical && args.useHwcVirtualDisplays) {
+        mId = maybeAllocateDisplayIdForVirtualDisplay(args.pixels, args.pixelFormat);
+    }
+}
+
+std::optional<DisplayId> Display::maybeAllocateDisplayIdForVirtualDisplay(
+        ui::Size pixels, ui::PixelFormat pixelFormat) const {
+    auto& hwc = getCompositionEngine().getHwComposer();
+    return hwc.allocateVirtualDisplay(static_cast<uint32_t>(pixels.width),
+                                      static_cast<uint32_t>(pixels.height), &pixelFormat);
+}
+
+bool Display::isValid() const {
+    return Output::isValid() && mPowerAdvisor;
+}
+
 const std::optional<DisplayId>& Display::getId() const {
     return mId;
 }
@@ -68,6 +93,10 @@
     return mId;
 }
 
+void Display::setDisplayIdForTesting(std::optional<DisplayId> displayId) {
+    mId = displayId;
+}
+
 void Display::disconnect() {
     if (!mId) {
         return;
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 16f7a4e..88f2686 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -30,6 +30,8 @@
 #include <compositionengine/mock/OutputLayer.h>
 #include <compositionengine/mock/RenderSurface.h>
 #include <gtest/gtest.h>
+#include <ui/DisplayInfo.h>
+#include <ui/Rect.h>
 
 #include "MockHWC2.h"
 #include "MockHWComposer.h"
@@ -40,8 +42,11 @@
 
 using testing::_;
 using testing::DoAll;
+using testing::Eq;
 using testing::InSequence;
 using testing::NiceMock;
+using testing::Pointee;
+using testing::Ref;
 using testing::Return;
 using testing::ReturnRef;
 using testing::Sequence;
@@ -49,8 +54,10 @@
 using testing::StrictMock;
 
 constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{42};
+constexpr DisplayId VIRTUAL_DISPLAY_ID = DisplayId{43};
 constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1920;
 constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1080;
+constexpr int32_t DEFAULT_LAYER_STACK = 123;
 
 struct Layer {
     Layer() {
@@ -73,25 +80,126 @@
     StrictMock<mock::OutputLayer>* outputLayer = new StrictMock<mock::OutputLayer>();
 };
 
-struct DisplayTest : public testing::Test {
-    class Display : public impl::Display {
+struct DisplayTestCommon : public testing::Test {
+    // Uses the full implementation of a display
+    class FullImplDisplay : public impl::Display {
     public:
-        explicit Display(const compositionengine::DisplayCreationArgs& args)
-              : impl::Display(args) {}
-
         using impl::Display::injectOutputLayerForTest;
         virtual void injectOutputLayerForTest(std::unique_ptr<compositionengine::OutputLayer>) = 0;
+
+        using impl::Display::maybeAllocateDisplayIdForVirtualDisplay;
     };
 
+    // Uses a special implementation with key internal member functions set up
+    // as mock implementations, to allow for easier testing.
+    struct PartialMockDisplay : public impl::Display {
+        PartialMockDisplay(const compositionengine::CompositionEngine& compositionEngine)
+              : mCompositionEngine(compositionEngine) {}
+
+        // compositionengine::Output overrides
+        const OutputCompositionState& getState() const override { return mState; }
+        OutputCompositionState& editState() override { return mState; }
+
+        // compositionengine::impl::Output overrides
+        const CompositionEngine& getCompositionEngine() const override {
+            return mCompositionEngine;
+        };
+
+        // Mock implementation overrides
+        MOCK_CONST_METHOD0(getOutputLayerCount, size_t());
+        MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex,
+                           compositionengine::OutputLayer*(size_t));
+        MOCK_METHOD2(ensureOutputLayer,
+                     compositionengine::OutputLayer*(std::optional<size_t>, const sp<LayerFE>&));
+        MOCK_METHOD0(finalizePendingOutputLayers, void());
+        MOCK_METHOD0(clearOutputLayers, void());
+        MOCK_CONST_METHOD1(dumpState, void(std::string&));
+        MOCK_METHOD1(injectOutputLayerForTest, compositionengine::OutputLayer*(const sp<LayerFE>&));
+        MOCK_METHOD1(injectOutputLayerForTest, void(std::unique_ptr<OutputLayer>));
+        MOCK_CONST_METHOD0(anyLayersRequireClientComposition, bool());
+        MOCK_CONST_METHOD0(allLayersRequireClientComposition, bool());
+        MOCK_METHOD1(applyChangedTypesToLayers, void(const impl::Display::ChangedTypes&));
+        MOCK_METHOD1(applyDisplayRequests, void(const impl::Display::DisplayRequests&));
+        MOCK_METHOD1(applyLayerRequestsToLayers, void(const impl::Display::LayerRequests&));
+
+        const compositionengine::CompositionEngine& mCompositionEngine;
+        impl::OutputCompositionState mState;
+    };
+
+    static std::string getDisplayNameFromCurrentTest() {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        return std::string("display for ") + test_info->test_case_name() + "." + test_info->name();
+    }
+
+    template <typename Display>
     static std::shared_ptr<Display> createDisplay(
             const compositionengine::CompositionEngine& compositionEngine,
-            compositionengine::DisplayCreationArgs&& args) {
+            compositionengine::DisplayCreationArgs args) {
+        args.name = getDisplayNameFromCurrentTest();
         return impl::createDisplayTemplated<Display>(compositionEngine, args);
     }
 
-    DisplayTest() {
-        EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+    template <typename Display>
+    static std::shared_ptr<StrictMock<Display>> createPartialMockDisplay(
+            const compositionengine::CompositionEngine& compositionEngine,
+            compositionengine::DisplayCreationArgs args) {
+        args.name = getDisplayNameFromCurrentTest();
+        auto display = std::make_shared<StrictMock<Display>>(compositionEngine);
 
+        display->setConfiguration(args);
+
+        return display;
+    }
+
+    DisplayTestCommon() {
+        EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+    }
+
+    DisplayCreationArgs getDisplayCreationArgsForPhysicalHWCDisplay() {
+        return DisplayCreationArgsBuilder()
+                .setPhysical({DEFAULT_DISPLAY_ID, DisplayConnectionType::Internal})
+                .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
+                .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
+                .setIsSecure(true)
+                .setLayerStackId(DEFAULT_LAYER_STACK)
+                .setPowerAdvisor(&mPowerAdvisor)
+                .build();
+    }
+
+    DisplayCreationArgs getDisplayCreationArgsForNonHWCVirtualDisplay() {
+        return DisplayCreationArgsBuilder()
+                .setUseHwcVirtualDisplays(false)
+                .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
+                .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
+                .setIsSecure(false)
+                .setLayerStackId(DEFAULT_LAYER_STACK)
+                .setPowerAdvisor(&mPowerAdvisor)
+                .build();
+    }
+
+    StrictMock<android::mock::HWComposer> mHwComposer;
+    StrictMock<Hwc2::mock::PowerAdvisor> mPowerAdvisor;
+    StrictMock<mock::CompositionEngine> mCompositionEngine;
+    sp<mock::NativeWindow> mNativeWindow = new StrictMock<mock::NativeWindow>();
+};
+
+struct PartialMockDisplayTestCommon : public DisplayTestCommon {
+    using Display = DisplayTestCommon::PartialMockDisplay;
+    std::shared_ptr<Display> mDisplay =
+            createPartialMockDisplay<Display>(mCompositionEngine,
+                                              getDisplayCreationArgsForPhysicalHWCDisplay());
+};
+
+struct FullDisplayImplTestCommon : public DisplayTestCommon {
+    using Display = DisplayTestCommon::FullImplDisplay;
+    std::shared_ptr<Display> mDisplay =
+            createDisplay<Display>(mCompositionEngine,
+                                   getDisplayCreationArgsForPhysicalHWCDisplay());
+};
+
+struct DisplayWithLayersTestCommon : public FullDisplayImplTestCommon {
+    DisplayWithLayersTestCommon() {
         mDisplay->injectOutputLayerForTest(
                 std::unique_ptr<compositionengine::OutputLayer>(mLayer1.outputLayer));
         mDisplay->injectOutputLayerForTest(
@@ -100,65 +208,166 @@
                 std::unique_ptr<compositionengine::OutputLayer>(mLayer3.outputLayer));
     }
 
-    StrictMock<android::mock::HWComposer> mHwComposer;
-    StrictMock<Hwc2::mock::PowerAdvisor> mPowerAdvisor;
-    StrictMock<mock::CompositionEngine> mCompositionEngine;
-    sp<mock::NativeWindow> mNativeWindow = new StrictMock<mock::NativeWindow>();
     Layer mLayer1;
     Layer mLayer2;
     LayerNoHWC2Layer mLayer3;
     StrictMock<HWC2::mock::Layer> hwc2LayerUnknown;
-
-    std::shared_ptr<Display> mDisplay = createDisplay(mCompositionEngine,
-                                                      DisplayCreationArgsBuilder()
-                                                              .setDisplayId(DEFAULT_DISPLAY_ID)
-                                                              .setPowerAdvisor(&mPowerAdvisor)
-                                                              .build());
+    std::shared_ptr<Display> mDisplay =
+            createDisplay<Display>(mCompositionEngine,
+                                   getDisplayCreationArgsForPhysicalHWCDisplay());
 };
 
 /*
  * Basic construction
  */
 
-TEST_F(DisplayTest, canInstantiateDisplay) {
-    {
-        constexpr DisplayId display1 = DisplayId{123u};
-        auto display =
-                impl::createDisplay(mCompositionEngine,
-                                    DisplayCreationArgsBuilder().setDisplayId(display1).build());
-        EXPECT_FALSE(display->isSecure());
-        EXPECT_FALSE(display->isVirtual());
-        EXPECT_EQ(display1, display->getId());
-    }
+struct DisplayCreationTest : public DisplayTestCommon {
+    using Display = DisplayTestCommon::FullImplDisplay;
+};
 
-    {
-        constexpr DisplayId display2 = DisplayId{546u};
-        auto display =
-                impl::createDisplay(mCompositionEngine,
-                                    DisplayCreationArgsBuilder().setDisplayId(display2).build());
-        EXPECT_FALSE(display->isSecure());
-        EXPECT_FALSE(display->isVirtual());
-        EXPECT_EQ(display2, display->getId());
-    }
+TEST_F(DisplayCreationTest, createPhysicalInternalDisplay) {
+    auto display =
+            impl::createDisplay(mCompositionEngine, getDisplayCreationArgsForPhysicalHWCDisplay());
+    EXPECT_TRUE(display->isSecure());
+    EXPECT_FALSE(display->isVirtual());
+    EXPECT_EQ(DEFAULT_DISPLAY_ID, display->getId());
+}
 
-    {
-        constexpr DisplayId display3 = DisplayId{789u};
-        auto display = impl::createDisplay(mCompositionEngine,
-                                           DisplayCreationArgsBuilder()
-                                                   .setIsVirtual(true)
-                                                   .setDisplayId(display3)
-                                                   .build());
-        EXPECT_FALSE(display->isSecure());
-        EXPECT_TRUE(display->isVirtual());
-        EXPECT_EQ(display3, display->getId());
-    }
+TEST_F(DisplayCreationTest, createNonHwcVirtualDisplay) {
+    auto display = impl::createDisplay(mCompositionEngine,
+                                       getDisplayCreationArgsForNonHWCVirtualDisplay());
+    EXPECT_FALSE(display->isSecure());
+    EXPECT_TRUE(display->isVirtual());
+    EXPECT_EQ(std::nullopt, display->getId());
+}
+
+/*
+ * Display::setConfiguration()
+ */
+
+using DisplaySetConfigurationTest = PartialMockDisplayTestCommon;
+
+TEST_F(DisplaySetConfigurationTest, configuresInternalSecurePhysicalDisplay) {
+    mDisplay->setConfiguration(
+            DisplayCreationArgsBuilder()
+                    .setUseHwcVirtualDisplays(true)
+                    .setPhysical({DEFAULT_DISPLAY_ID, DisplayConnectionType::Internal})
+                    .setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH))
+                    .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
+                    .setIsSecure(true)
+                    .setLayerStackId(DEFAULT_LAYER_STACK)
+                    .setPowerAdvisor(&mPowerAdvisor)
+                    .setName(getDisplayNameFromCurrentTest())
+                    .build());
+
+    EXPECT_EQ(DEFAULT_DISPLAY_ID, mDisplay->getId());
+    EXPECT_TRUE(mDisplay->isSecure());
+    EXPECT_FALSE(mDisplay->isVirtual());
+    EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
+    EXPECT_TRUE(mDisplay->getState().layerStackInternal);
+    EXPECT_FALSE(mDisplay->isValid());
+}
+
+TEST_F(DisplaySetConfigurationTest, configuresExternalInsecurePhysicalDisplay) {
+    mDisplay->setConfiguration(
+            DisplayCreationArgsBuilder()
+                    .setUseHwcVirtualDisplays(true)
+                    .setPhysical({DEFAULT_DISPLAY_ID, DisplayConnectionType::External})
+                    .setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH))
+                    .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
+                    .setIsSecure(false)
+                    .setLayerStackId(DEFAULT_LAYER_STACK)
+                    .setPowerAdvisor(&mPowerAdvisor)
+                    .setName(getDisplayNameFromCurrentTest())
+                    .build());
+
+    EXPECT_EQ(DEFAULT_DISPLAY_ID, mDisplay->getId());
+    EXPECT_FALSE(mDisplay->isSecure());
+    EXPECT_FALSE(mDisplay->isVirtual());
+    EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
+    EXPECT_FALSE(mDisplay->getState().layerStackInternal);
+    EXPECT_FALSE(mDisplay->isValid());
+}
+
+TEST_F(DisplaySetConfigurationTest, configuresHwcBackedVirtualDisplay) {
+    EXPECT_CALL(mHwComposer,
+                allocateVirtualDisplay(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH,
+                                       Pointee(Eq(static_cast<ui::PixelFormat>(
+                                               PIXEL_FORMAT_RGBA_8888)))))
+            .WillOnce(Return(VIRTUAL_DISPLAY_ID));
+
+    mDisplay->setConfiguration(
+            DisplayCreationArgsBuilder()
+                    .setUseHwcVirtualDisplays(true)
+                    .setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH))
+                    .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
+                    .setIsSecure(false)
+                    .setLayerStackId(DEFAULT_LAYER_STACK)
+                    .setPowerAdvisor(&mPowerAdvisor)
+                    .setName(getDisplayNameFromCurrentTest())
+                    .build());
+
+    EXPECT_EQ(VIRTUAL_DISPLAY_ID, mDisplay->getId());
+    EXPECT_FALSE(mDisplay->isSecure());
+    EXPECT_TRUE(mDisplay->isVirtual());
+    EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
+    EXPECT_FALSE(mDisplay->getState().layerStackInternal);
+    EXPECT_FALSE(mDisplay->isValid());
+}
+
+TEST_F(DisplaySetConfigurationTest, configuresNonHwcBackedVirtualDisplayIfHwcAllocationFails) {
+    EXPECT_CALL(mHwComposer,
+                allocateVirtualDisplay(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH,
+                                       Pointee(Eq(static_cast<ui::PixelFormat>(
+                                               PIXEL_FORMAT_RGBA_8888)))))
+            .WillOnce(Return(std::nullopt));
+
+    mDisplay->setConfiguration(
+            DisplayCreationArgsBuilder()
+                    .setUseHwcVirtualDisplays(true)
+                    .setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH))
+                    .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
+                    .setIsSecure(false)
+                    .setLayerStackId(DEFAULT_LAYER_STACK)
+                    .setPowerAdvisor(&mPowerAdvisor)
+                    .setName(getDisplayNameFromCurrentTest())
+                    .build());
+
+    EXPECT_EQ(std::nullopt, mDisplay->getId());
+    EXPECT_FALSE(mDisplay->isSecure());
+    EXPECT_TRUE(mDisplay->isVirtual());
+    EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
+    EXPECT_FALSE(mDisplay->getState().layerStackInternal);
+    EXPECT_FALSE(mDisplay->isValid());
+}
+
+TEST_F(DisplaySetConfigurationTest, configuresNonHwcBackedVirtualDisplayIfShouldNotUseHwc) {
+    mDisplay->setConfiguration(
+            DisplayCreationArgsBuilder()
+                    .setUseHwcVirtualDisplays(false)
+                    .setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH))
+                    .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
+                    .setIsSecure(false)
+                    .setLayerStackId(DEFAULT_LAYER_STACK)
+                    .setPowerAdvisor(&mPowerAdvisor)
+                    .setName(getDisplayNameFromCurrentTest())
+                    .build());
+
+    EXPECT_EQ(std::nullopt, mDisplay->getId());
+    EXPECT_FALSE(mDisplay->isSecure());
+    EXPECT_TRUE(mDisplay->isVirtual());
+    EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
+    EXPECT_FALSE(mDisplay->getState().layerStackInternal);
+    EXPECT_FALSE(mDisplay->isValid());
 }
 
 /*
  * Display::disconnect()
  */
 
-TEST_F(DisplayTest, disconnectDisconnectsDisplay) {
+using DisplayDisconnectTest = PartialMockDisplayTestCommon;
+
+TEST_F(DisplayDisconnectTest, disconnectsDisplay) {
     // The first call to disconnect will disconnect the display with the HWC and
     // set mHwcId to -1.
     EXPECT_CALL(mHwComposer, disconnectDisplay(DEFAULT_DISPLAY_ID)).Times(1);
@@ -175,7 +384,9 @@
  * Display::setColorTransform()
  */
 
-TEST_F(DisplayTest, setColorTransformSetsTransform) {
+using DisplaySetColorTransformTest = PartialMockDisplayTestCommon;
+
+TEST_F(DisplaySetColorTransformTest, setsTransform) {
     // No change does nothing
     CompositionRefreshArgs refreshArgs;
     refreshArgs.colorTransformMatrix = std::nullopt;
@@ -202,7 +413,9 @@
  * Display::setColorMode()
  */
 
-TEST_F(DisplayTest, setColorModeSetsModeUnlessNoChange) {
+using DisplaySetColorModeTest = PartialMockDisplayTestCommon;
+
+TEST_F(DisplaySetColorModeTest, setsModeUnlessNoChange) {
     using ColorProfile = Output::ColorProfile;
 
     mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
@@ -245,11 +458,11 @@
     EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().targetDataspace);
 }
 
-TEST_F(DisplayTest, setColorModeDoesNothingForVirtualDisplay) {
+TEST_F(DisplaySetColorModeTest, doesNothingForVirtualDisplay) {
     using ColorProfile = Output::ColorProfile;
 
-    std::shared_ptr<impl::Display> virtualDisplay{
-            impl::createDisplay(mCompositionEngine, DisplayCreationArgs{true, DEFAULT_DISPLAY_ID})};
+    auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
+    std::shared_ptr<impl::Display> virtualDisplay = impl::createDisplay(mCompositionEngine, args);
 
     mock::DisplayColorProfile* colorProfile = new StrictMock<mock::DisplayColorProfile>();
     virtualDisplay->setDisplayColorProfileForTest(
@@ -274,7 +487,9 @@
  * Display::createDisplayColorProfile()
  */
 
-TEST_F(DisplayTest, createDisplayColorProfileSetsDisplayColorProfile) {
+using DisplayCreateColorProfileTest = PartialMockDisplayTestCommon;
+
+TEST_F(DisplayCreateColorProfileTest, setsDisplayColorProfile) {
     EXPECT_TRUE(mDisplay->getDisplayColorProfile() == nullptr);
     mDisplay->createDisplayColorProfile(
             DisplayColorProfileCreationArgs{false, HdrCapabilities(), 0,
@@ -286,7 +501,9 @@
  * Display::createRenderSurface()
  */
 
-TEST_F(DisplayTest, createRenderSurfaceSetsRenderSurface) {
+using DisplayCreateRenderSurfaceTest = PartialMockDisplayTestCommon;
+
+TEST_F(DisplayCreateRenderSurfaceTest, setsRenderSurface) {
     EXPECT_CALL(*mNativeWindow, disconnect(NATIVE_WINDOW_API_EGL)).WillRepeatedly(Return(NO_ERROR));
     EXPECT_TRUE(mDisplay->getRenderSurface() == nullptr);
     mDisplay->createRenderSurface(RenderSurfaceCreationArgs{640, 480, mNativeWindow, nullptr});
@@ -297,7 +514,9 @@
  * Display::createOutputLayer()
  */
 
-TEST_F(DisplayTest, createOutputLayerSetsHwcLayer) {
+using DisplayCreateOutputLayerTest = FullDisplayImplTestCommon;
+
+TEST_F(DisplayCreateOutputLayerTest, setsHwcLayer) {
     sp<mock::LayerFE> layerFE = new StrictMock<mock::LayerFE>();
     StrictMock<HWC2::mock::Layer> hwcLayer;
 
@@ -315,9 +534,11 @@
  * Display::setReleasedLayers()
  */
 
-TEST_F(DisplayTest, setReleasedLayersDoesNothingIfNotHwcDisplay) {
-    std::shared_ptr<impl::Display> nonHwcDisplay{
-            impl::createDisplay(mCompositionEngine, DisplayCreationArgsBuilder().build())};
+using DisplaySetReleasedLayersTest = DisplayWithLayersTestCommon;
+
+TEST_F(DisplaySetReleasedLayersTest, doesNothingIfNotHwcDisplay) {
+    auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
+    std::shared_ptr<impl::Display> nonHwcDisplay = impl::createDisplay(mCompositionEngine, args);
 
     sp<mock::LayerFE> layerXLayerFE = new StrictMock<mock::LayerFE>();
 
@@ -336,7 +557,7 @@
     ASSERT_EQ(1, releasedLayers.size());
 }
 
-TEST_F(DisplayTest, setReleasedLayersDoesNothingIfNoLayersWithQueuedFrames) {
+TEST_F(DisplaySetReleasedLayersTest, doesNothingIfNoLayersWithQueuedFrames) {
     sp<mock::LayerFE> layerXLayerFE = new StrictMock<mock::LayerFE>();
 
     {
@@ -352,7 +573,7 @@
     ASSERT_EQ(1, releasedLayers.size());
 }
 
-TEST_F(DisplayTest, setReleasedLayers) {
+TEST_F(DisplaySetReleasedLayersTest, setReleasedLayers) {
     sp<mock::LayerFE> unknownLayer = new StrictMock<mock::LayerFE>();
 
     CompositionRefreshArgs refreshArgs;
@@ -372,59 +593,12 @@
  * Display::chooseCompositionStrategy()
  */
 
-struct DisplayChooseCompositionStrategyTest : public testing::Test {
-    struct DisplayPartialMock : public impl::Display {
-        DisplayPartialMock(const compositionengine::CompositionEngine& compositionEngine,
-                           const compositionengine::DisplayCreationArgs& args)
-              : impl::Display(args), mCompositionEngine(compositionEngine) {}
-
-        // Sets up the helper functions called by chooseCompositionStrategy to
-        // use a mock implementations.
-        MOCK_CONST_METHOD0(anyLayersRequireClientComposition, bool());
-        MOCK_CONST_METHOD0(allLayersRequireClientComposition, bool());
-        MOCK_METHOD1(applyChangedTypesToLayers, void(const impl::Display::ChangedTypes&));
-        MOCK_METHOD1(applyDisplayRequests, void(const impl::Display::DisplayRequests&));
-        MOCK_METHOD1(applyLayerRequestsToLayers, void(const impl::Display::LayerRequests&));
-
-        // compositionengine::Output overrides
-        const OutputCompositionState& getState() const override { return mState; }
-        OutputCompositionState& editState() override { return mState; }
-
-        // compositionengine::impl::Output overrides
-        const CompositionEngine& getCompositionEngine() const override {
-            return mCompositionEngine;
-        };
-
-        // These need implementations though are not expected to be called.
-        MOCK_CONST_METHOD0(getOutputLayerCount, size_t());
-        MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex,
-                           compositionengine::OutputLayer*(size_t));
-        MOCK_METHOD2(ensureOutputLayer,
-                     compositionengine::OutputLayer*(std::optional<size_t>, const sp<LayerFE>&));
-        MOCK_METHOD0(finalizePendingOutputLayers, void());
-        MOCK_METHOD0(clearOutputLayers, void());
-        MOCK_CONST_METHOD1(dumpState, void(std::string&));
-        MOCK_METHOD1(injectOutputLayerForTest, compositionengine::OutputLayer*(const sp<LayerFE>&));
-        MOCK_METHOD1(injectOutputLayerForTest, void(std::unique_ptr<OutputLayer>));
-
-        const compositionengine::CompositionEngine& mCompositionEngine;
-        impl::OutputCompositionState mState;
-    };
-
-    DisplayChooseCompositionStrategyTest() {
-        EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
-    }
-
-    StrictMock<android::mock::HWComposer> mHwComposer;
-    StrictMock<mock::CompositionEngine> mCompositionEngine;
-    StrictMock<DisplayPartialMock>
-            mDisplay{mCompositionEngine,
-                     DisplayCreationArgsBuilder().setDisplayId(DEFAULT_DISPLAY_ID).build()};
-};
+using DisplayChooseCompositionStrategyTest = PartialMockDisplayTestCommon;
 
 TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutIfNotAHwcDisplay) {
-    std::shared_ptr<impl::Display> nonHwcDisplay{
-            impl::createDisplay(mCompositionEngine, DisplayCreationArgsBuilder().build())};
+    auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
+    std::shared_ptr<Display> nonHwcDisplay =
+            createPartialMockDisplay<Display>(mCompositionEngine, args);
     EXPECT_FALSE(nonHwcDisplay->getId());
 
     nonHwcDisplay->chooseCompositionStrategy();
@@ -435,33 +609,36 @@
 }
 
 TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutOnHwcError) {
-    EXPECT_CALL(mDisplay, anyLayersRequireClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition()).WillOnce(Return(false));
     EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(DEFAULT_DISPLAY_ID, false, _))
             .WillOnce(Return(INVALID_OPERATION));
 
-    mDisplay.chooseCompositionStrategy();
+    mDisplay->chooseCompositionStrategy();
 
-    auto& state = mDisplay.getState();
+    auto& state = mDisplay->getState();
     EXPECT_TRUE(state.usesClientComposition);
     EXPECT_FALSE(state.usesDeviceComposition);
 }
 
 TEST_F(DisplayChooseCompositionStrategyTest, normalOperation) {
-    // Since two calls are made to anyLayersRequireClientComposition with different return values,
-    // use a Sequence to control the matching so the values are returned in a known order.
+    // Since two calls are made to anyLayersRequireClientComposition with different return
+    // values, use a Sequence to control the matching so the values are returned in a known
+    // order.
     Sequence s;
-    EXPECT_CALL(mDisplay, anyLayersRequireClientComposition()).InSequence(s).WillOnce(Return(true));
-    EXPECT_CALL(mDisplay, anyLayersRequireClientComposition())
+    EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition())
+            .InSequence(s)
+            .WillOnce(Return(true));
+    EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition())
             .InSequence(s)
             .WillOnce(Return(false));
 
     EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(DEFAULT_DISPLAY_ID, true, _))
             .WillOnce(Return(NO_ERROR));
-    EXPECT_CALL(mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
 
-    mDisplay.chooseCompositionStrategy();
+    mDisplay->chooseCompositionStrategy();
 
-    auto& state = mDisplay.getState();
+    auto& state = mDisplay->getState();
     EXPECT_FALSE(state.usesClientComposition);
     EXPECT_TRUE(state.usesDeviceComposition);
 }
@@ -473,24 +650,27 @@
             {{nullptr, HWC2::LayerRequest::ClearClientTarget}},
     };
 
-    // Since two calls are made to anyLayersRequireClientComposition with different return values,
-    // use a Sequence to control the matching so the values are returned in a known order.
+    // Since two calls are made to anyLayersRequireClientComposition with different return
+    // values, use a Sequence to control the matching so the values are returned in a known
+    // order.
     Sequence s;
-    EXPECT_CALL(mDisplay, anyLayersRequireClientComposition()).InSequence(s).WillOnce(Return(true));
-    EXPECT_CALL(mDisplay, anyLayersRequireClientComposition())
+    EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition())
+            .InSequence(s)
+            .WillOnce(Return(true));
+    EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition())
             .InSequence(s)
             .WillOnce(Return(false));
 
     EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(DEFAULT_DISPLAY_ID, true, _))
             .WillOnce(DoAll(SetArgPointee<2>(changes), Return(NO_ERROR)));
-    EXPECT_CALL(mDisplay, applyChangedTypesToLayers(changes.changedTypes)).Times(1);
-    EXPECT_CALL(mDisplay, applyDisplayRequests(changes.displayRequests)).Times(1);
-    EXPECT_CALL(mDisplay, applyLayerRequestsToLayers(changes.layerRequests)).Times(1);
-    EXPECT_CALL(mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(*mDisplay, applyChangedTypesToLayers(changes.changedTypes)).Times(1);
+    EXPECT_CALL(*mDisplay, applyDisplayRequests(changes.displayRequests)).Times(1);
+    EXPECT_CALL(*mDisplay, applyLayerRequestsToLayers(changes.layerRequests)).Times(1);
+    EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
 
-    mDisplay.chooseCompositionStrategy();
+    mDisplay->chooseCompositionStrategy();
 
-    auto& state = mDisplay.getState();
+    auto& state = mDisplay->getState();
     EXPECT_FALSE(state.usesClientComposition);
     EXPECT_TRUE(state.usesDeviceComposition);
 }
@@ -499,13 +679,15 @@
  * Display::getSkipColorTransform()
  */
 
-TEST_F(DisplayTest, getSkipColorTransformDoesNothingIfNonHwcDisplay) {
-    auto nonHwcDisplay{
-            impl::createDisplay(mCompositionEngine, DisplayCreationArgsBuilder().build())};
+using DisplayGetSkipColorTransformTest = DisplayWithLayersTestCommon;
+
+TEST_F(DisplayGetSkipColorTransformTest, doesNothingIfNonHwcDisplay) {
+    auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
+    auto nonHwcDisplay{impl::createDisplay(mCompositionEngine, args)};
     EXPECT_FALSE(nonHwcDisplay->getSkipColorTransform());
 }
 
-TEST_F(DisplayTest, getSkipColorTransformChecksHwcCapability) {
+TEST_F(DisplayGetSkipColorTransformTest, checksHwcCapability) {
     EXPECT_CALL(mHwComposer,
                 hasDisplayCapability(std::make_optional(DEFAULT_DISPLAY_ID),
                                      HWC2::DisplayCapability::SkipClientColorTransform))
@@ -517,7 +699,9 @@
  * Display::anyLayersRequireClientComposition()
  */
 
-TEST_F(DisplayTest, anyLayersRequireClientCompositionReturnsFalse) {
+using DisplayAnyLayersRequireClientCompositionTest = DisplayWithLayersTestCommon;
+
+TEST_F(DisplayAnyLayersRequireClientCompositionTest, returnsFalse) {
     EXPECT_CALL(*mLayer1.outputLayer, requiresClientComposition()).WillOnce(Return(false));
     EXPECT_CALL(*mLayer2.outputLayer, requiresClientComposition()).WillOnce(Return(false));
     EXPECT_CALL(*mLayer3.outputLayer, requiresClientComposition()).WillOnce(Return(false));
@@ -525,7 +709,7 @@
     EXPECT_FALSE(mDisplay->anyLayersRequireClientComposition());
 }
 
-TEST_F(DisplayTest, anyLayersRequireClientCompositionReturnsTrue) {
+TEST_F(DisplayAnyLayersRequireClientCompositionTest, returnsTrue) {
     EXPECT_CALL(*mLayer1.outputLayer, requiresClientComposition()).WillOnce(Return(false));
     EXPECT_CALL(*mLayer2.outputLayer, requiresClientComposition()).WillOnce(Return(true));
 
@@ -536,7 +720,9 @@
  * Display::allLayersRequireClientComposition()
  */
 
-TEST_F(DisplayTest, allLayersRequireClientCompositionReturnsTrue) {
+using DisplayAllLayersRequireClientCompositionTest = DisplayWithLayersTestCommon;
+
+TEST_F(DisplayAllLayersRequireClientCompositionTest, returnsTrue) {
     EXPECT_CALL(*mLayer1.outputLayer, requiresClientComposition()).WillOnce(Return(true));
     EXPECT_CALL(*mLayer2.outputLayer, requiresClientComposition()).WillOnce(Return(true));
     EXPECT_CALL(*mLayer3.outputLayer, requiresClientComposition()).WillOnce(Return(true));
@@ -544,7 +730,7 @@
     EXPECT_TRUE(mDisplay->allLayersRequireClientComposition());
 }
 
-TEST_F(DisplayTest, allLayersRequireClientCompositionReturnsFalse) {
+TEST_F(DisplayAllLayersRequireClientCompositionTest, returnsFalse) {
     EXPECT_CALL(*mLayer1.outputLayer, requiresClientComposition()).WillOnce(Return(true));
     EXPECT_CALL(*mLayer2.outputLayer, requiresClientComposition()).WillOnce(Return(false));
 
@@ -555,11 +741,13 @@
  * Display::applyChangedTypesToLayers()
  */
 
-TEST_F(DisplayTest, applyChangedTypesToLayersTakesEarlyOutIfNoChangedLayers) {
+using DisplayApplyChangedTypesToLayersTest = DisplayWithLayersTestCommon;
+
+TEST_F(DisplayApplyChangedTypesToLayersTest, takesEarlyOutIfNoChangedLayers) {
     mDisplay->applyChangedTypesToLayers(impl::Display::ChangedTypes());
 }
 
-TEST_F(DisplayTest, applyChangedTypesToLayersAppliesChanges) {
+TEST_F(DisplayApplyChangedTypesToLayersTest, appliesChanges) {
     EXPECT_CALL(*mLayer1.outputLayer,
                 applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition::CLIENT))
             .Times(1);
@@ -578,28 +766,30 @@
  * Display::applyDisplayRequests()
  */
 
-TEST_F(DisplayTest, applyDisplayRequestsToLayersHandlesNoRequests) {
+using DisplayApplyDisplayRequestsTest = DisplayWithLayersTestCommon;
+
+TEST_F(DisplayApplyDisplayRequestsTest, handlesNoRequests) {
     mDisplay->applyDisplayRequests(static_cast<HWC2::DisplayRequest>(0));
 
     auto& state = mDisplay->getState();
     EXPECT_FALSE(state.flipClientTarget);
 }
 
-TEST_F(DisplayTest, applyDisplayRequestsToLayersHandlesFlipClientTarget) {
+TEST_F(DisplayApplyDisplayRequestsTest, handlesFlipClientTarget) {
     mDisplay->applyDisplayRequests(HWC2::DisplayRequest::FlipClientTarget);
 
     auto& state = mDisplay->getState();
     EXPECT_TRUE(state.flipClientTarget);
 }
 
-TEST_F(DisplayTest, applyDisplayRequestsToLayersHandlesWriteClientTargetToOutput) {
+TEST_F(DisplayApplyDisplayRequestsTest, handlesWriteClientTargetToOutput) {
     mDisplay->applyDisplayRequests(HWC2::DisplayRequest::WriteClientTargetToOutput);
 
     auto& state = mDisplay->getState();
     EXPECT_FALSE(state.flipClientTarget);
 }
 
-TEST_F(DisplayTest, applyDisplayRequestsToLayersHandlesAllRequestFlagsSet) {
+TEST_F(DisplayApplyDisplayRequestsTest, handlesAllRequestFlagsSet) {
     mDisplay->applyDisplayRequests(static_cast<HWC2::DisplayRequest>(~0));
 
     auto& state = mDisplay->getState();
@@ -610,7 +800,9 @@
  * Display::applyLayerRequestsToLayers()
  */
 
-TEST_F(DisplayTest, applyLayerRequestsToLayersPreparesAllLayers) {
+using DisplayApplyLayerRequestsToLayersTest = DisplayWithLayersTestCommon;
+
+TEST_F(DisplayApplyLayerRequestsToLayersTest, preparesAllLayers) {
     EXPECT_CALL(*mLayer1.outputLayer, prepareForDeviceLayerRequests()).Times(1);
     EXPECT_CALL(*mLayer2.outputLayer, prepareForDeviceLayerRequests()).Times(1);
     EXPECT_CALL(*mLayer3.outputLayer, prepareForDeviceLayerRequests()).Times(1);
@@ -618,7 +810,7 @@
     mDisplay->applyLayerRequestsToLayers(impl::Display::LayerRequests());
 }
 
-TEST_F(DisplayTest, applyLayerRequestsToLayers2) {
+TEST_F(DisplayApplyLayerRequestsToLayersTest, appliesDeviceLayerRequests) {
     EXPECT_CALL(*mLayer1.outputLayer, prepareForDeviceLayerRequests()).Times(1);
     EXPECT_CALL(*mLayer2.outputLayer, prepareForDeviceLayerRequests()).Times(1);
     EXPECT_CALL(*mLayer3.outputLayer, prepareForDeviceLayerRequests()).Times(1);
@@ -637,9 +829,11 @@
  * Display::presentAndGetFrameFences()
  */
 
-TEST_F(DisplayTest, presentAndGetFrameFencesReturnsNoFencesOnNonHwcDisplay) {
-    auto nonHwcDisplay{
-            impl::createDisplay(mCompositionEngine, DisplayCreationArgsBuilder().build())};
+using DisplayPresentAndGetFrameFencesTest = DisplayWithLayersTestCommon;
+
+TEST_F(DisplayPresentAndGetFrameFencesTest, returnsNoFencesOnNonHwcDisplay) {
+    auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
+    auto nonHwcDisplay{impl::createDisplay(mCompositionEngine, args)};
 
     auto result = nonHwcDisplay->presentAndGetFrameFences();
 
@@ -648,7 +842,7 @@
     EXPECT_EQ(0u, result.layerFences.size());
 }
 
-TEST_F(DisplayTest, presentAndGetFrameFencesReturnsPresentAndLayerFences) {
+TEST_F(DisplayPresentAndGetFrameFencesTest, returnsPresentAndLayerFences) {
     sp<Fence> presentFence = new Fence();
     sp<Fence> layer1Fence = new Fence();
     sp<Fence> layer2Fence = new Fence();
@@ -676,7 +870,9 @@
  * Display::setExpensiveRenderingExpected()
  */
 
-TEST_F(DisplayTest, setExpensiveRenderingExpectedForwardsToPowerAdvisor) {
+using DisplaySetExpensiveRenderingExpectedTest = DisplayWithLayersTestCommon;
+
+TEST_F(DisplaySetExpensiveRenderingExpectedTest, forwardsToPowerAdvisor) {
     EXPECT_CALL(mPowerAdvisor, setExpensiveRenderingExpected(DEFAULT_DISPLAY_ID, true)).Times(1);
     mDisplay->setExpensiveRenderingExpected(true);
 
@@ -688,7 +884,9 @@
  * Display::finishFrame()
  */
 
-TEST_F(DisplayTest, finishFrameDoesNotSkipCompositionIfNotDirtyOnHwcDisplay) {
+using DisplayFinishFrameTest = DisplayWithLayersTestCommon;
+
+TEST_F(DisplayFinishFrameTest, doesNotSkipCompositionIfNotDirtyOnHwcDisplay) {
     mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
     mDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
 
@@ -709,9 +907,9 @@
     mDisplay->finishFrame(refreshArgs);
 }
 
-TEST_F(DisplayTest, finishFrameSkipsCompositionIfNotDirty) {
-    std::shared_ptr<impl::Display> nonHwcDisplay{
-            impl::createDisplay(mCompositionEngine, DisplayCreationArgsBuilder().build())};
+TEST_F(DisplayFinishFrameTest, skipsCompositionIfNotDirty) {
+    auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
+    std::shared_ptr<impl::Display> nonHwcDisplay = impl::createDisplay(mCompositionEngine, args);
 
     mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
     nonHwcDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
@@ -730,9 +928,9 @@
     nonHwcDisplay->finishFrame(refreshArgs);
 }
 
-TEST_F(DisplayTest, finishFramePerformsCompositionIfDirty) {
-    std::shared_ptr<impl::Display> nonHwcDisplay{
-            impl::createDisplay(mCompositionEngine, DisplayCreationArgsBuilder().build())};
+TEST_F(DisplayFinishFrameTest, performsCompositionIfDirty) {
+    auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
+    std::shared_ptr<impl::Display> nonHwcDisplay = impl::createDisplay(mCompositionEngine, args);
 
     mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
     nonHwcDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
@@ -751,9 +949,9 @@
     nonHwcDisplay->finishFrame(refreshArgs);
 }
 
-TEST_F(DisplayTest, finishFramePerformsCompositionIfRepaintEverything) {
-    std::shared_ptr<impl::Display> nonHwcDisplay{
-            impl::createDisplay(mCompositionEngine, DisplayCreationArgsBuilder().build())};
+TEST_F(DisplayFinishFrameTest, performsCompositionIfRepaintEverything) {
+    auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
+    std::shared_ptr<impl::Display> nonHwcDisplay = impl::createDisplay(mCompositionEngine, args);
 
     mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
     nonHwcDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
@@ -779,19 +977,10 @@
 struct DisplayFunctionalTest : public testing::Test {
     class Display : public impl::Display {
     public:
-        explicit Display(const compositionengine::DisplayCreationArgs& args)
-              : impl::Display(args) {}
-
         using impl::Display::injectOutputLayerForTest;
         virtual void injectOutputLayerForTest(std::unique_ptr<compositionengine::OutputLayer>) = 0;
     };
 
-    static std::shared_ptr<Display> createDisplay(
-            const compositionengine::CompositionEngine& compositionEngine,
-            compositionengine::DisplayCreationArgs&& args) {
-        return impl::createDisplayTemplated<Display>(compositionEngine, args);
-    }
-
     DisplayFunctionalTest() {
         EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
 
@@ -803,11 +992,18 @@
     NiceMock<mock::CompositionEngine> mCompositionEngine;
     sp<mock::NativeWindow> mNativeWindow = new NiceMock<mock::NativeWindow>();
     sp<mock::DisplaySurface> mDisplaySurface = new NiceMock<mock::DisplaySurface>();
-    std::shared_ptr<Display> mDisplay = createDisplay(mCompositionEngine,
-                                                      DisplayCreationArgsBuilder()
-                                                              .setDisplayId(DEFAULT_DISPLAY_ID)
-                                                              .setPowerAdvisor(&mPowerAdvisor)
-                                                              .build());
+    std::shared_ptr<Display> mDisplay = impl::createDisplayTemplated<
+            Display>(mCompositionEngine,
+                     DisplayCreationArgsBuilder()
+                             .setPhysical({DEFAULT_DISPLAY_ID, DisplayConnectionType::Internal})
+                             .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
+                             .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
+                             .setIsSecure(true)
+                             .setLayerStackId(DEFAULT_LAYER_STACK)
+                             .setPowerAdvisor(&mPowerAdvisor)
+                             .build()
+
+    );
     impl::RenderSurface* mRenderSurface =
             new impl::RenderSurface{mCompositionEngine, *mDisplay,
                                     RenderSurfaceCreationArgs{DEFAULT_DISPLAY_WIDTH,
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
index e740b13..b738096 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
@@ -29,6 +29,7 @@
     PowerAdvisor();
     ~PowerAdvisor() override;
 
+    MOCK_METHOD0(onBootFinished, void());
     MOCK_METHOD2(setExpensiveRenderingExpected, void(DisplayId displayId, bool expected));
     MOCK_METHOD0(notifyDisplayUpdateImminent, void());
 };
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index cd6bbd1..b72214d 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -47,19 +47,17 @@
 
 ui::Transform::RotationFlags DisplayDevice::sPrimaryDisplayRotationFlags = ui::Transform::ROT_0;
 
-DisplayDeviceCreationArgs::DisplayDeviceCreationArgs(const sp<SurfaceFlinger>& flinger,
-                                                     const wp<IBinder>& displayToken,
-                                                     std::optional<DisplayId> displayId)
-      : flinger(flinger), displayToken(displayToken), displayId(displayId) {}
+DisplayDeviceCreationArgs::DisplayDeviceCreationArgs(
+        const sp<SurfaceFlinger>& flinger, const wp<IBinder>& displayToken,
+        std::shared_ptr<compositionengine::Display> compositionDisplay)
+      : flinger(flinger), displayToken(displayToken), compositionDisplay(compositionDisplay) {}
 
-DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs&& args)
+DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs& args)
       : mFlinger(args.flinger),
         mDisplayToken(args.displayToken),
         mSequenceId(args.sequenceId),
         mConnectionType(args.connectionType),
-        mCompositionDisplay{mFlinger->getCompositionEngine().createDisplay(
-                compositionengine::DisplayCreationArgs{args.isVirtual(), args.displayId,
-                                                       args.powerAdvisor})},
+        mCompositionDisplay{args.compositionDisplay},
         mPhysicalOrientation(args.physicalOrientation),
         mIsPrimary(args.isPrimary) {
     mCompositionDisplay->editState().isSecure = args.isSecure;
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index d970b82..fb6c817 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -64,7 +64,7 @@
     constexpr static float sDefaultMinLumiance = 0.0;
     constexpr static float sDefaultMaxLumiance = 500.0;
 
-    explicit DisplayDevice(DisplayDeviceCreationArgs&& args);
+    explicit DisplayDevice(DisplayDeviceCreationArgs& args);
     virtual ~DisplayDevice();
 
     std::shared_ptr<compositionengine::Display> getCompositionDisplay() const {
@@ -211,13 +211,10 @@
     // We use a constructor to ensure some of the values are set, without
     // assuming a default value.
     DisplayDeviceCreationArgs(const sp<SurfaceFlinger>&, const wp<IBinder>& displayToken,
-                              std::optional<DisplayId>);
-
-    bool isVirtual() const { return !connectionType; }
-
+                              std::shared_ptr<compositionengine::Display>);
     const sp<SurfaceFlinger> flinger;
     const wp<IBinder> displayToken;
-    const std::optional<DisplayId> displayId;
+    const std::shared_ptr<compositionengine::Display> compositionDisplay;
 
     int32_t sequenceId{0};
     std::optional<DisplayConnectionType> connectionType;
@@ -231,7 +228,6 @@
     std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> hwcColorModes;
     int initialPowerMode{HWC_POWER_MODE_NORMAL};
     bool isPrimary{false};
-    Hwc2::PowerAdvisor* powerAdvisor{nullptr};
 };
 
 class DisplayRenderArea : public RenderArea {
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
index 9aaef65..f24f314 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
@@ -104,9 +104,8 @@
     return static_cast<uint16_t>(value >> 40);
 }
 
-DisplayId DisplayId::fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t displayNameHash) {
-    return {(static_cast<Type>(manufacturerId) << 40) | (static_cast<Type>(displayNameHash) << 8) |
-            port};
+DisplayId DisplayId::fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t modelHash) {
+    return {(static_cast<Type>(manufacturerId) << 40) | (static_cast<Type>(modelHash) << 8) | port};
 }
 
 bool isEdid(const DisplayIdentificationData& data) {
@@ -209,23 +208,30 @@
         view.remove_prefix(kDescriptorLength);
     }
 
-    if (displayName.empty()) {
+    std::string_view modelString = displayName;
+
+    if (modelString.empty()) {
         ALOGW("Invalid EDID: falling back to serial number due to missing display name.");
-        displayName = serialNumber;
+        modelString = serialNumber;
     }
-    if (displayName.empty()) {
+    if (modelString.empty()) {
         ALOGW("Invalid EDID: falling back to ASCII text due to missing serial number.");
-        displayName = asciiText;
+        modelString = asciiText;
     }
-    if (displayName.empty()) {
+    if (modelString.empty()) {
         ALOGE("Invalid EDID: display name and fallback descriptors are missing.");
         return {};
     }
 
+    // Hash model string instead of using product code or (integer) serial number, since the latter
+    // have been observed to change on some displays with multiple inputs.
+    const auto modelHash = static_cast<uint32_t>(std::hash<std::string_view>()(modelString));
+
     return Edid{.manufacturerId = manufacturerId,
-                .pnpId = *pnpId,
-                .displayName = displayName,
                 .productId = productId,
+                .pnpId = *pnpId,
+                .modelHash = modelHash,
+                .displayName = displayName,
                 .manufactureWeek = manufactureWeek,
                 .manufactureOrModelYear = manufactureOrModelYear};
 }
@@ -253,10 +259,8 @@
         return {};
     }
 
-    // Hash display name instead of using product code or serial number, since the latter have been
-    // observed to change on some displays with multiple inputs.
-    const auto hash = static_cast<uint32_t>(std::hash<std::string_view>()(edid->displayName));
-    return DisplayIdentificationInfo{.id = DisplayId::fromEdid(port, edid->manufacturerId, hash),
+    const auto displayId = DisplayId::fromEdid(port, edid->manufacturerId, edid->modelHash);
+    return DisplayIdentificationInfo{.id = displayId,
                                      .name = std::string(edid->displayName),
                                      .deviceProductInfo = buildDeviceProductInfo(*edid)};
 }
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
index 0a18ba1..d91b957 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
@@ -34,7 +34,7 @@
 
     uint16_t manufacturerId() const;
 
-    static DisplayId fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t displayNameHash);
+    static DisplayId fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t modelHash);
 };
 
 inline bool operator==(DisplayId lhs, DisplayId rhs) {
@@ -61,6 +61,7 @@
     uint16_t manufacturerId;
     uint16_t productId;
     PnpId pnpId;
+    uint32_t modelHash;
     std::string_view displayName;
     uint8_t manufactureOrModelYear;
     uint8_t manufactureWeek;
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index 36544b6..4c3b3e5 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -57,9 +57,12 @@
  */
 
 FramebufferSurface::FramebufferSurface(HWComposer& hwc, DisplayId displayId,
-                                       const sp<IGraphicBufferConsumer>& consumer)
+                                       const sp<IGraphicBufferConsumer>& consumer,
+                                       uint32_t maxWidth, uint32_t maxHeight)
       : ConsumerBase(consumer),
         mDisplayId(displayId),
+        mMaxWidth(maxWidth),
+        mMaxHeight(maxHeight),
         mCurrentBufferSlot(-1),
         mCurrentBuffer(),
         mCurrentFence(Fence::NO_FENCE),
@@ -75,14 +78,16 @@
                                        GRALLOC_USAGE_HW_RENDER |
                                        GRALLOC_USAGE_HW_COMPOSER);
     const auto& activeConfig = mHwc.getActiveConfig(displayId);
-    mConsumer->setDefaultBufferSize(activeConfig->getWidth(),
-            activeConfig->getHeight());
+    ui::Size limitedSize =
+            limitFramebufferSize(activeConfig->getWidth(), activeConfig->getHeight());
+    mConsumer->setDefaultBufferSize(limitedSize.width, limitedSize.height);
     mConsumer->setMaxAcquiredBufferCount(
             SurfaceFlinger::maxFrameBufferAcquiredBuffers - 1);
 }
 
-void FramebufferSurface::resizeBuffers(const uint32_t width, const uint32_t height) {
-    mConsumer->setDefaultBufferSize(width, height);
+void FramebufferSurface::resizeBuffers(uint32_t width, uint32_t height) {
+    ui::Size limitedSize = limitFramebufferSize(width, height);
+    mConsumer->setDefaultBufferSize(limitedSize.width, limitedSize.height);
 }
 
 status_t FramebufferSurface::beginFrame(bool /*mustRecompose*/) {
@@ -177,6 +182,26 @@
     }
 }
 
+ui::Size FramebufferSurface::limitFramebufferSize(uint32_t width, uint32_t height) {
+    ui::Size framebufferSize(width, height);
+    bool wasLimited = true;
+    if (width > mMaxWidth && mMaxWidth != 0) {
+        float aspectRatio = float(width) / float(height);
+        framebufferSize.height = mMaxWidth / aspectRatio;
+        framebufferSize.width = mMaxWidth;
+        wasLimited = true;
+    }
+    if (height > mMaxHeight && mMaxHeight != 0) {
+        float aspectRatio = float(width) / float(height);
+        framebufferSize.height = mMaxHeight;
+        framebufferSize.width = mMaxHeight * aspectRatio;
+        wasLimited = true;
+    }
+    ALOGI_IF(wasLimited, "framebuffer size has been limited to [%dx%d] from [%dx%d]",
+             framebufferSize.width, framebufferSize.height, width, height);
+    return framebufferSize;
+}
+
 void FramebufferSurface::dumpAsString(String8& result) const {
     Mutex::Autolock lock(mMutex);
     result.appendFormat("  FramebufferSurface: dataspace: %s(%d)\n",
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index 7f451a5..a1859f3 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -23,6 +23,7 @@
 #include <compositionengine/DisplaySurface.h>
 #include <compositionengine/impl/HwcBufferCache.h>
 #include <gui/ConsumerBase.h>
+#include <ui/Size.h>
 
 #include "DisplayIdentification.h"
 
@@ -39,7 +40,8 @@
 class FramebufferSurface : public ConsumerBase, public compositionengine::DisplaySurface {
 public:
     FramebufferSurface(HWComposer& hwc, DisplayId displayId,
-                       const sp<IGraphicBufferConsumer>& consumer);
+                       const sp<IGraphicBufferConsumer>& consumer, uint32_t maxWidth,
+                       uint32_t maxHeight);
 
     virtual status_t beginFrame(bool mustRecompose);
     virtual status_t prepareFrame(CompositionType compositionType);
@@ -47,7 +49,7 @@
     virtual void onFrameCommitted();
     virtual void dumpAsString(String8& result) const;
 
-    virtual void resizeBuffers(const uint32_t width, const uint32_t height);
+    virtual void resizeBuffers(uint32_t width, uint32_t height);
 
     virtual const sp<Fence>& getClientTargetAcquireFence() const override;
 
@@ -58,6 +60,9 @@
 
     virtual void dumpLocked(String8& result, const char* prefix) const;
 
+    // Limits the width and height by the maximum width specified in the constructor.
+    ui::Size limitFramebufferSize(uint32_t width, uint32_t height);
+
     // nextBuffer waits for and then latches the next buffer from the
     // BufferQueue and releases the previously latched buffer to the
     // BufferQueue.  The new buffer is returned in the 'buffer' argument.
@@ -66,6 +71,14 @@
 
     const DisplayId mDisplayId;
 
+    // Framebuffer size has a dimension limitation in pixels based on the graphics capabilities of
+    // the device.
+    const uint32_t mMaxWidth;
+
+    // Framebuffer size has a dimension limitation in pixels based on the graphics capabilities of
+    // the device.
+    const uint32_t mMaxHeight;
+
     // mCurrentBufferIndex is the slot index of the current buffer or
     // INVALID_BUFFER_SLOT to indicate that either there is no current buffer
     // or the buffer is not associated with a slot.
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 550ec61..1d8179c 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -72,6 +72,10 @@
     }
 }
 
+void PowerAdvisor::onBootFinished() {
+    mBootFinished.store(true);
+}
+
 void PowerAdvisor::setExpensiveRenderingExpected(DisplayId displayId, bool expected) {
     if (expected) {
         mExpensiveDisplays.insert(displayId);
@@ -97,6 +101,12 @@
 }
 
 void PowerAdvisor::notifyDisplayUpdateImminent() {
+    // Only start sending this notification once the system has booted so we don't introduce an
+    // early-boot dependency on Power HAL
+    if (!mBootFinished.load()) {
+        return;
+    }
+
     if (mSendUpdateImminent.load()) {
         HalWrapper* const halWrapper = getPowerHal();
         if (halWrapper == nullptr) {
@@ -124,24 +134,20 @@
     static std::unique_ptr<HalWrapper> connect() {
         // Power HAL 1.3 is not guaranteed to be available, thus we need to query
         // Power HAL 1.0 first and try to cast it to Power HAL 1.3.
-        // Power HAL 1.0 is always available, thus if we fail to query it, it means
-        // Power HAL is not available temporarily and we should retry later. However,
-        // if Power HAL 1.0 is available and we can't cast it to Power HAL 1.3,
-        // it means Power HAL 1.3 is not available at all, so we should stop trying.
         sp<V1_3::IPower> powerHal = nullptr;
-        if (sHasPowerHal_1_3) {
-            sp<V1_0::IPower> powerHal_1_0 = V1_0::IPower::getService();
-            if (powerHal_1_0 != nullptr) {
-                // Try to cast to Power HAL 1.3
-                powerHal = V1_3::IPower::castFrom(powerHal_1_0);
-                if (powerHal == nullptr) {
-                    ALOGW("No Power HAL 1.3 service in system");
-                    sHasPowerHal_1_3 = false;
-                } else {
-                    ALOGI("Loaded Power HAL 1.3 service");
-                }
+        sp<V1_0::IPower> powerHal_1_0 = V1_0::IPower::getService();
+        if (powerHal_1_0 != nullptr) {
+            // Try to cast to Power HAL 1.3
+            powerHal = V1_3::IPower::castFrom(powerHal_1_0);
+            if (powerHal == nullptr) {
+                ALOGW("No Power HAL 1.3 service in system, disabling PowerAdvisor");
+            } else {
+                ALOGI("Loaded Power HAL 1.3 service");
             }
+        } else {
+            ALOGW("No Power HAL found, disabling PowerAdvisor");
         }
+
         if (powerHal == nullptr) {
             return nullptr;
         }
@@ -162,12 +168,9 @@
     }
 
 private:
-    static bool sHasPowerHal_1_3;
     const sp<V1_3::IPower> mPowerHal = nullptr;
 };
 
-bool HidlPowerHalWrapper::sHasPowerHal_1_3 = true;
-
 class AidlPowerHalWrapper : public PowerAdvisor::HalWrapper {
 public:
     AidlPowerHalWrapper(sp<IPower> powerHal) : mPowerHal(std::move(powerHal)) {
@@ -226,7 +229,13 @@
 
 PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() {
     static std::unique_ptr<HalWrapper> sHalWrapper = nullptr;
+    static bool sHasHal = true;
 
+    if (!sHasHal) {
+        return nullptr;
+    }
+
+    // If we used to have a HAL, but it stopped responding, attempt to reconnect
     if (mReconnectPowerHal) {
         sHalWrapper = nullptr;
         mReconnectPowerHal = false;
@@ -244,6 +253,12 @@
         sHalWrapper = HidlPowerHalWrapper::connect();
     }
 
+    // If we make it to this point and still don't have a HAL, it's unlikely we
+    // will, so stop trying
+    if (sHalWrapper == nullptr) {
+        sHasHal = false;
+    }
+
     return sHalWrapper.get();
 }
 
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 957d289..4a90acb 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -35,6 +35,7 @@
 public:
     virtual ~PowerAdvisor();
 
+    virtual void onBootFinished() = 0;
     virtual void setExpensiveRenderingExpected(DisplayId displayId, bool expected) = 0;
     virtual void notifyDisplayUpdateImminent() = 0;
 };
@@ -56,12 +57,14 @@
     PowerAdvisor();
     ~PowerAdvisor() override;
 
+    void onBootFinished() override;
     void setExpensiveRenderingExpected(DisplayId displayId, bool expected) override;
     void notifyDisplayUpdateImminent() override;
 
 private:
     HalWrapper* getPowerHal();
 
+    std::atomic_bool mBootFinished = false;
     bool mReconnectPowerHal = false;
 
     std::unordered_set<DisplayId> mExpensiveDisplays;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 766871e..3d67a6b 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -116,6 +116,7 @@
     mCurrentState.frameRateSelectionPriority = PRIORITY_UNSET;
     mCurrentState.metadata = args.metadata;
     mCurrentState.shadowRadius = 0.f;
+    mCurrentState.treeHasFrameRateVote = false;
 
     // drawing state & current state are identical
     mDrawingState = mCurrentState;
@@ -219,6 +220,9 @@
     }
 
     mFlinger->markLayerPendingRemovalLocked(this);
+    if (hasInput()) {
+        mFlinger->dirtyInput();
+    }
 }
 
 void Layer::onRemovedFromCurrentState() {
@@ -719,9 +723,14 @@
 Hwc2::IComposerClient::Composition Layer::getCompositionType(
         const sp<const DisplayDevice>& display) const {
     const auto outputLayer = findOutputLayerForDisplay(display);
-    LOG_FATAL_IF(!outputLayer);
-    return outputLayer->getState().hwc ? (*outputLayer->getState().hwc).hwcCompositionType
-                                       : Hwc2::IComposerClient::Composition::CLIENT;
+    if (outputLayer == nullptr) {
+        return Hwc2::IComposerClient::Composition::INVALID;
+    }
+    if (outputLayer->getState().hwc) {
+        return (*outputLayer->getState().hwc).hwcCompositionType;
+    } else {
+        return Hwc2::IComposerClient::Composition::CLIENT;
+    }
 }
 
 bool Layer::getClearClientTarget(const sp<const DisplayDevice>& display) const {
@@ -988,6 +997,9 @@
     commitTransaction(c);
     mPendingStatesSnapshot = mPendingStates;
     mCurrentState.callbackHandles = {};
+
+    maybeDirtyInput();
+
     return flags;
 }
 
@@ -1326,6 +1338,44 @@
     return true;
 }
 
+void Layer::updateTreeHasFrameRateVote() {
+    const auto traverseTree = [&](const LayerVector::Visitor& visitor) {
+        auto parent = getParent();
+        while (parent) {
+            visitor(parent.get());
+            parent = parent->getParent();
+        }
+
+        traverse(LayerVector::StateSet::Current, visitor);
+    };
+
+    // update parents and children about the vote
+    // First traverse the tree and count how many layers has votes
+    int layersWithVote = 0;
+    traverseTree([&layersWithVote](Layer* layer) {
+        if (layer->mCurrentState.frameRate.rate > 0 ||
+            layer->mCurrentState.frameRate.type == FrameRateCompatibility::NoVote) {
+            layersWithVote++;
+        }
+    });
+
+    // Now update the other layers
+    bool transactionNeeded = false;
+    traverseTree([layersWithVote, &transactionNeeded](Layer* layer) {
+        if (layer->mCurrentState.treeHasFrameRateVote != layersWithVote > 0) {
+            layer->mCurrentState.sequence++;
+            layer->mCurrentState.treeHasFrameRateVote = layersWithVote > 0;
+            layer->mCurrentState.modified = true;
+            layer->setTransactionFlags(eTransactionNeeded);
+            transactionNeeded = true;
+        }
+    });
+
+    if (transactionNeeded) {
+        mFlinger->setTransactionFlags(eTraversalNeeded);
+    }
+}
+
 bool Layer::setFrameRate(FrameRate frameRate) {
     if (!mFlinger->useFrameRateApi) {
         return false;
@@ -1337,12 +1387,26 @@
     mCurrentState.sequence++;
     mCurrentState.frameRate = frameRate;
     mCurrentState.modified = true;
+
+    updateTreeHasFrameRateVote();
+
     setTransactionFlags(eTransactionNeeded);
     return true;
 }
 
-Layer::FrameRate Layer::getFrameRate() const {
-    return getDrawingState().frameRate;
+Layer::FrameRate Layer::getFrameRateForLayerTree() const {
+    const auto frameRate = getDrawingState().frameRate;
+    if (frameRate.rate > 0 || frameRate.type == FrameRateCompatibility::NoVote) {
+        return frameRate;
+    }
+
+    // This layer doesn't have a frame rate. If one of its ancestors or successors
+    // have a vote, return a NoVote for ancestors/successors to set the vote
+    if (getDrawingState().treeHasFrameRateVote) {
+        return {0, FrameRateCompatibility::NoVote};
+    }
+
+    return frameRate;
 }
 
 void Layer::deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber) {
@@ -1600,6 +1664,7 @@
 
     mCurrentChildren.add(layer);
     layer->setParent(this);
+    updateTreeHasFrameRateVote();
 }
 
 ssize_t Layer::removeChild(const sp<Layer>& layer) {
@@ -1607,7 +1672,24 @@
     setTransactionFlags(eTransactionNeeded);
 
     layer->setParent(nullptr);
-    return mCurrentChildren.remove(layer);
+    const auto removeResult = mCurrentChildren.remove(layer);
+
+    updateTreeHasFrameRateVote();
+    layer->updateTreeHasFrameRateVote();
+
+    return removeResult;
+}
+
+void Layer::reparentChildren(const sp<Layer>& newParent) {
+    if (attachChildren()) {
+        setTransactionFlags(eTransactionNeeded);
+    }
+
+    for (const sp<Layer>& child : mCurrentChildren) {
+        newParent->addChild(child);
+    }
+    mCurrentChildren.clear();
+    updateTreeHasFrameRateVote();
 }
 
 bool Layer::reparentChildren(const sp<IBinder>& newParentHandle) {
@@ -1623,13 +1705,7 @@
         return false;
     }
 
-    if (attachChildren()) {
-        setTransactionFlags(eTransactionNeeded);
-    }
-    for (const sp<Layer>& child : mCurrentChildren) {
-        newParent->addChild(child);
-    }
-    mCurrentChildren.clear();
+    reparentChildren(newParent);
 
     return true;
 }
@@ -2052,13 +2128,20 @@
     setTransactionFlags(eTransactionNeeded);
 }
 
-LayerProto* Layer::writeToProto(LayersProto& layersProto, uint32_t traceFlags) const {
+LayerProto* Layer::writeToProto(LayersProto& layersProto, uint32_t traceFlags,
+                                const sp<const DisplayDevice>& device) const {
     LayerProto* layerProto = layersProto.add_layers();
     writeToProtoDrawingState(layerProto, traceFlags);
     writeToProtoCommonState(layerProto, LayerVector::StateSet::Drawing, traceFlags);
 
+    // Only populate for the primary display.
+    if (device) {
+        const Hwc2::IComposerClient::Composition compositionType = getCompositionType(device);
+        layerProto->set_hwc_composition_type(static_cast<HwcCompositionType>(compositionType));
+    }
+
     for (const sp<Layer>& layer : mDrawingChildren) {
-        layer->writeToProto(layersProto, traceFlags);
+        layer->writeToProto(layersProto, traceFlags, device);
     }
 
     return layerProto;
@@ -2460,6 +2543,32 @@
     }
 }
 
+bool Layer::maybeDirtyInput() {
+    // No sense redirtying input.
+    if (mFlinger->inputDirty()) return true;
+
+    if (hasInput()) {
+        mFlinger->dirtyInput();
+        return true;
+    }
+
+    // If a child or relative dirties the input, no sense continuing to traverse
+    // so we return early and halt the recursion. We traverse ourselves instead
+    // of using traverse() so we can implement this early halt.
+    for (const sp<Layer>& child : mDrawingChildren) {
+        if (child->maybeDirtyInput()) {
+            return true;
+        }
+    }
+    for (const wp<Layer>& weakRelative : mDrawingState.zOrderRelatives) {
+        sp<Layer> relative = weakRelative.promote();
+        if (relative && relative->maybeDirtyInput()) {
+            return true;
+        }
+    }
+    return false;
+}
+
 // ---------------------------------------------------------------------------
 
 }; // namespace android
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 5d2144a..be80f78 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -259,6 +259,9 @@
         int32_t frameRateSelectionPriority;
 
         FrameRate frameRate;
+
+        // Indicates whether parents / children of this layer had set FrameRate
+        bool treeHasFrameRateVote;
     };
 
     explicit Layer(const LayerCreationArgs& args);
@@ -344,7 +347,8 @@
     virtual void deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber);
     virtual bool setOverrideScalingMode(int32_t overrideScalingMode);
     virtual bool setMetadata(const LayerMetadata& data);
-    virtual bool reparentChildren(const sp<IBinder>& layer);
+    bool reparentChildren(const sp<IBinder>& newParentHandle);
+    void reparentChildren(const sp<Layer>& newParent);
     virtual void setChildrenDrawingParent(const sp<Layer>& layer);
     virtual bool reparent(const sp<IBinder>& newParentHandle);
     virtual bool detachChildren();
@@ -506,7 +510,8 @@
     bool isRemovedFromCurrentState() const;
 
     LayerProto* writeToProto(LayersProto& layersProto,
-                             uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const;
+                             uint32_t traceFlags = SurfaceTracing::TRACE_ALL,
+                             const sp<const DisplayDevice>& device = nullptr) const;
 
     // Write states that are modified by the main thread. This includes drawing
     // state as well as buffer data. This should be called in the main or tracing
@@ -800,7 +805,7 @@
     Rect getCroppedBufferSize(const Layer::State& s) const;
 
     bool setFrameRate(FrameRate frameRate);
-    virtual FrameRate getFrameRate() const;
+    virtual FrameRate getFrameRateForLayerTree() const;
 
 protected:
     // constant
@@ -829,6 +834,7 @@
     // For unit tests
     friend class TestableSurfaceFlinger;
     friend class RefreshRateSelectionTest;
+    friend class SetFrameRateTest;
 
     virtual void commitTransaction(const State& stateToCommit);
 
@@ -996,6 +1002,10 @@
     // Window types from WindowManager.LayoutParams
     const int mWindowType;
 
+    // Called when mDrawingState has changed. If we or one of our children/relatives hasInput()
+    // then we will dirty the setInputWindows cache.
+    bool maybeDirtyInput();
+
 private:
     /**
      * Returns an unsorted vector of all layers that are part of this tree.
@@ -1012,6 +1022,8 @@
     LayerVector makeChildrenTraversalList(LayerVector::StateSet stateSet,
                                           const std::vector<Layer*>& layersInTree);
 
+    void updateTreeHasFrameRateVote();
+
     // Cached properties computed from drawing state
     // Effective transform taking into account parent transforms and any parent scaling.
     ui::Transform mEffectiveTransform;
diff --git a/services/surfaceflinger/LayerRejecter.cpp b/services/surfaceflinger/LayerRejecter.cpp
index d3364a0..e6c8654 100644
--- a/services/surfaceflinger/LayerRejecter.cpp
+++ b/services/surfaceflinger/LayerRejecter.cpp
@@ -120,7 +120,7 @@
     // We latch the transparent region here, instead of above where we latch
     // the rest of the geometry because it is only content but not necessarily
     // resize dependent.
-    if (!mFront.activeTransparentRegion_legacy.isTriviallyEqual(
+    if (!mFront.activeTransparentRegion_legacy.hasSameRects(
                 mFront.requestedTransparentRegion_legacy)) {
         mFront.activeTransparentRegion_legacy = mFront.requestedTransparentRegion_legacy;
 
diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp
index 40a63d7..5009e10 100644
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ b/services/surfaceflinger/MonitoredProducer.cpp
@@ -162,10 +162,6 @@
     return mProducer->setAutoPrerotation(autoPrerotation);
 }
 
-status_t MonitoredProducer::setFrameRate(float frameRate) {
-    return mProducer->setFrameRate(frameRate);
-}
-
 IBinder* MonitoredProducer::onAsBinder() {
     return this;
 }
diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h
index 4bda831..788919b 100644
--- a/services/surfaceflinger/MonitoredProducer.h
+++ b/services/surfaceflinger/MonitoredProducer.h
@@ -71,7 +71,6 @@
     virtual status_t getUniqueId(uint64_t* outId) const override;
     virtual status_t getConsumerUsage(uint64_t* outUsage) const override;
     virtual status_t setAutoPrerotation(bool autoPrerotation) override;
-    virtual status_t setFrameRate(float frameRate) override;
 
     // The Layer which created this producer, and on which queued Buffer's will be displayed.
     sp<Layer> getLayer() const;
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 682679c..c675971 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -168,9 +168,9 @@
 }
 
 void RefreshRateOverlay::primeCache() {
-    auto allRefreshRates = mFlinger.mRefreshRateConfigs->getAllRefreshRates();
+    auto& allRefreshRates = mFlinger.mRefreshRateConfigs->getAllRefreshRates();
     if (allRefreshRates.size() == 1) {
-        auto fps = allRefreshRates.begin()->second.fps;
+        auto fps = allRefreshRates.begin()->second->fps;
         half4 color = {LOW_FPS_COLOR, ALPHA};
         mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color));
         return;
@@ -178,8 +178,8 @@
 
     std::vector<uint32_t> supportedFps;
     supportedFps.reserve(allRefreshRates.size());
-    for (auto [ignored, refreshRate] : allRefreshRates) {
-        supportedFps.push_back(refreshRate.fps);
+    for (auto& [ignored, refreshRate] : allRefreshRates) {
+        supportedFps.push_back(refreshRate->fps);
     }
 
     std::sort(supportedFps.begin(), supportedFps.end());
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 0031d70..68cd84f 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -201,9 +201,10 @@
 
 void RegionSamplingThread::addListener(const Rect& samplingArea, const sp<IBinder>& stopLayerHandle,
                                        const sp<IRegionSamplingListener>& listener) {
-    wp<Layer> stopLayer = stopLayerHandle != nullptr
-            ? static_cast<Layer::Handle*>(stopLayerHandle.get())->owner
-            : nullptr;
+    wp<Layer> stopLayer;
+    if (stopLayerHandle != nullptr && stopLayerHandle->localBinder() != nullptr) {
+        stopLayer = static_cast<Layer::Handle*>(stopLayerHandle.get())->owner;
+    }
 
     sp<IBinder> asBinder = IInterface::asBinder(listener);
     asBinder->linkToDeath(this);
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
index 776e984..4e3f85f 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
@@ -30,6 +30,7 @@
 #include "EventThread.h"
 
 namespace android {
+using base::StringAppendF;
 
 DispSyncSource::DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync,
                                const char* name)
@@ -107,6 +108,12 @@
     }
 }
 
+void DispSyncSource::dump(std::string& result) const {
+    std::lock_guard lock(mVsyncMutex);
+    StringAppendF(&result, "DispSyncSource: %s(%s)\n", mName, mEnabled ? "enabled" : "disabled");
+    mDispSync->dump(result);
+}
+
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.h b/services/surfaceflinger/Scheduler/DispSyncSource.h
index 328b8c1..f278712 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.h
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.h
@@ -36,6 +36,8 @@
     void setCallback(VSyncSource::Callback* callback) override;
     void setPhaseOffset(nsecs_t phaseOffset) override;
 
+    void dump(std::string&) const override;
+
 private:
     // The following method is the implementation of the DispSync::Callback.
     virtual void onDispSyncEvent(nsecs_t when);
@@ -52,7 +54,7 @@
     std::mutex mCallbackMutex;
     VSyncSource::Callback* mCallback GUARDED_BY(mCallbackMutex) = nullptr;
 
-    std::mutex mVsyncMutex;
+    mutable std::mutex mVsyncMutex;
     TracedOrdinal<nsecs_t> mPhaseOffset GUARDED_BY(mVsyncMutex);
     bool mEnabled GUARDED_BY(mVsyncMutex) = false;
 };
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index acab5a6..0d6a92e 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -425,7 +425,12 @@
             // display is off, keep feeding clients at 60 Hz.
             const auto timeout = mState == State::SyntheticVSync ? 16ms : 1000ms;
             if (mCondition.wait_for(lock, timeout) == std::cv_status::timeout) {
-                ALOGW_IF(mState == State::VSync, "Faking VSYNC due to driver stall");
+                if (mState == State::VSync) {
+                    ALOGW("Faking VSYNC due to driver stall for thread %s", mThreadName);
+                    std::string debugInfo = "VsyncSource debug info:\n";
+                    mVSyncSource->dump(debugInfo);
+                    ALOGW("%s", debugInfo.c_str());
+                }
 
                 LOG_FATAL_IF(!mVSyncState);
                 mPendingEvents.push_back(makeVSync(mVSyncState->displayId,
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 466234d..98b1876 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -66,6 +66,8 @@
     virtual void setVSyncEnabled(bool enable) = 0;
     virtual void setCallback(Callback* callback) = 0;
     virtual void setPhaseOffset(nsecs_t phaseOffset) = 0;
+
+    virtual void dump(std::string& result) const = 0;
 };
 
 class EventThreadConnection : public BnDisplayEventConnection {
diff --git a/services/surfaceflinger/Scheduler/InjectVSyncSource.h b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
index fa46e6f..31da588 100644
--- a/services/surfaceflinger/Scheduler/InjectVSyncSource.h
+++ b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
@@ -45,6 +45,7 @@
     const char* getName() const override { return "inject"; }
     void setVSyncEnabled(bool) override {}
     void setPhaseOffset(nsecs_t) override {}
+    void dump(std::string&) const override {}
 
 private:
     std::mutex mCallbackMutex;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index a8e6756..8958d9a 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -39,7 +39,7 @@
 namespace {
 
 bool isLayerActive(const Layer& layer, const LayerInfo& info, nsecs_t threshold) {
-    if (layer.getFrameRate().rate > 0) {
+    if (layer.getFrameRateForLayerTree().rate > 0) {
         return layer.isVisible();
     }
     return layer.isVisible() && info.getLastUpdatedTime() >= threshold;
@@ -109,7 +109,7 @@
         // Only use the layer if the reference still exists.
         if (layer || CC_UNLIKELY(mTraceEnabled)) {
             // Check if frame rate was set on layer.
-            const auto frameRate = layer->getFrameRate();
+            const auto frameRate = layer->getFrameRateForLayerTree();
             if (frameRate.rate > 0.f) {
                 const auto voteType = [&]() {
                     switch (frameRate.type) {
diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
index 6ef6ce4..8b08592 100644
--- a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
@@ -40,7 +40,7 @@
 namespace {
 
 bool isLayerActive(const Layer& layer, const LayerInfoV2& info, nsecs_t threshold) {
-    if (layer.getFrameRate().rate > 0) {
+    if (layer.getFrameRateForLayerTree().rate > 0) {
         return layer.isVisible();
     }
     return layer.isVisible() && info.getLastUpdatedTime() >= threshold;
@@ -166,7 +166,7 @@
         if (const auto layer = weak.promote(); layer && isLayerActive(*layer, *info, threshold)) {
             i++;
             // Set layer vote if set
-            const auto frameRate = layer->getFrameRate();
+            const auto frameRate = layer->getFrameRateForLayerTree();
             const auto voteType = [&]() {
                 switch (frameRate.type) {
                     case Layer::FrameRateCompatibility::Default:
@@ -212,7 +212,5 @@
     for (const auto& [layer, info] : activeLayers()) {
         info->clearHistory();
     }
-
-    mActiveLayersEnd = 0;
 }
 } // namespace android::scheduler::impl
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
index b755798..b4365bf 100644
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
@@ -82,14 +82,14 @@
         }
     }
 
-    const auto numFrames = std::distance(it, mFrameTimes.end()) - 1;
-    if (numFrames <= 0) {
+    const auto numFrames = std::distance(it, mFrameTimes.end());
+    if (numFrames < FREQUENT_LAYER_WINDOW_SIZE) {
         return false;
     }
 
     // Layer is considered frequent if the average frame rate is higher than the threshold
     const auto totalTime = mFrameTimes.back().queueTime - it->queueTime;
-    return (1e9f * numFrames) / totalTime >= MIN_FPS_FOR_FREQUENT_LAYER;
+    return (1e9f * (numFrames - 1)) / totalTime >= MIN_FPS_FOR_FREQUENT_LAYER;
 }
 
 bool LayerInfoV2::hasEnoughDataForHeuristic() const {
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
index 63d9c4b..43883fb 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -36,6 +36,19 @@
     return std::abs(fpsA - fpsB) <= MARGIN;
 }
 
+std::vector<float> getRefreshRatesFromConfigs(
+        const android::scheduler::RefreshRateConfigs& refreshRateConfigs) {
+    const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates();
+    std::vector<float> refreshRates;
+    refreshRates.reserve(allRefreshRates.size());
+
+    for (const auto& [ignored, refreshRate] : allRefreshRates) {
+        refreshRates.emplace_back(refreshRate->fps);
+    }
+
+    return refreshRates;
+}
+
 } // namespace
 
 namespace android::scheduler {
@@ -45,14 +58,21 @@
 namespace impl {
 
 PhaseOffsets::PhaseOffsets(const scheduler::RefreshRateConfigs& refreshRateConfigs)
-      : // Below defines the threshold when an offset is considered to be negative, i.e. targeting
-        // for the N+2 vsync instead of N+1. This means that:
-        // For offset < threshold, SF wake up (vsync_duration - offset) before HW vsync.
-        // For offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW vsync.
-        mThresholdForNextVsync(getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
-                                       .value_or(std::numeric_limits<nsecs_t>::max())),
-        mOffsets(initializeOffsets(refreshRateConfigs)),
-        mRefreshRateFps(refreshRateConfigs.getCurrentRefreshRate().fps) {}
+      : PhaseOffsets(getRefreshRatesFromConfigs(refreshRateConfigs),
+                     refreshRateConfigs.getCurrentRefreshRate().fps,
+                     // Below defines the threshold when an offset is considered to be negative,
+                     // i.e. targeting for the N+2 vsync instead of N+1. This means that: For offset
+                     // < threshold, SF wake up (vsync_duration - offset) before HW vsync. For
+                     // offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW
+                     // vsync.
+                     getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
+                             .value_or(std::numeric_limits<nsecs_t>::max())) {}
+
+PhaseOffsets::PhaseOffsets(const std::vector<float>& refreshRates, float currentFps,
+                           nsecs_t thresholdForNextVsync)
+      : mThresholdForNextVsync(thresholdForNextVsync),
+        mOffsets(initializeOffsets(refreshRates)),
+        mRefreshRateFps(currentFps) {}
 
 void PhaseOffsets::dump(std::string& result) const {
     const auto [early, earlyGl, late] = getCurrentOffsets();
@@ -67,19 +87,24 @@
 }
 
 std::unordered_map<float, PhaseOffsets::Offsets> PhaseOffsets::initializeOffsets(
-        const scheduler::RefreshRateConfigs& refreshRateConfigs) const {
+        const std::vector<float>& refreshRates) const {
     std::unordered_map<float, Offsets> offsets;
 
-    for (const auto& [ignored, refreshRate] : refreshRateConfigs.getAllRefreshRates()) {
-        if (refreshRate.fps > 65.0f) {
-            offsets.emplace(refreshRate.fps, getHighFpsOffsets(refreshRate.vsyncPeriod));
-        } else {
-            offsets.emplace(refreshRate.fps, getDefaultOffsets(refreshRate.vsyncPeriod));
-        }
+    for (const auto& refreshRate : refreshRates) {
+        offsets.emplace(refreshRate,
+                        getPhaseOffsets(refreshRate, static_cast<nsecs_t>(1e9f / refreshRate)));
     }
     return offsets;
 }
 
+PhaseOffsets::Offsets PhaseOffsets::getPhaseOffsets(float fps, nsecs_t vsyncPeriod) const {
+    if (fps > 65.0f) {
+        return getHighFpsOffsets(vsyncPeriod);
+    } else {
+        return getDefaultOffsets(vsyncPeriod);
+    }
+}
+
 PhaseOffsets::Offsets PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const {
     const int64_t vsyncPhaseOffsetNs = sysprop::vsync_event_phase_offset_ns(1000000);
     const int64_t sfVsyncPhaseOffsetNs = sysprop::vsync_sf_event_phase_offset_ns(1000000);
@@ -159,8 +184,15 @@
                                    [&fps](const std::pair<float, Offsets>& candidateFps) {
                                        return fpsEqualsWithMargin(fps, candidateFps.first);
                                    });
-    LOG_ALWAYS_FATAL_IF(iter == mOffsets.end());
-    return iter->second;
+
+    if (iter != mOffsets.end()) {
+        return iter->second;
+    }
+
+    // Unknown refresh rate. This might happen if we get a hotplug event for an external display.
+    // In this case just construct the offset.
+    ALOGW("Can't find offset for %.2f fps", fps);
+    return getPhaseOffsets(fps, static_cast<nsecs_t>(1e9f / fps));
 }
 
 static void validateSysprops() {
@@ -231,19 +263,6 @@
     };
 }
 
-static std::vector<float> getRefreshRatesFromConfigs(
-        const android::scheduler::RefreshRateConfigs& refreshRateConfigs) {
-    const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates();
-    std::vector<float> refreshRates;
-    refreshRates.reserve(allRefreshRates.size());
-
-    for (const auto& [ignored, refreshRate] : allRefreshRates) {
-        refreshRates.emplace_back(refreshRate.fps);
-    }
-
-    return refreshRates;
-}
-
 std::unordered_map<float, PhaseDurations::Offsets> PhaseDurations::initializeOffsets(
         const std::vector<float>& refreshRates) const {
     std::unordered_map<float, Offsets> offsets;
@@ -288,8 +307,7 @@
         return iter->second;
     }
 
-    // Unknown refresh rate. This might happen if we get a hotplug event for the default display.
-    // This happens only during tests and not during regular device operation.
+    // Unknown refresh rate. This might happen if we get a hotplug event for an external display.
     // In this case just construct the offset.
     ALOGW("Can't find offset for %.2f fps", fps);
     return constructOffsets(static_cast<nsecs_t>(1e9f / fps));
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h
index b7d4eae..fa8011d 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.h
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.h
@@ -66,11 +66,15 @@
     // Returns current offsets in human friendly format.
     void dump(std::string& result) const override;
 
-private:
+protected:
+    // Used for unit tests
+    PhaseOffsets(const std::vector<float>& refreshRates, float currentFps,
+                 nsecs_t thresholdForNextVsync);
     std::unordered_map<float, Offsets> initializeOffsets(
-            const scheduler::RefreshRateConfigs&) const;
-    Offsets getDefaultOffsets(nsecs_t vsyncDuration) const;
-    Offsets getHighFpsOffsets(nsecs_t vsyncDuration) const;
+            const std::vector<float>& refreshRates) const;
+    Offsets getDefaultOffsets(nsecs_t vsyncPeriod) const;
+    Offsets getHighFpsOffsets(nsecs_t vsyncPeriod) const;
+    Offsets getPhaseOffsets(float fps, nsecs_t vsyncPeriod) const;
 
     const nsecs_t mThresholdForNextVsync;
     const std::unordered_map<float, Offsets> mOffsets;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index b876ccd..02d0b53 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -23,6 +23,9 @@
 #include <chrono>
 #include <cmath>
 
+#undef LOG_TAG
+#define LOG_TAG "RefreshRateConfigs"
+
 namespace android::scheduler {
 
 using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType;
@@ -95,16 +98,19 @@
 }
 
 const RefreshRate& RefreshRateConfigs::getRefreshRateForContentV2(
-        const std::vector<LayerRequirement>& layers, bool touchActive) const {
+        const std::vector<LayerRequirement>& layers, bool touchActive,
+        bool* touchConsidered) const {
     ATRACE_CALL();
     ALOGV("getRefreshRateForContent %zu layers", layers.size());
 
+    *touchConsidered = false;
     std::lock_guard lock(mLock);
 
-    // For now if the touch is active return the peak refresh rate
-    // This should be optimized to consider other layers as well.
-    if (touchActive) {
-        return *mAvailableRefreshRates.back();
+    // If there are not layers, there is not content detection, so return the current
+    // refresh rate.
+    if (layers.empty()) {
+        *touchConsidered = touchActive;
+        return touchActive ? *mAvailableRefreshRates.back() : getCurrentRefreshRateByPolicyLocked();
     }
 
     int noVoteLayers = 0;
@@ -112,17 +118,30 @@
     int maxVoteLayers = 0;
     int explicitDefaultVoteLayers = 0;
     int explicitExactOrMultipleVoteLayers = 0;
+    float maxExplicitWeight = 0;
     for (const auto& layer : layers) {
-        if (layer.vote == LayerVoteType::NoVote)
+        if (layer.vote == LayerVoteType::NoVote) {
             noVoteLayers++;
-        else if (layer.vote == LayerVoteType::Min)
+        } else if (layer.vote == LayerVoteType::Min) {
             minVoteLayers++;
-        else if (layer.vote == LayerVoteType::Max)
+        } else if (layer.vote == LayerVoteType::Max) {
             maxVoteLayers++;
-        else if (layer.vote == LayerVoteType::ExplicitDefault)
+        } else if (layer.vote == LayerVoteType::ExplicitDefault) {
             explicitDefaultVoteLayers++;
-        else if (layer.vote == LayerVoteType::ExplicitExactOrMultiple)
+            maxExplicitWeight = std::max(maxExplicitWeight, layer.weight);
+        } else if (layer.vote == LayerVoteType::ExplicitExactOrMultiple) {
             explicitExactOrMultipleVoteLayers++;
+            maxExplicitWeight = std::max(maxExplicitWeight, layer.weight);
+        }
+    }
+
+    // Consider the touch event if there are no ExplicitDefault layers.
+    // ExplicitDefault are mostly interactive (as opposed to ExplicitExactOrMultiple)
+    // and therefore if those posted an explicit vote we should not change it
+    // if get get a touch event.
+    if (touchActive && explicitDefaultVoteLayers == 0) {
+        *touchConsidered = true;
+        return *mAvailableRefreshRates.back();
     }
 
     // Only if all layers want Min we should return Min
@@ -162,16 +181,17 @@
             const auto layerPeriod = round<nsecs_t>(1e9f / layer.desiredRefreshRate);
             if (layer.vote == LayerVoteType::ExplicitDefault) {
                 const auto layerScore = [&]() {
-                    const auto [displayFramesQuot, displayFramesRem] =
-                            getDisplayFrames(layerPeriod, displayPeriod);
-                    if (displayFramesQuot == 0) {
-                        // Layer desired refresh rate is higher the display rate.
-                        return static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod);
+                    // Find the actual rate the layer will render, assuming
+                    // that layerPeriod is the minimal time to render a frame
+                    auto actualLayerPeriod = displayPeriod;
+                    int multiplier = 1;
+                    while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) {
+                        multiplier++;
+                        actualLayerPeriod = displayPeriod * multiplier;
                     }
-
-                    return 1.0f -
-                            (static_cast<float>(displayFramesRem) /
-                             static_cast<float>(layerPeriod));
+                    return std::min(1.0f,
+                                    static_cast<float>(layerPeriod) /
+                                            static_cast<float>(actualLayerPeriod));
                 }();
 
                 ALOGV("%s (ExplicitDefault, weight %.2f) %.2fHz gives %s score of %.2f",
@@ -229,20 +249,21 @@
             ? getBestRefreshRate(scores.rbegin(), scores.rend())
             : getBestRefreshRate(scores.begin(), scores.end());
 
-    return bestRefreshRate == nullptr ? *mCurrentRefreshRate : *bestRefreshRate;
+    return *bestRefreshRate;
 }
 
 template <typename Iter>
 const RefreshRate* RefreshRateConfigs::getBestRefreshRate(Iter begin, Iter end) const {
-    const RefreshRate* bestRefreshRate = nullptr;
-    float max = 0;
+    constexpr auto EPSILON = 0.001f;
+    const RefreshRate* bestRefreshRate = begin->first;
+    float max = begin->second;
     for (auto i = begin; i != end; ++i) {
         const auto [refreshRate, score] = *i;
         ALOGV("%s scores %.2f", refreshRate->name.c_str(), score);
 
         ATRACE_INT(refreshRate->name.c_str(), round<int>(score * 100));
 
-        if (score > max) {
+        if (score > max * (1 + EPSILON)) {
             max = score;
             bestRefreshRate = refreshRate;
         }
@@ -272,16 +293,20 @@
 
 const RefreshRate& RefreshRateConfigs::getCurrentRefreshRateByPolicy() const {
     std::lock_guard lock(mLock);
+    return getCurrentRefreshRateByPolicyLocked();
+}
+
+const RefreshRate& RefreshRateConfigs::getCurrentRefreshRateByPolicyLocked() const {
     if (std::find(mAvailableRefreshRates.begin(), mAvailableRefreshRates.end(),
                   mCurrentRefreshRate) != mAvailableRefreshRates.end()) {
         return *mCurrentRefreshRate;
     }
-    return mRefreshRates.at(mDefaultConfig);
+    return *mRefreshRates.at(mDefaultConfig);
 }
 
 void RefreshRateConfigs::setCurrentConfigId(HwcConfigIndexType configId) {
     std::lock_guard lock(mLock);
-    mCurrentRefreshRate = &mRefreshRates.at(configId);
+    mCurrentRefreshRate = mRefreshRates.at(configId).get();
 }
 
 RefreshRateConfigs::RefreshRateConfigs(const std::vector<InputConfig>& configs,
@@ -316,7 +341,7 @@
     if (mRefreshRates.count(defaultConfigId) == 0) {
         return BAD_VALUE;
     }
-    const RefreshRate& refreshRate = mRefreshRates.at(defaultConfigId);
+    const RefreshRate& refreshRate = *mRefreshRates.at(defaultConfigId);
     if (!refreshRate.inPolicy(minRefreshRate, maxRefreshRate)) {
         return BAD_VALUE;
     }
@@ -351,10 +376,10 @@
     outRefreshRates->clear();
     outRefreshRates->reserve(mRefreshRates.size());
     for (const auto& [type, refreshRate] : mRefreshRates) {
-        if (shouldAddRefreshRate(refreshRate)) {
+        if (shouldAddRefreshRate(*refreshRate)) {
             ALOGV("getSortedRefreshRateList: config %d added to list policy",
-                  refreshRate.configId.value());
-            outRefreshRates->push_back(&refreshRate);
+                  refreshRate->configId.value());
+            outRefreshRates->push_back(refreshRate.get());
         }
     }
 
@@ -366,7 +391,7 @@
 
 void RefreshRateConfigs::constructAvailableRefreshRates() {
     // Filter configs based on current policy and sort based on vsync period
-    HwcConfigGroupType group = mRefreshRates.at(mDefaultConfig).configGroup;
+    HwcConfigGroupType group = mRefreshRates.at(mDefaultConfig)->configGroup;
     ALOGV("constructAvailableRefreshRates: default %d group %d min %.2f max %.2f",
           mDefaultConfig.value(), group.value(), mMinRefreshRateFps, mMaxRefreshRateFps);
     getSortedRefreshRateList(
@@ -393,16 +418,15 @@
     LOG_ALWAYS_FATAL_IF(configs.empty());
     LOG_ALWAYS_FATAL_IF(currentHwcConfig.value() >= configs.size());
 
-    auto buildRefreshRate = [&](InputConfig config) -> RefreshRate {
-        const float fps = 1e9f / config.vsyncPeriod;
-        return RefreshRate(config.configId, config.vsyncPeriod, config.configGroup,
-                           base::StringPrintf("%2.ffps", fps), fps);
-    };
-
     for (const auto& config : configs) {
-        mRefreshRates.emplace(config.configId, buildRefreshRate(config));
+        const float fps = 1e9f / config.vsyncPeriod;
+        mRefreshRates.emplace(config.configId,
+                              std::make_unique<RefreshRate>(config.configId, config.vsyncPeriod,
+                                                            config.configGroup,
+                                                            base::StringPrintf("%2.ffps", fps),
+                                                            fps));
         if (config.configId == currentHwcConfig) {
-            mCurrentRefreshRate = &mRefreshRates.at(config.configId);
+            mCurrentRefreshRate = mRefreshRates.at(config.configId).get();
         }
     }
 
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 0b5c73c..87d4389 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -59,6 +59,8 @@
                 configGroup(configGroup),
                 name(std::move(name)),
                 fps(fps) {}
+
+        RefreshRate(const RefreshRate&) = delete;
         // This config ID corresponds to the position of the config in the vector that is stored
         // on the device.
         const HwcConfigIndexType configId;
@@ -85,7 +87,8 @@
         bool operator==(const RefreshRate& other) const { return !(*this != other); }
     };
 
-    using AllRefreshRatesMapType = std::unordered_map<HwcConfigIndexType, const RefreshRate>;
+    using AllRefreshRatesMapType =
+            std::unordered_map<HwcConfigIndexType, std::unique_ptr<const RefreshRate>>;
 
     // Sets the current policy to choose refresh rates. Returns NO_ERROR if the requested policy is
     // valid, or a negative error value otherwise. policyChanged, if non-null, will be set to true
@@ -134,9 +137,11 @@
 
     // Returns the refresh rate that fits best to the given layers. This function also gets a
     // boolean flag that indicates whether user touched the screen recently to be factored in when
-    // choosing the refresh rate.
+    // choosing the refresh rate and returns whether the refresh rate was chosen as a result of
+    // a touch event.
     const RefreshRate& getRefreshRateForContentV2(const std::vector<LayerRequirement>& layers,
-                                                  bool touchActive) const EXCLUDES(mLock);
+                                                  bool touchActive, bool* touchConsidered) const
+            EXCLUDES(mLock);
 
     // Returns all the refresh rates supported by the device. This won't change at runtime.
     const AllRefreshRatesMapType& getAllRefreshRates() const EXCLUDES(mLock);
@@ -163,7 +168,7 @@
     // Returns the refresh rate that corresponds to a HwcConfigIndexType. This won't change at
     // runtime.
     const RefreshRate& getRefreshRateFromConfigId(HwcConfigIndexType configId) const {
-        return mRefreshRates.at(configId);
+        return *mRefreshRates.at(configId);
     };
 
     // Stores the current configId the device operates at
@@ -199,6 +204,10 @@
     // display refresh period.
     std::pair<nsecs_t, nsecs_t> getDisplayFrames(nsecs_t layerPeriod, nsecs_t displayPeriod) const;
 
+    // Returns the current refresh rate, if allowed. Otherwise the default that is allowed by
+    // the policy.
+    const RefreshRate& getCurrentRefreshRateByPolicyLocked() const REQUIRES(mLock);
+
     // The list of refresh rates, indexed by display config ID. This must not change after this
     // object is initialized.
     AllRefreshRatesMapType mRefreshRates;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 3a44332..cd6075f 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -99,12 +99,14 @@
 
 Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function,
                      const scheduler::RefreshRateConfigs& refreshRateConfig,
-                     ISchedulerCallback& schedulerCallback, bool useContentDetectionV2)
+                     ISchedulerCallback& schedulerCallback, bool useContentDetectionV2,
+                     bool useContentDetection)
       : mPrimaryDispSync(createDispSync()),
         mEventControlThread(new impl::EventControlThread(std::move(function))),
         mSupportKernelTimer(sysprop::support_kernel_idle_timer(false)),
         mSchedulerCallback(schedulerCallback),
         mRefreshRateConfigs(refreshRateConfig),
+        mUseContentDetection(useContentDetection),
         mUseContentDetectionV2(useContentDetectionV2) {
     using namespace sysprop;
 
@@ -147,12 +149,14 @@
 Scheduler::Scheduler(std::unique_ptr<DispSync> primaryDispSync,
                      std::unique_ptr<EventControlThread> eventControlThread,
                      const scheduler::RefreshRateConfigs& configs,
-                     ISchedulerCallback& schedulerCallback, bool useContentDetectionV2)
+                     ISchedulerCallback& schedulerCallback, bool useContentDetectionV2,
+                     bool useContentDetection)
       : mPrimaryDispSync(std::move(primaryDispSync)),
         mEventControlThread(std::move(eventControlThread)),
         mSupportKernelTimer(false),
         mSchedulerCallback(schedulerCallback),
         mRefreshRateConfigs(configs),
+        mUseContentDetection(useContentDetection),
         mUseContentDetectionV2(useContentDetectionV2) {}
 
 Scheduler::~Scheduler() {
@@ -381,6 +385,17 @@
 void Scheduler::registerLayer(Layer* layer) {
     if (!mLayerHistory) return;
 
+    // If the content detection feature is off, all layers are registered at NoVote. We still
+    // keep the layer history, since we use it for other features (like Frame Rate API), so layers
+    // still need to be registered.
+    if (!mUseContentDetection) {
+        mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().fps,
+                                     mRefreshRateConfigs.getMaxRefreshRate().fps,
+                                     scheduler::LayerHistory::LayerVoteType::NoVote);
+        return;
+    }
+
+    // In V1 of content detection, all layers are registered as Heuristic (unless it's wallpaper).
     if (!mUseContentDetectionV2) {
         const auto lowFps = mRefreshRateConfigs.getMinRefreshRate().fps;
         const auto highFps = layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER
@@ -391,6 +406,7 @@
                                      scheduler::LayerHistory::LayerVoteType::Heuristic);
     } else {
         if (layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER) {
+            // Running Wallpaper at Min is considered as part of content detection.
             mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().fps,
                                          mRefreshRateConfigs.getMaxRefreshRate().fps,
                                          scheduler::LayerHistory::LayerVoteType::Min);
@@ -403,20 +419,6 @@
                                          mRefreshRateConfigs.getMaxRefreshRate().fps,
                                          scheduler::LayerHistory::LayerVoteType::Heuristic);
         }
-
-        // TODO(146935143): Simulate youtube app vote. This should be removed once youtube calls the
-        // API to set desired rate
-        {
-            const auto vote = property_get_int32("experimental.sf.force_youtube_vote", 0);
-            if (vote != 0 &&
-                layer->getName() ==
-                        "SurfaceView - "
-                        "com.google.android.youtube/"
-                        "com.google.android.apps.youtube.app.WatchWhileActivity#0") {
-                layer->setFrameRate(
-                        Layer::FrameRate(vote, Layer::FrameRateCompatibility::ExactOrMultiple));
-            }
-        }
     }
 }
 
@@ -447,7 +449,7 @@
             return;
         }
         mFeatures.configId = newConfigId;
-        auto newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+        auto& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
         mSchedulerCallback.changeRefreshRate(newRefreshRate, ConfigEvent::Changed);
     }
 }
@@ -467,7 +469,7 @@
     // that is currently on top. b/142507166 will give us this capability.
     std::lock_guard<std::mutex> lock(mFeatureStateLock);
     if (mLayerHistory) {
-        mLayerHistory->clear();
+        // Layer History will be cleared based on RefreshRateConfigs::getRefreshRateForContentV2
 
         mTouchTimer->reset();
 
@@ -499,7 +501,7 @@
 
     // TODO(145561154): cleanup the kernel idle timer implementation and the refresh rate
     // magic number
-    const auto refreshRate = mRefreshRateConfigs.getCurrentRefreshRate();
+    const auto& refreshRate = mRefreshRateConfigs.getCurrentRefreshRate();
     constexpr float FPS_THRESHOLD_FOR_KERNEL_TIMER = 65.0f;
     if (state == TimerState::Reset && refreshRate.fps > FPS_THRESHOLD_FOR_KERNEL_TIMER) {
         // If we're not in performance mode then the kernel timer shouldn't do
@@ -512,6 +514,8 @@
         // need to update the DispSync model anyway.
         disableHardwareVsync(false /* makeUnavailable */);
     }
+
+    mSchedulerCallback.kernelTimerChanged(state == TimerState::Expired);
 }
 
 void Scheduler::idleTimerCallback(TimerState state) {
@@ -537,8 +541,10 @@
 
     StringAppendF(&result, "+  Idle timer: %s\n",
                   mIdleTimer ? mIdleTimer->dump().c_str() : states[0]);
-    StringAppendF(&result, "+  Touch timer: %s\n\n",
+    StringAppendF(&result, "+  Touch timer: %s\n",
                   mTouchTimer ? mTouchTimer->dump().c_str() : states[0]);
+    StringAppendF(&result, "+  Use content detection: %s\n\n",
+                  sysprop::use_content_detection_for_refresh_rate(false) ? "on" : "off");
 }
 
 template <class T>
@@ -600,10 +606,21 @@
         return mRefreshRateConfigs.getRefreshRateForContent(mFeatures.contentRequirements).configId;
     }
 
-    return mRefreshRateConfigs
-            .getRefreshRateForContentV2(mFeatures.contentRequirements,
-                                        mTouchTimer && mFeatures.touch == TouchState::Active)
-            .configId;
+    bool touchConsidered;
+    const auto& ret =
+            mRefreshRateConfigs
+                    .getRefreshRateForContentV2(mFeatures.contentRequirements,
+                                                mTouchTimer &&
+                                                        mFeatures.touch == TouchState::Active,
+                                                &touchConsidered)
+                    .configId;
+    if (touchConsidered) {
+        // Clear layer history if refresh rate was selected based on touch to allow
+        // the hueristic to pick up with the new rate.
+        mLayerHistory->clear();
+    }
+
+    return ret;
 }
 
 std::optional<HwcConfigIndexType> Scheduler::getPreferredConfigId() {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 46d1a5e..04cc96a 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -51,6 +51,7 @@
     virtual void changeRefreshRate(const scheduler::RefreshRateConfigs::RefreshRate&,
                                    scheduler::RefreshRateConfigEvent) = 0;
     virtual void repaintEverythingForHWC() = 0;
+    virtual void kernelTimerChanged(bool expired) = 0;
 };
 
 class Scheduler {
@@ -63,7 +64,7 @@
 
     Scheduler(impl::EventControlThread::SetVSyncEnabledFunction,
               const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback,
-              bool useContentDetectionV2);
+              bool useContentDetectionV2, bool useContentDetection);
 
     virtual ~Scheduler();
 
@@ -159,7 +160,7 @@
     // Used by tests to inject mocks.
     Scheduler(std::unique_ptr<DispSync>, std::unique_ptr<EventControlThread>,
               const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback,
-              bool useContentDetectionV2);
+              bool useContentDetectionV2, bool useContentDetection);
 
     std::unique_ptr<VSyncSource> makePrimaryDispSyncSource(const char* name, nsecs_t phaseOffsetNs);
 
@@ -245,6 +246,9 @@
             GUARDED_BY(mVsyncTimelineLock);
     static constexpr std::chrono::nanoseconds MAX_VSYNC_APPLIED_TIME = 200ms;
 
+    // This variable indicates whether to use the content detection feature at all.
+    const bool mUseContentDetection;
+    // This variable indicates whether to use V2 version of the content detection.
     const bool mUseContentDetectionV2;
 };
 
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index a6fb3a4..2a2d7c5 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -108,6 +108,8 @@
      */
     virtual CancelResult cancel(CallbackToken token) = 0;
 
+    virtual void dump(std::string& result) const = 0;
+
 protected:
     VSyncDispatch() = default;
     VSyncDispatch(VSyncDispatch const&) = delete;
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index d0f18ab..460d4a5 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -15,6 +15,7 @@
  */
 
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <android-base/stringprintf.h>
 #include <utils/Trace.h>
 #include <vector>
 
@@ -23,6 +24,7 @@
 #include "VSyncTracker.h"
 
 namespace android::scheduler {
+using base::StringAppendF;
 
 VSyncDispatch::~VSyncDispatch() = default;
 VSyncTracker::~VSyncTracker() = default;
@@ -122,6 +124,28 @@
     mCv.wait(lk, [this]() REQUIRES(mRunningMutex) { return !mRunning; });
 }
 
+void VSyncDispatchTimerQueueEntry::dump(std::string& result) const {
+    std::lock_guard<std::mutex> lk(mRunningMutex);
+    std::string armedInfo;
+    if (mArmedInfo) {
+        StringAppendF(&armedInfo, "[wake up in %.2fms for vsync %.2fms from now]",
+                      (mArmedInfo->mActualWakeupTime - systemTime()) / 1e6f,
+                      (mArmedInfo->mActualVsyncTime - systemTime()) / 1e6f);
+    }
+
+    StringAppendF(&result, "\t\t%s: %s %s\n", mName.c_str(),
+                  mRunning ? "(in callback function)" : "", armedInfo.c_str());
+    StringAppendF(&result, "\t\t\tmWorkDuration: %.2fms mEarliestVsync: %.2fms relative to now\n",
+                  mWorkDuration / 1e6f, (mEarliestVsync - systemTime()) / 1e6f);
+
+    if (mLastDispatchTime) {
+        StringAppendF(&result, "\t\t\tmLastDispatchTime: %.2fms ago\n",
+                      (systemTime() - *mLastDispatchTime) / 1e6f);
+    } else {
+        StringAppendF(&result, "\t\t\tmLastDispatchTime unknown\n");
+    }
+}
+
 VSyncDispatchTimerQueue::VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk,
                                                  VSyncTracker& tracker, nsecs_t timerSlack,
                                                  nsecs_t minVsyncDistance)
@@ -296,6 +320,18 @@
     return CancelResult::TooLate;
 }
 
+void VSyncDispatchTimerQueue::dump(std::string& result) const {
+    std::lock_guard<decltype(mMutex)> lk(mMutex);
+    StringAppendF(&result, "\tmTimerSlack: %.2fms mMinVsyncDistance: %.2fms\n", mTimerSlack / 1e6f,
+                  mMinVsyncDistance / 1e6f);
+    StringAppendF(&result, "\tmIntendedWakeupTime: %.2fms from now\n",
+                  (mIntendedWakeupTime - systemTime()) / 1e6f);
+    StringAppendF(&result, "\tCallbacks:\n");
+    for (const auto& [token, entry] : mCallbacks) {
+        entry->dump(result);
+    }
+}
+
 VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncDispatch& dispatch,
                                                      VSyncDispatch::Callback const& callbackFn,
                                                      std::string const& callbackName)
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index fd0a034..390e0c4 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -71,6 +71,8 @@
     // Block calling thread while the callback is executing.
     void ensureNotRunning();
 
+    void dump(std::string& result) const;
+
 private:
     std::string const mName;
     VSyncDispatch::Callback const mCallback;
@@ -86,7 +88,7 @@
     std::optional<ArmingInfo> mArmedInfo;
     std::optional<nsecs_t> mLastDispatchTime;
 
-    std::mutex mRunningMutex;
+    mutable std::mutex mRunningMutex;
     std::condition_variable mCv;
     bool mRunning GUARDED_BY(mRunningMutex) = false;
 };
@@ -112,6 +114,7 @@
     void unregisterCallback(CallbackToken token) final;
     ScheduleResult schedule(CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync) final;
     CancelResult cancel(CallbackToken token) final;
+    void dump(std::string& result) const final;
 
 private:
     VSyncDispatchTimerQueue(VSyncDispatchTimerQueue const&) = delete;
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 399da19..a3cb772 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -22,6 +22,7 @@
 //#define LOG_NDEBUG 0
 #include "VSyncPredictor.h"
 #include <android-base/logging.h>
+#include <android-base/stringprintf.h>
 #include <cutils/compiler.h>
 #include <cutils/properties.h>
 #include <utils/Log.h>
@@ -31,6 +32,7 @@
 #include <sstream>
 
 namespace android::scheduler {
+using base::StringAppendF;
 
 static auto constexpr kMaxPercent = 100u;
 
@@ -115,10 +117,10 @@
     auto it = mRateMap.find(mIdealPeriod);
     auto const currentPeriod = std::get<0>(it->second);
     // TODO (b/144707443): its important that there's some precision in the mean of the ordinals
-    //                     for the intercept calculation, so scale the ordinals by 10 to continue
+    //                     for the intercept calculation, so scale the ordinals by 1000 to continue
     //                     fixed point calculation. Explore expanding
     //                     scheduler::utils::calculate_mean to have a fixed point fractional part.
-    static constexpr int kScalingFactor = 10;
+    static constexpr int64_t kScalingFactor = 1000;
 
     for (auto i = 0u; i < mTimestamps.size(); i++) {
         traceInt64If("VSP-ts", mTimestamps[i]);
@@ -147,7 +149,7 @@
         return false;
     }
 
-    nsecs_t const anticipatedPeriod = top / bottom * kScalingFactor;
+    nsecs_t const anticipatedPeriod = top * kScalingFactor / bottom;
     nsecs_t const intercept = meanTS - (anticipatedPeriod * meanOrdinal / kScalingFactor);
 
     auto const percent = std::abs(anticipatedPeriod - mIdealPeriod) * kMaxPercent / mIdealPeriod;
@@ -263,6 +265,18 @@
     clearTimestamps();
 }
 
+void VSyncPredictor::dump(std::string& result) const {
+    std::lock_guard<std::mutex> lk(mMutex);
+    StringAppendF(&result, "\tmIdealPeriod=%.2f\n", mIdealPeriod / 1e6f);
+    StringAppendF(&result, "\tRefresh Rate Map:\n");
+    for (const auto& [idealPeriod, periodInterceptTuple] : mRateMap) {
+        StringAppendF(&result,
+                      "\t\tFor ideal period %.2fms: period = %.2fms, intercept = %" PRId64 "\n",
+                      idealPeriod / 1e6f, std::get<0>(periodInterceptTuple) / 1e6f,
+                      std::get<1>(periodInterceptTuple));
+    }
+}
+
 } // namespace android::scheduler
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index ef1d88a..3ca878d 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -60,6 +60,8 @@
 
     std::tuple<nsecs_t /* slope */, nsecs_t /* intercept */> getVSyncPredictionModel() const;
 
+    void dump(std::string& result) const final;
+
 private:
     VSyncPredictor(VSyncPredictor const&) = delete;
     VSyncPredictor& operator=(VSyncPredictor const&) = delete;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 949ba4c..892ae62 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -19,25 +19,51 @@
 #define LOG_TAG "VSyncReactor"
 //#define LOG_NDEBUG 0
 #include "VSyncReactor.h"
+#include <cutils/properties.h>
 #include <log/log.h>
 #include <utils/Trace.h>
+#include "../TracedOrdinal.h"
 #include "TimeKeeper.h"
 #include "VSyncDispatch.h"
 #include "VSyncTracker.h"
 
 namespace android::scheduler {
+using base::StringAppendF;
 
 Clock::~Clock() = default;
 nsecs_t SystemClock::now() const {
     return systemTime(SYSTEM_TIME_MONOTONIC);
 }
 
+class PredictedVsyncTracer {
+public:
+    PredictedVsyncTracer(VSyncDispatch& dispatch)
+          : mRegistration(dispatch,
+                          std::bind(&PredictedVsyncTracer::callback, this, std::placeholders::_1,
+                                    std::placeholders::_2),
+                          "PredictedVsyncTracer") {
+        mRegistration.schedule(0, 0);
+    }
+
+private:
+    TracedOrdinal<bool> mParity = {"VSYNC-predicted", 0};
+    VSyncCallbackRegistration mRegistration;
+
+    void callback(nsecs_t /*vsyncTime*/, nsecs_t /*targetWakeupTim*/) {
+        mParity = !mParity;
+        mRegistration.schedule(0, 0);
+    }
+};
+
 VSyncReactor::VSyncReactor(std::unique_ptr<Clock> clock, std::unique_ptr<VSyncDispatch> dispatch,
                            std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit)
       : mClock(std::move(clock)),
         mTracker(std::move(tracker)),
         mDispatch(std::move(dispatch)),
-        mPendingLimit(pendingFenceLimit) {}
+        mPendingLimit(pendingFenceLimit),
+        mPredictedVsyncTracer(property_get_bool("debug.sf.show_predicted_vsync", false)
+                                      ? std::make_unique<PredictedVsyncTracer>(*mDispatch)
+                                      : nullptr) {}
 
 VSyncReactor::~VSyncReactor() = default;
 
@@ -48,11 +74,12 @@
 public:
     CallbackRepeater(VSyncDispatch& dispatch, DispSync::Callback* cb, const char* name,
                      nsecs_t period, nsecs_t offset, nsecs_t notBefore)
-          : mCallback(cb),
+          : mName(name),
+            mCallback(cb),
             mRegistration(dispatch,
                           std::bind(&CallbackRepeater::callback, this, std::placeholders::_1,
                                     std::placeholders::_2),
-                          std::string(name)),
+                          mName),
             mPeriod(period),
             mOffset(offset),
             mLastCallTime(notBefore) {}
@@ -87,6 +114,13 @@
         mRegistration.cancel();
     }
 
+    void dump(std::string& result) const {
+        std::lock_guard<std::mutex> lk(mMutex);
+        StringAppendF(&result, "\t%s: mPeriod=%.2f last vsync time %.2fms relative to now (%s)\n",
+                      mName.c_str(), mPeriod / 1e6f, (mLastCallTime - systemTime()) / 1e6f,
+                      mStopped ? "stopped" : "running");
+    }
+
 private:
     void callback(nsecs_t vsynctime, nsecs_t wakeupTime) {
         {
@@ -112,6 +146,7 @@
     // Note change in sign between the two defnitions.
     nsecs_t calculateWorkload() REQUIRES(mMutex) { return mPeriod - mOffset; }
 
+    const std::string mName;
     DispSync::Callback* const mCallback;
 
     std::mutex mutable mMutex;
@@ -287,7 +322,7 @@
     auto it = mCallbacks.find(callback);
     if (it == mCallbacks.end()) {
         // TODO (b/146557561): resolve lastCallbackTime semantics in DispSync i/f.
-        static auto constexpr maxListeners = 3;
+        static auto constexpr maxListeners = 4;
         if (mCallbacks.size() >= maxListeners) {
             ALOGE("callback %s not added, exceeded callback limit of %i (currently %zu)", name,
                   maxListeners, mCallbacks.size());
@@ -324,7 +359,35 @@
 }
 
 void VSyncReactor::dump(std::string& result) const {
-    result += "VsyncReactor in use\n"; // TODO (b/144927823): add more information!
+    std::lock_guard<std::mutex> lk(mMutex);
+    StringAppendF(&result, "VsyncReactor in use\n");
+    StringAppendF(&result, "Has %zu unfired fences\n", mUnfiredFences.size());
+    StringAppendF(&result, "mInternalIgnoreFences=%d mExternalIgnoreFences=%d\n",
+                  mInternalIgnoreFences, mExternalIgnoreFences);
+    StringAppendF(&result, "mMoreSamplesNeeded=%d mPeriodConfirmationInProgress=%d\n",
+                  mMoreSamplesNeeded, mPeriodConfirmationInProgress);
+    if (mPeriodTransitioningTo) {
+        StringAppendF(&result, "mPeriodTransitioningTo=%" PRId64 "\n", *mPeriodTransitioningTo);
+    } else {
+        StringAppendF(&result, "mPeriodTransitioningTo=nullptr\n");
+    }
+
+    if (mLastHwVsync) {
+        StringAppendF(&result, "Last HW vsync was %.2fms ago\n",
+                      (mClock->now() - *mLastHwVsync) / 1e6f);
+    } else {
+        StringAppendF(&result, "No Last HW vsync\n");
+    }
+
+    StringAppendF(&result, "CallbackRepeaters:\n");
+    for (const auto& [callback, repeater] : mCallbacks) {
+        repeater->dump(result);
+    }
+
+    StringAppendF(&result, "VSyncTracker:\n");
+    mTracker->dump(result);
+    StringAppendF(&result, "VSyncDispatch:\n");
+    mDispatch->dump(result);
 }
 
 void VSyncReactor::reset() {}
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index aa8a38d..5ee29f8 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -30,6 +30,7 @@
 class VSyncDispatch;
 class VSyncTracker;
 class CallbackRepeater;
+class PredictedVsyncTracer;
 
 // TODO (b/145217110): consider renaming.
 class VSyncReactor : public android::DispSync {
@@ -74,7 +75,7 @@
     std::unique_ptr<VSyncDispatch> const mDispatch;
     size_t const mPendingLimit;
 
-    std::mutex mMutex;
+    mutable std::mutex mMutex;
     bool mInternalIgnoreFences GUARDED_BY(mMutex) = false;
     bool mExternalIgnoreFences GUARDED_BY(mMutex) = false;
     std::vector<std::shared_ptr<FenceTime>> mUnfiredFences GUARDED_BY(mMutex);
@@ -86,6 +87,8 @@
 
     std::unordered_map<DispSync::Callback*, std::unique_ptr<CallbackRepeater>> mCallbacks
             GUARDED_BY(mMutex);
+
+    const std::unique_ptr<PredictedVsyncTracer> mPredictedVsyncTracer;
 };
 
 class SystemClock : public Clock {
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index a25b8a9..05a6fc3 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -66,6 +66,8 @@
     /* Inform the tracker that the samples it has are not accurate for prediction. */
     virtual void resetModel() = 0;
 
+    virtual void dump(std::string& result) const = 0;
+
 protected:
     VSyncTracker(VSyncTracker const&) = delete;
     VSyncTracker& operator=(VSyncTracker const&) = delete;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index ce0a8a1..2da92b3 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -21,40 +21,34 @@
 //#define LOG_NDEBUG 0
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
-#include <sys/types.h>
-#include <errno.h>
-#include <dlfcn.h>
+#include "SurfaceFlinger.h"
 
-#include <algorithm>
-#include <cinttypes>
-#include <cmath>
-#include <cstdint>
-#include <functional>
-#include <mutex>
-#include <optional>
-#include <unordered_map>
-
+#include <android/configuration.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/power/1.0/IPower.h>
 #include <android/native_window.h>
-
-#include <cutils/properties.h>
-#include <log/log.h>
-
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/PermissionCache.h>
-
 #include <compositionengine/CompositionEngine.h>
 #include <compositionengine/CompositionRefreshArgs.h>
 #include <compositionengine/Display.h>
 #include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/DisplayCreationArgs.h>
 #include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/OutputLayer.h>
 #include <compositionengine/RenderSurface.h>
 #include <compositionengine/impl/OutputCompositionState.h>
+#include <configstore/Utils.h>
+#include <cutils/compiler.h>
+#include <cutils/properties.h>
+#include <dlfcn.h>
 #include <dvr/vr_flinger.h>
+#include <errno.h>
 #include <gui/BufferQueue.h>
 #include <gui/DebugEGLImageTracker.h>
-
 #include <gui/GuiConfig.h>
 #include <gui/IDisplayEventConnection.h>
 #include <gui/IProducerListener.h>
@@ -63,7 +57,13 @@
 #include <gui/LayerState.h>
 #include <gui/Surface.h>
 #include <input/IInputFlinger.h>
+#include <layerproto/LayerProtoParser.h>
+#include <log/log.h>
+#include <private/android_filesystem_config.h>
+#include <private/gui/SyncFeatures.h>
 #include <renderengine/RenderEngine.h>
+#include <statslog.h>
+#include <sys/types.h>
 #include <ui/ColorSpace.h>
 #include <ui/DebugUtils.h>
 #include <ui/DisplayConfig.h>
@@ -80,8 +80,14 @@
 #include <utils/Trace.h>
 #include <utils/misc.h>
 
-#include <private/android_filesystem_config.h>
-#include <private/gui/SyncFeatures.h>
+#include <algorithm>
+#include <cinttypes>
+#include <cmath>
+#include <cstdint>
+#include <functional>
+#include <mutex>
+#include <optional>
+#include <unordered_map>
 
 #include "BufferLayer.h"
 #include "BufferQueueLayer.h"
@@ -90,23 +96,19 @@
 #include "Colorizer.h"
 #include "ContainerLayer.h"
 #include "DisplayDevice.h"
-#include "EffectLayer.h"
-#include "Layer.h"
-#include "LayerVector.h"
-#include "MonitoredProducer.h"
-#include "NativeWindowSurface.h"
-#include "RefreshRateOverlay.h"
-#include "StartPropertySetThread.h"
-#include "SurfaceFlinger.h"
-#include "SurfaceInterceptor.h"
-
 #include "DisplayHardware/ComposerHal.h"
 #include "DisplayHardware/DisplayIdentification.h"
 #include "DisplayHardware/FramebufferSurface.h"
 #include "DisplayHardware/HWComposer.h"
 #include "DisplayHardware/VirtualDisplaySurface.h"
+#include "EffectLayer.h"
 #include "Effects/Daltonizer.h"
 #include "FrameTracer/FrameTracer.h"
+#include "Layer.h"
+#include "LayerVector.h"
+#include "MonitoredProducer.h"
+#include "NativeWindowSurface.h"
+#include "RefreshRateOverlay.h"
 #include "RegionSamplingThread.h"
 #include "Scheduler/DispSync.h"
 #include "Scheduler/DispSyncSource.h"
@@ -115,23 +117,13 @@
 #include "Scheduler/MessageQueue.h"
 #include "Scheduler/PhaseOffsets.h"
 #include "Scheduler/Scheduler.h"
+#include "StartPropertySetThread.h"
+#include "SurfaceFlingerProperties.h"
+#include "SurfaceInterceptor.h"
 #include "TimeStats/TimeStats.h"
-
-#include <cutils/compiler.h>
-
 #include "android-base/parseint.h"
 #include "android-base/stringprintf.h"
 
-#include <android/configuration.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/power/1.0/IPower.h>
-#include <configstore/Utils.h>
-
-#include <layerproto/LayerProtoParser.h>
-#include "SurfaceFlingerProperties.h"
-
 namespace android {
 
 using namespace std::string_literals;
@@ -216,6 +208,7 @@
 const String16 sAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER");
 const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER");
 const String16 sDump("android.permission.DUMP");
+const char* KERNEL_IDLE_TIMER_PROP = "vendor.display.enable_kernel_idle_timer";
 
 // ---------------------------------------------------------------------------
 int64_t SurfaceFlinger::dispSyncPresentTimeOffset;
@@ -224,6 +217,8 @@
 bool SurfaceFlinger::hasSyncFramework;
 bool SurfaceFlinger::useVrFlinger;
 int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers;
+uint32_t SurfaceFlinger::maxGraphicsWidth;
+uint32_t SurfaceFlinger::maxGraphicsHeight;
 bool SurfaceFlinger::hasWideColorDisplay;
 ui::Rotation SurfaceFlinger::internalDisplayOrientation = ui::ROTATION_0;
 bool SurfaceFlinger::useColorManagement;
@@ -290,6 +285,9 @@
 
     maxFrameBufferAcquiredBuffers = max_frame_buffer_acquired_buffers(2);
 
+    maxGraphicsWidth = std::max(max_graphics_width(0), 0);
+    maxGraphicsHeight = std::max(max_graphics_height(0), 0);
+
     hasWideColorDisplay = has_wide_color_display(false);
 
     useColorManagement = use_color_management(false);
@@ -334,6 +332,9 @@
     property_get("ro.bq.gpu_to_cpu_unsupported", value, "0");
     mGpuToCpuSupported = !atoi(value);
 
+    property_get("ro.build.type", value, "user");
+    mIsUserBuild = strcmp(value, "user") == 0;
+
     property_get("debug.sf.showupdates", value, "0");
     mDebugRegion = atoi(value);
 
@@ -363,15 +364,12 @@
 
     property_get("ro.surface_flinger.supports_background_blur", value, "0");
     bool supportsBlurs = atoi(value);
-    property_get("debug.sf.disable_blurs", value, "0");
-    bool disableBlurs = atoi(value);
-    mEnableBlurs = supportsBlurs && !disableBlurs;
-    ALOGI_IF(!mEnableBlurs, "Disabling blur effects. supported: %d, disabled: %d", supportsBlurs,
-             disableBlurs);
+    mSupportsBlur = supportsBlurs;
+    ALOGI_IF(!mSupportsBlur, "Disabling blur effects, they are not supported.");
     property_get("ro.sf.blurs_are_expensive", value, "0");
     mBlursAreExpensive = atoi(value);
 
-    const size_t defaultListSize = MAX_LAYERS;
+    const size_t defaultListSize = ISurfaceComposer::MAX_LAYERS;
     auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize));
     mMaxGraphicBufferProducerListSize = (listSize > 0) ? size_t(listSize) : defaultListSize;
 
@@ -408,6 +406,7 @@
 void SurfaceFlinger::binderDied(const wp<IBinder>& /* who */)
 {
     // the window manager died on us. prepare its eulogy.
+    mBootFinished = false;
 
     // restore initial conditions (default device unblank, etc)
     initializeDisplays();
@@ -524,6 +523,11 @@
 
 void SurfaceFlinger::bootFinished()
 {
+    if (mBootFinished == true) {
+        ALOGE("Extra call to bootFinished");
+        return;
+    }
+    mBootFinished = true;
     if (mStartPropertySetThread->join() != NO_ERROR) {
         ALOGE("Join StartPropertySetThread failed!");
     }
@@ -563,6 +567,7 @@
 
     postMessageAsync(new LambdaMessage([this]() NO_THREAD_SAFETY_ANALYSIS {
         readPersistentProperties();
+        mPowerAdvisor.onBootFinished();
         mBootStage = BootStage::FINISHED;
 
         if (property_get_bool("sf.debug.show_refresh_rate_overlay", false)) {
@@ -618,7 +623,7 @@
                 .setUseColorManagerment(useColorManagement)
                 .setEnableProtectedContext(enable_protected_contents(false))
                 .setPrecacheToneMapperShaderOnly(false)
-                .setSupportsBackgroundBlur(mEnableBlurs)
+                .setSupportsBackgroundBlur(mSupportsBlur)
                 .setContextPriority(useContextPriority
                         ? renderengine::RenderEngine::ContextPriority::HIGH
                         : renderengine::RenderEngine::ContextPriority::MEDIUM)
@@ -700,6 +705,11 @@
 
     property_get("persist.sys.sf.color_mode", value, "0");
     mForceColorMode = static_cast<ColorMode>(atoi(value));
+
+    property_get("persist.sys.sf.disable_blurs", value, "0");
+    bool disableBlurs = atoi(value);
+    mDisableBlurs = disableBlurs;
+    ALOGI_IF(disableBlurs, "Disabling blur effects, user preference.");
 }
 
 void SurfaceFlinger::startBootAnim() {
@@ -857,6 +867,7 @@
         const auto offsets = mPhaseConfiguration->getOffsetsForRefreshRate(config.refreshRate);
         config.appVsyncOffset = offsets.late.app;
         config.sfVsyncOffset = offsets.late.sf;
+        config.configGroup = hwConfig->getConfigGroup();
 
         // This is how far in advance a buffer must be queued for
         // presentation at a given time.  If you want a buffer to appear
@@ -906,18 +917,27 @@
 
 void SurfaceFlinger::setDesiredActiveConfig(const ActiveConfigInfo& info) {
     ATRACE_CALL();
-    auto refreshRate = mRefreshRateConfigs->getRefreshRateFromConfigId(info.configId);
+    auto& refreshRate = mRefreshRateConfigs->getRefreshRateFromConfigId(info.configId);
     ALOGV("setDesiredActiveConfig(%s)", refreshRate.name.c_str());
 
-    // Don't check against the current mode yet. Worst case we set the desired
-    // config twice. However event generation config might have changed so we need to update it
-    // accordingly
     std::lock_guard<std::mutex> lock(mActiveConfigLock);
-    const Scheduler::ConfigEvent prevConfig = mDesiredActiveConfig.event;
-    mDesiredActiveConfig = info;
-    mDesiredActiveConfig.event = mDesiredActiveConfig.event | prevConfig;
+    if (mDesiredActiveConfigChanged) {
+        // If a config change is pending, just cache the latest request in
+        // mDesiredActiveConfig
+        const Scheduler::ConfigEvent prevConfig = mDesiredActiveConfig.event;
+        mDesiredActiveConfig = info;
+        mDesiredActiveConfig.event = mDesiredActiveConfig.event | prevConfig;
+    } else {
+        // Check is we are already at the desired config
+        const auto display = getDefaultDisplayDeviceLocked();
+        if (!display || display->getActiveConfig() == refreshRate.configId) {
+            return;
+        }
 
-    if (!mDesiredActiveConfigChanged) {
+        // Initiate a config change.
+        mDesiredActiveConfigChanged = true;
+        mDesiredActiveConfig = info;
+
         // This will trigger HWC refresh without resetting the idle timer.
         repaintEverythingForHWC();
         // Start receiving vsync samples now, so that we can detect a period
@@ -930,7 +950,6 @@
         mPhaseConfiguration->setRefreshRateFps(refreshRate.fps);
         mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
     }
-    mDesiredActiveConfigChanged = true;
 
     if (mRefreshRateOverlay) {
         mRefreshRateOverlay->changeRefreshRate(refreshRate);
@@ -979,7 +998,7 @@
     mRefreshRateStats->setConfigMode(mUpcomingActiveConfig.configId);
     display->setActiveConfig(mUpcomingActiveConfig.configId);
 
-    auto refreshRate =
+    auto& refreshRate =
             mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId);
     mPhaseConfiguration->setRefreshRateFps(refreshRate.fps);
     mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
@@ -999,7 +1018,7 @@
     mDesiredActiveConfig.event = Scheduler::ConfigEvent::None;
     mDesiredActiveConfigChanged = false;
 
-    auto const refreshRate =
+    const auto& refreshRate =
             mRefreshRateConfigs->getRefreshRateFromConfigId(mDesiredActiveConfig.configId);
     mScheduler->resyncToHardwareVsync(true, refreshRate.vsyncPeriod);
     mPhaseConfiguration->setRefreshRateFps(refreshRate.fps);
@@ -1010,7 +1029,7 @@
     ATRACE_CALL();
     ALOGV("performSetActiveConfig");
     if (mCheckPendingFence) {
-        if (previousFrameMissed()) {
+        if (previousFramePending()) {
             // fence has not signaled yet. wait for the next invalidate
             mEventQueue->invalidate();
             return true;
@@ -1032,7 +1051,7 @@
         desiredActiveConfig = mDesiredActiveConfig;
     }
 
-    auto refreshRate =
+    auto& refreshRate =
             mRefreshRateConfigs->getRefreshRateFromConfigId(desiredActiveConfig.configId);
     ALOGV("performSetActiveConfig changing active config to %d(%s)", refreshRate.configId.value(),
           refreshRate.name.c_str());
@@ -1069,7 +1088,9 @@
                                                            mUpcomingActiveConfig.configId.value(),
                                                            constraints, &outTimeline);
     if (status != NO_ERROR) {
-        LOG_ALWAYS_FATAL("setActiveConfigWithConstraints failed: %d", status);
+        // setActiveConfigWithConstraints may fail if a hotplug event is just about
+        // to be sent. We just log the error in this case.
+        ALOGW("setActiveConfigWithConstraints failed: %d", status);
         return false;
     }
 
@@ -1755,13 +1776,17 @@
     setTransactionFlags(eDisplayTransactionNeeded);
 }
 
-bool SurfaceFlinger::previousFrameMissed(int graceTimeMs) NO_THREAD_SAFETY_ANALYSIS {
-    ATRACE_CALL();
+sp<Fence> SurfaceFlinger::previousFrameFence() NO_THREAD_SAFETY_ANALYSIS {
     // We are storing the last 2 present fences. If sf's phase offset is to be
     // woken up before the actual vsync but targeting the next vsync, we need to check
     // fence N-2
-    const sp<Fence>& fence = mVSyncModulator->getOffsets().sf > 0 ? mPreviousPresentFences[0]
-                                                                  : mPreviousPresentFences[1];
+    return mVSyncModulator->getOffsets().sf > 0 ? mPreviousPresentFences[0]
+                                                : mPreviousPresentFences[1];
+}
+
+bool SurfaceFlinger::previousFramePending(int graceTimeMs) NO_THREAD_SAFETY_ANALYSIS {
+    ATRACE_CALL();
+    const sp<Fence>& fence = previousFrameFence();
 
     if (fence == Fence::NO_FENCE) {
         return false;
@@ -1774,6 +1799,16 @@
     return (fence->getStatus() == Fence::Status::Unsignaled);
 }
 
+nsecs_t SurfaceFlinger::previousFramePresentTime() NO_THREAD_SAFETY_ANALYSIS {
+    const sp<Fence>& fence = previousFrameFence();
+
+    if (fence == Fence::NO_FENCE) {
+        return Fence::SIGNAL_TIME_INVALID;
+    }
+
+    return fence->getSignalTime();
+}
+
 void SurfaceFlinger::populateExpectedPresentTime() {
     DisplayStatInfo stats;
     mScheduler->getDisplayStatInfo(&stats);
@@ -1791,6 +1826,7 @@
             // calculate the expected present time once and use the cached
             // value throughout this frame to make sure all layers are
             // seeing this same value.
+            const nsecs_t lastExpectedPresentTime = mExpectedPresentTime.load();
             populateExpectedPresentTime();
 
             // When Backpressure propagation is enabled we want to give a small grace period
@@ -1801,17 +1837,41 @@
                      (mPropagateBackpressureClientComposition || !mHadClientComposition))
                     ? 1
                     : 0;
-            const TracedOrdinal<bool> frameMissed = {"FrameMissed",
-                                                     previousFrameMissed(
-                                                             graceTimeForPresentFenceMs)};
-            const TracedOrdinal<bool> hwcFrameMissed = {"HwcFrameMissed",
+
+            // Pending frames may trigger backpressure propagation.
+            const TracedOrdinal<bool> framePending = {"PrevFramePending",
+                                                      previousFramePending(
+                                                              graceTimeForPresentFenceMs)};
+
+            // Frame missed counts for metrics tracking.
+            // A frame is missed if the prior frame is still pending. If no longer pending,
+            // then we still count the frame as missed if the predicted present time
+            // was further in the past than when the fence actually fired.
+
+            // Add some slop to correct for drift. This should generally be
+            // smaller than a typical frame duration, but should not be so small
+            // that it reports reasonable drift as a missed frame.
+            DisplayStatInfo stats;
+            mScheduler->getDisplayStatInfo(&stats);
+            const nsecs_t frameMissedSlop = stats.vsyncPeriod / 2;
+            const nsecs_t previousPresentTime = previousFramePresentTime();
+            const TracedOrdinal<bool> frameMissed =
+                    {"PrevFrameMissed",
+                     framePending ||
+                             (previousPresentTime >= 0 &&
+                              (lastExpectedPresentTime < previousPresentTime - frameMissedSlop))};
+            const TracedOrdinal<bool> hwcFrameMissed = {"PrevHwcFrameMissed",
                                                         mHadDeviceComposition && frameMissed};
-            const TracedOrdinal<bool> gpuFrameMissed = {"GpuFrameMissed",
+            const TracedOrdinal<bool> gpuFrameMissed = {"PrevGpuFrameMissed",
                                                         mHadClientComposition && frameMissed};
 
             if (frameMissed) {
                 mFrameMissedCount++;
                 mTimeStats->incrementMissedFrames();
+                if (mMissedFrameJankCount == 0) {
+                    mMissedFrameJankStart = systemTime();
+                }
+                mMissedFrameJankCount++;
             }
 
             if (hwcFrameMissed) {
@@ -1822,7 +1882,7 @@
                 mGpuFrameMissedCount++;
             }
 
-            if (frameMissed && mPropagateBackpressure) {
+            if (framePending && mPropagateBackpressure) {
                 if ((hwcFrameMissed && !gpuFrameMissed) ||
                     mPropagateBackpressureClientComposition) {
                     signalLayerUpdate();
@@ -1830,6 +1890,40 @@
                 }
             }
 
+            // Our jank window is always at least 100ms since we missed a
+            // frame...
+            static constexpr nsecs_t kMinJankyDuration =
+                    std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count();
+            // ...but if it's larger than 1s then we missed the trace cutoff.
+            static constexpr nsecs_t kMaxJankyDuration =
+                    std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
+            // If we're in a user build then don't push any atoms
+            if (!mIsUserBuild && mMissedFrameJankCount > 0) {
+                const auto displayDevice = getDefaultDisplayDeviceLocked();
+                // Only report jank when the display is on, as displays in DOZE
+                // power mode may operate at a different frame rate than is
+                // reported in their config, which causes noticeable (but less
+                // severe) jank.
+                if (displayDevice && displayDevice->getPowerMode() == HWC_POWER_MODE_NORMAL) {
+                    const nsecs_t currentTime = systemTime();
+                    const nsecs_t jankDuration = currentTime - mMissedFrameJankStart;
+                    if (jankDuration > kMinJankyDuration && jankDuration < kMaxJankyDuration) {
+                        ATRACE_NAME("Jank detected");
+                        ALOGD("Detected janky event. Missed frames: %d", mMissedFrameJankCount);
+                        const int32_t jankyDurationMillis = jankDuration / (1000 * 1000);
+                        android::util::stats_write(android::util::DISPLAY_JANK_REPORTED,
+                                                   jankyDurationMillis, mMissedFrameJankCount);
+                    }
+
+                    // We either reported a jank event or we missed the trace
+                    // window, so clear counters here.
+                    if (jankDuration > kMinJankyDuration) {
+                        mMissedFrameJankCount = 0;
+                        mMissedFrameJankStart = 0;
+                    }
+                }
+            }
+
             // Now that we're going to make it to the handleMessageTransaction()
             // call below it's safe to call updateVrFlinger(), which will
             // potentially trigger a display handoff.
@@ -2325,16 +2419,18 @@
 }
 
 sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal(
-        const wp<IBinder>& displayToken, const std::optional<DisplayId>& displayId,
-        const DisplayDeviceState& state, const sp<compositionengine::DisplaySurface>& dispSurface,
+        const wp<IBinder>& displayToken,
+        std::shared_ptr<compositionengine::Display> compositionDisplay,
+        const DisplayDeviceState& state,
+        const sp<compositionengine::DisplaySurface>& displaySurface,
         const sp<IGraphicBufferProducer>& producer) {
-    DisplayDeviceCreationArgs creationArgs(this, displayToken, displayId);
+    auto displayId = compositionDisplay->getDisplayId();
+    DisplayDeviceCreationArgs creationArgs(this, displayToken, compositionDisplay);
     creationArgs.sequenceId = state.sequenceId;
     creationArgs.isSecure = state.isSecure;
-    creationArgs.displaySurface = dispSurface;
+    creationArgs.displaySurface = displaySurface;
     creationArgs.hasWideColorGamut = false;
     creationArgs.supportedPerFrameMetadata = 0;
-    creationArgs.powerAdvisor = displayId ? &mPowerAdvisor : nullptr;
 
     if (const auto& physical = state.physical) {
         creationArgs.connectionType = physical->type;
@@ -2379,7 +2475,7 @@
     // virtual displays are always considered enabled
     creationArgs.initialPowerMode = state.isVirtual() ? HWC_POWER_MODE_NORMAL : HWC_POWER_MODE_OFF;
 
-    sp<DisplayDevice> display = getFactory().createDisplayDevice(std::move(creationArgs));
+    sp<DisplayDevice> display = getFactory().createDisplayDevice(creationArgs);
 
     if (maxFrameBufferAcquiredBuffers >= 3) {
         nativeWindowSurface->preallocateBuffers();
@@ -2408,6 +2504,140 @@
     return display;
 }
 
+void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken,
+                                         const DisplayDeviceState& state) {
+    int width = 0;
+    int height = 0;
+    ui::PixelFormat pixelFormat = static_cast<ui::PixelFormat>(PIXEL_FORMAT_UNKNOWN);
+    if (state.physical) {
+        const auto& activeConfig =
+                getCompositionEngine().getHwComposer().getActiveConfig(state.physical->id);
+        width = activeConfig->getWidth();
+        height = activeConfig->getHeight();
+        pixelFormat = static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888);
+    } else if (state.surface != nullptr) {
+        int status = state.surface->query(NATIVE_WINDOW_WIDTH, &width);
+        ALOGE_IF(status != NO_ERROR, "Unable to query width (%d)", status);
+        status = state.surface->query(NATIVE_WINDOW_HEIGHT, &height);
+        ALOGE_IF(status != NO_ERROR, "Unable to query height (%d)", status);
+        int intPixelFormat;
+        status = state.surface->query(NATIVE_WINDOW_FORMAT, &intPixelFormat);
+        ALOGE_IF(status != NO_ERROR, "Unable to query format (%d)", status);
+        pixelFormat = static_cast<ui::PixelFormat>(intPixelFormat);
+    } else {
+        // Virtual displays without a surface are dormant:
+        // they have external state (layer stack, projection,
+        // etc.) but no internal state (i.e. a DisplayDevice).
+        return;
+    }
+
+    compositionengine::DisplayCreationArgsBuilder builder;
+    if (const auto& physical = state.physical) {
+        builder.setPhysical({physical->id, physical->type});
+    }
+    builder.setPixels(ui::Size(width, height));
+    builder.setPixelFormat(pixelFormat);
+    builder.setIsSecure(state.isSecure);
+    builder.setLayerStackId(state.layerStack);
+    builder.setPowerAdvisor(&mPowerAdvisor);
+    builder.setUseHwcVirtualDisplays(mUseHwcVirtualDisplays || getHwComposer().isUsingVrComposer());
+    builder.setName(state.displayName);
+    const auto compositionDisplay = getCompositionEngine().createDisplay(builder.build());
+
+    sp<compositionengine::DisplaySurface> displaySurface;
+    sp<IGraphicBufferProducer> producer;
+    sp<IGraphicBufferProducer> bqProducer;
+    sp<IGraphicBufferConsumer> bqConsumer;
+    getFactory().createBufferQueue(&bqProducer, &bqConsumer, /*consumerIsSurfaceFlinger =*/false);
+
+    std::optional<DisplayId> displayId = compositionDisplay->getId();
+
+    if (state.isVirtual()) {
+        sp<VirtualDisplaySurface> vds =
+                new VirtualDisplaySurface(getHwComposer(), displayId, state.surface, bqProducer,
+                                          bqConsumer, state.displayName);
+
+        displaySurface = vds;
+        producer = vds;
+    } else {
+        ALOGE_IF(state.surface != nullptr,
+                 "adding a supported display, but rendering "
+                 "surface is provided (%p), ignoring it",
+                 state.surface.get());
+
+        LOG_ALWAYS_FATAL_IF(!displayId);
+        displaySurface = new FramebufferSurface(getHwComposer(), *displayId, bqConsumer,
+                                                maxGraphicsWidth, maxGraphicsHeight);
+        producer = bqProducer;
+    }
+
+    if (displaySurface != nullptr) {
+        mDisplays.emplace(displayToken,
+                          setupNewDisplayDeviceInternal(displayToken, compositionDisplay, state,
+                                                        displaySurface, producer));
+        if (!state.isVirtual()) {
+            LOG_ALWAYS_FATAL_IF(!displayId);
+            dispatchDisplayHotplugEvent(displayId->value, true);
+        }
+
+        const auto displayDevice = mDisplays[displayToken];
+        if (displayDevice->isPrimary()) {
+            mScheduler->onPrimaryDisplayAreaChanged(displayDevice->getWidth() *
+                                                    displayDevice->getHeight());
+        }
+    }
+}
+
+void SurfaceFlinger::processDisplayRemoved(const wp<IBinder>& displayToken) {
+    if (const auto display = getDisplayDeviceLocked(displayToken)) {
+        // Save display ID before disconnecting.
+        const auto displayId = display->getId();
+        display->disconnect();
+
+        if (!display->isVirtual()) {
+            LOG_ALWAYS_FATAL_IF(!displayId);
+            dispatchDisplayHotplugEvent(displayId->value, false);
+        }
+    }
+
+    mDisplays.erase(displayToken);
+}
+
+void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken,
+                                           const DisplayDeviceState& currentState,
+                                           const DisplayDeviceState& drawingState) {
+    const sp<IBinder> currentBinder = IInterface::asBinder(currentState.surface);
+    const sp<IBinder> drawingBinder = IInterface::asBinder(drawingState.surface);
+    if (currentBinder != drawingBinder) {
+        // changing the surface is like destroying and recreating the DisplayDevice
+        if (const auto display = getDisplayDeviceLocked(displayToken)) {
+            display->disconnect();
+        }
+        mDisplays.erase(displayToken);
+        processDisplayAdded(displayToken, currentState);
+        return;
+    }
+
+    if (const auto display = getDisplayDeviceLocked(displayToken)) {
+        if (currentState.layerStack != drawingState.layerStack) {
+            display->setLayerStack(currentState.layerStack);
+        }
+        if ((currentState.orientation != drawingState.orientation) ||
+            (currentState.viewport != drawingState.viewport) ||
+            (currentState.frame != drawingState.frame)) {
+            display->setProjection(currentState.orientation, currentState.viewport,
+                                   currentState.frame);
+        }
+        if (currentState.width != drawingState.width ||
+            currentState.height != drawingState.height) {
+            display->setDisplaySize(currentState.width, currentState.height);
+            if (display->isPrimary()) {
+                mScheduler->onPrimaryDisplayAreaChanged(currentState.width * currentState.height);
+            }
+        }
+    }
+}
+
 void SurfaceFlinger::processDisplayChangesLocked() {
     // here we take advantage of Vector's copy-on-write semantics to
     // improve performance by skipping the transaction entirely when
@@ -2416,142 +2646,31 @@
     const KeyedVector<wp<IBinder>, DisplayDeviceState>& draw(mDrawingState.displays);
     if (!curr.isIdenticalTo(draw)) {
         mVisibleRegionsDirty = true;
-        const size_t cc = curr.size();
-        size_t dc = draw.size();
 
         // find the displays that were removed
         // (ie: in drawing state but not in current state)
         // also handle displays that changed
         // (ie: displays that are in both lists)
-        for (size_t i = 0; i < dc;) {
-            const ssize_t j = curr.indexOfKey(draw.keyAt(i));
+        for (size_t i = 0; i < draw.size(); i++) {
+            const wp<IBinder>& displayToken = draw.keyAt(i);
+            const ssize_t j = curr.indexOfKey(displayToken);
             if (j < 0) {
                 // in drawing state but not in current state
-                if (const auto display = getDisplayDeviceLocked(draw.keyAt(i))) {
-                    // Save display ID before disconnecting.
-                    const auto displayId = display->getId();
-                    display->disconnect();
-
-                    if (!display->isVirtual()) {
-                        LOG_ALWAYS_FATAL_IF(!displayId);
-                        dispatchDisplayHotplugEvent(displayId->value, false);
-                    }
-                }
-
-                mDisplays.erase(draw.keyAt(i));
+                processDisplayRemoved(displayToken);
             } else {
                 // this display is in both lists. see if something changed.
-                const DisplayDeviceState& state(curr[j]);
-                const wp<IBinder>& displayToken = curr.keyAt(j);
-                const sp<IBinder> state_binder = IInterface::asBinder(state.surface);
-                const sp<IBinder> draw_binder = IInterface::asBinder(draw[i].surface);
-                if (state_binder != draw_binder) {
-                    // changing the surface is like destroying and
-                    // recreating the DisplayDevice, so we just remove it
-                    // from the drawing state, so that it get re-added
-                    // below.
-                    if (const auto display = getDisplayDeviceLocked(displayToken)) {
-                        display->disconnect();
-                    }
-                    mDisplays.erase(displayToken);
-                    mDrawingState.displays.removeItemsAt(i);
-                    dc--;
-                    // at this point we must loop to the next item
-                    continue;
-                }
-
-                if (const auto display = getDisplayDeviceLocked(displayToken)) {
-                    if (state.layerStack != draw[i].layerStack) {
-                        display->setLayerStack(state.layerStack);
-                    }
-                    if ((state.orientation != draw[i].orientation) ||
-                        (state.viewport != draw[i].viewport) || (state.frame != draw[i].frame)) {
-                        display->setProjection(state.orientation, state.viewport, state.frame);
-                    }
-                    if (state.width != draw[i].width || state.height != draw[i].height) {
-                        display->setDisplaySize(state.width, state.height);
-                        if (display->isPrimary()) {
-                            mScheduler->onPrimaryDisplayAreaChanged(state.width * state.height);
-                        }
-                    }
-                }
+                const DisplayDeviceState& currentState = curr[j];
+                const DisplayDeviceState& drawingState = draw[i];
+                processDisplayChanged(displayToken, currentState, drawingState);
             }
-            ++i;
         }
 
         // find displays that were added
         // (ie: in current state but not in drawing state)
-        for (size_t i = 0; i < cc; i++) {
-            if (draw.indexOfKey(curr.keyAt(i)) < 0) {
-                const DisplayDeviceState& state(curr[i]);
-
-                sp<compositionengine::DisplaySurface> dispSurface;
-                sp<IGraphicBufferProducer> producer;
-                sp<IGraphicBufferProducer> bqProducer;
-                sp<IGraphicBufferConsumer> bqConsumer;
-                getFactory().createBufferQueue(&bqProducer, &bqConsumer, false);
-
-                std::optional<DisplayId> displayId;
-                if (state.isVirtual()) {
-                    // Virtual displays without a surface are dormant:
-                    // they have external state (layer stack, projection,
-                    // etc.) but no internal state (i.e. a DisplayDevice).
-                    if (state.surface != nullptr) {
-                        // Allow VR composer to use virtual displays.
-                        if (mUseHwcVirtualDisplays || getHwComposer().isUsingVrComposer()) {
-                            int width = 0;
-                            int status = state.surface->query(NATIVE_WINDOW_WIDTH, &width);
-                            ALOGE_IF(status != NO_ERROR, "Unable to query width (%d)", status);
-                            int height = 0;
-                            status = state.surface->query(NATIVE_WINDOW_HEIGHT, &height);
-                            ALOGE_IF(status != NO_ERROR, "Unable to query height (%d)", status);
-                            int intFormat = 0;
-                            status = state.surface->query(NATIVE_WINDOW_FORMAT, &intFormat);
-                            ALOGE_IF(status != NO_ERROR, "Unable to query format (%d)", status);
-                            auto format = static_cast<ui::PixelFormat>(intFormat);
-
-                            displayId =
-                                    getHwComposer().allocateVirtualDisplay(width, height, &format);
-                        }
-
-                        // TODO: Plumb requested format back up to consumer
-
-                        sp<VirtualDisplaySurface> vds =
-                                new VirtualDisplaySurface(getHwComposer(), displayId, state.surface,
-                                                          bqProducer, bqConsumer,
-                                                          state.displayName);
-
-                        dispSurface = vds;
-                        producer = vds;
-                    }
-                } else {
-                    ALOGE_IF(state.surface != nullptr,
-                             "adding a supported display, but rendering "
-                             "surface is provided (%p), ignoring it",
-                             state.surface.get());
-
-                    LOG_FATAL_IF(!state.physical);
-                    displayId = state.physical->id;
-                    dispSurface = new FramebufferSurface(getHwComposer(), *displayId, bqConsumer);
-                    producer = bqProducer;
-                }
-
-                const wp<IBinder>& displayToken = curr.keyAt(i);
-                if (dispSurface != nullptr) {
-                    mDisplays.emplace(displayToken,
-                                      setupNewDisplayDeviceInternal(displayToken, displayId, state,
-                                                                    dispSurface, producer));
-                    if (!state.isVirtual()) {
-                        LOG_ALWAYS_FATAL_IF(!displayId);
-                        dispatchDisplayHotplugEvent(displayId->value, true);
-                    }
-
-                    const auto displayDevice = mDisplays[displayToken];
-                    if (displayDevice->isPrimary()) {
-                        mScheduler->onPrimaryDisplayAreaChanged(displayDevice->getWidth() *
-                                                                displayDevice->getHeight());
-                    }
-                }
+        for (size_t i = 0; i < curr.size(); i++) {
+            const wp<IBinder>& displayToken = curr.keyAt(i);
+            if (draw.indexOfKey(displayToken) < 0) {
+                processDisplayAdded(displayToken, curr[i]);
             }
         }
     }
@@ -2717,6 +2836,19 @@
 void SurfaceFlinger::updateInputWindowInfo() {
     std::vector<InputWindowInfo> inputHandles;
 
+    // We use a simple caching algorithm here. mInputDirty begins as true,
+    // after we call setInputWindows we set it to false, so
+    // in the future we wont call it again.. We set input dirty to true again
+    // when any layer that hasInput() has a transaction performed on it
+    // or when any parent or relative parent of such a layer has a transaction
+    // performed on it. Not all of these transactions will really result in
+    // input changes but all input changes will spring from these transactions
+    // so the cache is safe but not optimal. It seems like it might be annoyingly
+    // costly to cache and comapre the actual InputWindowHandle vector though.
+    if (!mInputDirty && !mInputWindowCommands.syncInputWindows) {
+        return;
+    }
+
     mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
         if (layer->hasInput()) {
             // When calculating the screen bounds we ignore the transparent region since it may
@@ -2728,10 +2860,12 @@
     mInputFlinger->setInputWindows(inputHandles,
                                    mInputWindowCommands.syncInputWindows ? mSetInputWindowsListener
                                                                          : nullptr);
+
+    mInputDirty = false;
 }
 
 void SurfaceFlinger::commitInputWindowCommands() {
-    mInputWindowCommands = mPendingInputWindowCommands;
+    mInputWindowCommands.merge(mPendingInputWindowCommands);
     mPendingInputWindowCommands.clear();
 }
 
@@ -3006,9 +3140,9 @@
             parent = parentLayer;
         }
 
-        if (mNumLayers >= MAX_LAYERS) {
+        if (mNumLayers >= ISurfaceComposer::MAX_LAYERS) {
             ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(),
-                  MAX_LAYERS);
+                  ISurfaceComposer::MAX_LAYERS);
             return NO_MEMORY;
         }
 
@@ -3498,7 +3632,7 @@
         if (layer->setCornerRadius(s.cornerRadius))
             flags |= eTraversalNeeded;
     }
-    if (what & layer_state_t::eBackgroundBlurRadiusChanged) {
+    if (what & layer_state_t::eBackgroundBlurRadiusChanged && !mDisableBlurs) {
         if (layer->setBackgroundBlurRadius(s.backgroundBlurRadius)) flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eLayerStackChanged) {
@@ -3668,10 +3802,6 @@
 
 uint32_t SurfaceFlinger::addInputWindowCommands(const InputWindowCommands& inputWindowCommands) {
     uint32_t flags = 0;
-    if (!inputWindowCommands.transferTouchFocusCommands.empty()) {
-        flags |= eTraversalNeeded;
-    }
-
     if (inputWindowCommands.syncInputWindows) {
         flags |= eTraversalNeeded;
     }
@@ -4397,14 +4527,20 @@
     result.append("\n");
 }
 
-LayersProto SurfaceFlinger::dumpDrawingStateProto(uint32_t traceFlags) const {
+LayersProto SurfaceFlinger::dumpDrawingStateProto(
+        uint32_t traceFlags, const sp<const DisplayDevice>& displayDevice) const {
     LayersProto layersProto;
     for (const sp<Layer>& layer : mDrawingState.layersSortedByZ) {
-        layer->writeToProto(layersProto, traceFlags);
+        layer->writeToProto(layersProto, traceFlags, displayDevice);
     }
+
     return layersProto;
 }
 
+void SurfaceFlinger::dumpHwc(std::string& result) const {
+    getHwComposer().dump(result);
+}
+
 void SurfaceFlinger::dumpOffscreenLayersProto(LayersProto& layersProto, uint32_t traceFlags) const {
     // Add a fake invisible root layer to the proto output and parent all the offscreen layers to
     // it.
@@ -4419,14 +4555,18 @@
         rootProto->add_children(offscreenLayer->sequence);
 
         // Add layer
-        LayerProto* layerProto = offscreenLayer->writeToProto(layersProto, traceFlags);
+        LayerProto* layerProto =
+                offscreenLayer->writeToProto(layersProto, traceFlags, nullptr /*device*/);
         layerProto->set_parent(offscreenRootLayerId);
     }
 }
 
 LayersProto SurfaceFlinger::dumpProtoFromMainThread(uint32_t traceFlags) {
     LayersProto layersProto;
-    postMessageSync(new LambdaMessage([&]() { layersProto = dumpDrawingStateProto(traceFlags); }));
+    postMessageSync(new LambdaMessage([&]() {
+        const auto& displayDevice = getDefaultDisplayDeviceLocked();
+        layersProto = dumpDrawingStateProto(traceFlags, displayDevice);
+    }));
     return layersProto;
 }
 
@@ -5068,7 +5208,7 @@
                 n = data.readInt32();
                 if (n == 1 && !mRefreshRateOverlay) {
                     mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(*this);
-                    auto current = mRefreshRateConfigs->getCurrentRefreshRate();
+                    auto& current = mRefreshRateConfigs->getCurrentRefreshRate();
                     mRefreshRateOverlay->changeRefreshRate(current);
                 } else if (n == 0) {
                     mRefreshRateOverlay.reset();
@@ -5106,6 +5246,36 @@
     mEventQueue->invalidate();
 }
 
+void SurfaceFlinger::kernelTimerChanged(bool expired) {
+    static bool updateOverlay =
+            property_get_bool("debug.sf.kernel_idle_timer_update_overlay", true);
+    if (!updateOverlay || !mRefreshRateOverlay) return;
+
+    // Update the overlay on the main thread to avoid race conditions with
+    // mRefreshRateConfigs->getCurrentRefreshRate()
+    postMessageAsync(new LambdaMessage([this, expired]() NO_THREAD_SAFETY_ANALYSIS {
+        if (mRefreshRateOverlay) {
+            const auto kernelTimerEnabled = property_get_bool(KERNEL_IDLE_TIMER_PROP, false);
+            const bool timerExpired = kernelTimerEnabled && expired;
+            const auto& current = [this]() -> const RefreshRate& {
+                std::lock_guard<std::mutex> lock(mActiveConfigLock);
+                if (mDesiredActiveConfigChanged) {
+                    return mRefreshRateConfigs->getRefreshRateFromConfigId(
+                            mDesiredActiveConfig.configId);
+                }
+
+                return mRefreshRateConfigs->getCurrentRefreshRate();
+            }();
+            const auto& min = mRefreshRateConfigs->getMinRefreshRate();
+
+            if (current != min) {
+                mRefreshRateOverlay->changeRefreshRate(timerExpired ? min : current);
+                mEventQueue->invalidate();
+            }
+        }
+    }));
+}
+
 // A simple RAII class to disconnect from an ANativeWindow* when it goes out of scope
 class WindowDisconnector {
 public:
@@ -5668,6 +5838,7 @@
     Mutex::Autolock _l(mStateLock);
 
     mPendingSyncInputWindows = false;
+
     mTransactionCV.broadcast();
 }
 
@@ -5767,7 +5938,7 @@
                                 display->getActiveConfig(), vsyncPeriod);
 
     auto configId = mScheduler->getPreferredConfigId();
-    auto preferredRefreshRate = configId
+    auto& preferredRefreshRate = configId
             ? mRefreshRateConfigs->getRefreshRateFromConfigId(*configId)
             // NOTE: Choose the default config ID, if Scheduler doesn't have one in mind.
             : mRefreshRateConfigs->getRefreshRateFromConfigId(defaultConfig);
@@ -5934,18 +6105,22 @@
         return BAD_VALUE;
     }
 
-    Mutex::Autolock lock(mStateLock);
-    if (authenticateSurfaceTextureLocked(surface)) {
-        sp<Layer> layer = (static_cast<MonitoredProducer*>(surface.get()))->getLayer();
-        if (layer->setFrameRate(
-                    Layer::FrameRate(frameRate,
-                                     Layer::FrameRate::convertCompatibility(compatibility)))) {
-            setTransactionFlags(eTraversalNeeded);
+    postMessageAsync(new LambdaMessage([=]() NO_THREAD_SAFETY_ANALYSIS {
+        Mutex::Autolock lock(mStateLock);
+        if (authenticateSurfaceTextureLocked(surface)) {
+            sp<Layer> layer = (static_cast<MonitoredProducer*>(surface.get()))->getLayer();
+            if (layer->setFrameRate(
+                        Layer::FrameRate(frameRate,
+                                         Layer::FrameRate::convertCompatibility(compatibility)))) {
+                setTransactionFlags(eTraversalNeeded);
+            }
+        } else {
+            ALOGE("Attempt to set frame rate on an unrecognized IGraphicBufferProducer");
+            return BAD_VALUE;
         }
-    } else {
-        ALOGE("Attempt to set frame rate on an unrecognized IGraphicBufferProducer");
-        return BAD_VALUE;
-    }
+        return NO_ERROR;
+    }));
+
     return NO_ERROR;
 }
 
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index c79621b..6ab1fcf 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -42,6 +42,7 @@
 #include <system/graphics.h>
 #include <ui/FenceTime.h>
 #include <ui/PixelFormat.h>
+#include <ui/Size.h>
 #include <utils/Errors.h>
 #include <utils/KeyedVector.h>
 #include <utils/RefBase.h>
@@ -225,6 +226,11 @@
     // FramebufferSurface
     static int64_t maxFrameBufferAcquiredBuffers;
 
+    // Controls the maximum width and height in pixels that the graphics pipeline can support for
+    // GPU fallback composition. For example, 8k devices with 4k GPUs, or 4k devices with 2k GPUs.
+    static uint32_t maxGraphicsWidth;
+    static uint32_t maxGraphicsHeight;
+
     // Indicate if a device has wide color gamut display. This is typically
     // found on devices with wide color gamut (e.g. Display-P3) display.
     static bool hasWideColorDisplay;
@@ -351,7 +357,6 @@
     // every half hour.
     enum { LOG_FRAME_STATS_PERIOD =  30*60*60 };
 
-    static const size_t MAX_LAYERS = 4096;
     static const int MAX_TRACING_MEMORY = 100 * 1024 * 1024; // 100MB
 
 protected:
@@ -524,6 +529,8 @@
     void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ConfigEvent) override;
     // force full composition on all displays without resetting the scheduler idle timer.
     void repaintEverythingForHWC() override;
+    // Called when kernel idle timer has expired. Used to update the refresh rate overlay.
+    void kernelTimerChanged(bool expired) override;
     /* ------------------------------------------------------------------------
      * Message handling
      */
@@ -810,11 +817,17 @@
      * Display management
      */
     sp<DisplayDevice> setupNewDisplayDeviceInternal(
-            const wp<IBinder>& displayToken, const std::optional<DisplayId>& displayId,
+            const wp<IBinder>& displayToken,
+            std::shared_ptr<compositionengine::Display> compositionDisplay,
             const DisplayDeviceState& state,
-            const sp<compositionengine::DisplaySurface>& dispSurface,
+            const sp<compositionengine::DisplaySurface>& displaySurface,
             const sp<IGraphicBufferProducer>& producer);
     void processDisplayChangesLocked();
+    void processDisplayAdded(const wp<IBinder>& displayToken, const DisplayDeviceState& state);
+    void processDisplayRemoved(const wp<IBinder>& displayToken);
+    void processDisplayChanged(const wp<IBinder>& displayToken,
+                               const DisplayDeviceState& currentState,
+                               const DisplayDeviceState& drawingState);
     void processDisplayHotplugEventsLocked();
 
     void dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected);
@@ -831,7 +844,21 @@
 
     bool isDisplayConfigAllowed(HwcConfigIndexType configId) const REQUIRES(mStateLock);
 
-    bool previousFrameMissed(int graceTimeMs = 0);
+    // Gets the fence for the previous frame.
+    // Must be called on the main thread.
+    sp<Fence> previousFrameFence();
+
+    // Whether the previous frame has not yet been presented to the display.
+    // If graceTimeMs is positive, this method waits for at most the provided
+    // grace period before reporting if the frame missed.
+    // Must be called on the main thread.
+    bool previousFramePending(int graceTimeMs = 0);
+
+    // Returns the previous time that the frame was presented. If the frame has
+    // not been presented yet, then returns Fence::SIGNAL_TIME_PENDING. If there
+    // is no pending frame, then returns Fence::SIGNAL_TIME_INVALID.
+    // Must be called on the main thread.
+    nsecs_t previousFramePresentTime();
 
     // Populates the expected present time for this frame. For negative offsets, performs a
     // correction using the predicted vsync for the next frame instead.
@@ -915,9 +942,12 @@
     void dumpDisplayIdentificationData(std::string& result) const;
     void dumpRawDisplayIdentificationData(const DumpArgs&, std::string& result) const;
     void dumpWideColorInfo(std::string& result) const;
-    LayersProto dumpDrawingStateProto(uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const;
+    LayersProto dumpDrawingStateProto(uint32_t traceFlags = SurfaceTracing::TRACE_ALL,
+                                      const sp<const DisplayDevice>& displayDevice = nullptr) const;
     void dumpOffscreenLayersProto(LayersProto& layersProto,
                                   uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const;
+    // Dumps state from HW Composer
+    void dumpHwc(std::string& result) const;
     LayersProto dumpProtoFromMainThread(uint32_t traceFlags = SurfaceTracing::TRACE_ALL)
             EXCLUDES(mStateLock);
     void dumpOffscreenLayers(std::string& result) EXCLUDES(mStateLock);
@@ -971,7 +1001,7 @@
 
     // Can't be unordered_set because wp<> isn't hashable
     std::set<wp<IBinder>> mGraphicBufferProducerList;
-    size_t mMaxGraphicBufferProducerListSize = MAX_LAYERS;
+    size_t mMaxGraphicBufferProducerListSize = ISurfaceComposer::MAX_LAYERS;
 
     // protected by mStateLock (but we could use another lock)
     bool mLayersRemoved = false;
@@ -982,6 +1012,7 @@
     // constant members (no synchronization needed for access)
     const nsecs_t mBootTime = systemTime();
     bool mGpuToCpuSupported = false;
+    bool mIsUserBuild = true;
 
     // Can only accessed from the main thread, these members
     // don't need synchronization
@@ -1043,8 +1074,10 @@
     const std::shared_ptr<TimeStats> mTimeStats;
     const std::unique_ptr<FrameTracer> mFrameTracer;
     bool mUseHwcVirtualDisplays = false;
+    // If blurs should be enabled on this device.
+    bool mSupportsBlur = false;
     // Disable blurs, for debugging
-    bool mEnableBlurs = false;
+    std::atomic<bool> mDisableBlurs = false;
     // If blurs are considered expensive and should require high GPU frequency.
     bool mBlursAreExpensive = false;
     std::atomic<uint32_t> mFrameMissedCount = 0;
@@ -1118,6 +1151,10 @@
 
     // to linkToDeath
     sp<IBinder> mWindowManager;
+    // We want to avoid multiple calls to BOOT_FINISHED as they come in on
+    // different threads without a lock and could trigger unsynchronized writes to
+    // to mWindowManager or mInputFlinger
+    std::atomic<bool> mBootFinished = false;
 
     std::unique_ptr<dvr::VrFlinger> mVrFlinger;
     std::atomic<bool> mVrFlingerRequestsDisplay = false;
@@ -1225,6 +1262,16 @@
     // Flags to capture the state of Vsync in HWC
     HWC2::Vsync mHWCVsyncState = HWC2::Vsync::Disable;
     HWC2::Vsync mHWCVsyncPendingState = HWC2::Vsync::Disable;
+
+    // Fields tracking the current jank event: when it started and how many
+    // janky frames there are.
+    nsecs_t mMissedFrameJankStart = 0;
+    int32_t mMissedFrameJankCount = 0;
+
+    // See updateInputWindowInfo() for details
+    std::atomic<bool> mInputDirty = true;
+    void dirtyInput() { mInputDirty = true; }
+    bool inputDirty() { return mInputDirty; }
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index d49133d..ddd20a5 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -33,6 +33,7 @@
 #include "NativeWindowSurface.h"
 #include "StartPropertySetThread.h"
 #include "SurfaceFlingerDefaultFactory.h"
+#include "SurfaceFlingerProperties.h"
 #include "SurfaceInterceptor.h"
 
 #include "DisplayHardware/ComposerHal.h"
@@ -76,8 +77,8 @@
         SetVSyncEnabled setVSyncEnabled, const scheduler::RefreshRateConfigs& configs,
         ISchedulerCallback& schedulerCallback) {
     return std::make_unique<Scheduler>(std::move(setVSyncEnabled), configs, schedulerCallback,
-                                       property_get_bool("debug.sf.use_content_detection_v2",
-                                                         true));
+                                       property_get_bool("debug.sf.use_content_detection_v2", true),
+                                       sysprop::use_content_detection_for_refresh_rate(false));
 }
 
 std::unique_ptr<SurfaceInterceptor> DefaultFactory::createSurfaceInterceptor(
@@ -90,8 +91,8 @@
     return new StartPropertySetThread(timestampPropertyValue);
 }
 
-sp<DisplayDevice> DefaultFactory::createDisplayDevice(DisplayDeviceCreationArgs&& creationArgs) {
-    return new DisplayDevice(std::move(creationArgs));
+sp<DisplayDevice> DefaultFactory::createDisplayDevice(DisplayDeviceCreationArgs& creationArgs) {
+    return new DisplayDevice(creationArgs);
 }
 
 sp<GraphicBuffer> DefaultFactory::createGraphicBuffer(uint32_t width, uint32_t height,
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index 89194c7..bd40cfb 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -37,7 +37,7 @@
                                                ISchedulerCallback&) override;
     std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger*) override;
     sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override;
-    sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&&) override;
+    sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&) override;
     sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format,
                                           uint32_t layerCount, uint64_t usage,
                                           std::string requestorName) override;
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index 209bd0c..6f4fcc6 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -83,7 +83,7 @@
 
     virtual sp<StartPropertySetThread> createStartPropertySetThread(
             bool timestampPropertyValue) = 0;
-    virtual sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&&) = 0;
+    virtual sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&) = 0;
     virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height,
                                                   PixelFormat format, uint32_t layerCount,
                                                   uint64_t usage, std::string requestorName) = 0;
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index f3352a5..9d78702 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -70,6 +70,22 @@
             defaultValue);
 }
 
+int32_t max_graphics_width(int32_t defaultValue) {
+    auto temp = SurfaceFlingerProperties::max_graphics_width();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return defaultValue;
+}
+
+int32_t max_graphics_height(int32_t defaultValue) {
+    auto temp = SurfaceFlingerProperties::max_graphics_height();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return defaultValue;
+}
+
 bool has_wide_color_display(bool defaultValue) {
     auto temp = SurfaceFlingerProperties::has_wide_color_display();
     if (temp.has_value()) {
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 12c711a..c63adfe 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -37,6 +37,9 @@
 
 int64_t max_frame_buffer_acquired_buffers(int64_t defaultValue);
 
+int32_t max_graphics_width(int32_t defaultValue);
+int32_t max_graphics_height(int32_t defaultValue);
+
 bool has_wide_color_display(bool defaultValue);
 
 bool running_without_sync_framework(bool defaultValue);
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 1f9d46c..5b3cd69 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -524,11 +524,11 @@
     deletion->set_id(getLayerId(layer));
 }
 
-void SurfaceInterceptor::addBufferUpdateLocked(Increment* increment, const sp<const Layer>& layer,
+void SurfaceInterceptor::addBufferUpdateLocked(Increment* increment, int32_t layerId,
         uint32_t width, uint32_t height, uint64_t frameNumber)
 {
     BufferUpdate* update(increment->mutable_buffer_update());
-    update->set_id(getLayerId(layer));
+    update->set_id(layerId);
     update->set_w(width);
     update->set_h(height);
     update->set_frame_number(frameNumber);
@@ -644,15 +644,22 @@
     addSurfaceDeletionLocked(createTraceIncrementLocked(), layer);
 }
 
-void SurfaceInterceptor::saveBufferUpdate(const sp<const Layer>& layer, uint32_t width,
+/**
+ * Here we pass the layer by ID instead of by sp<> since this is called without
+ * holding the state-lock from a Binder thread. If we required the caller
+ * to pass 'this' by sp<> the temporary sp<> constructed could end up
+ * being the last reference and we might accidentally destroy the Layer
+ * from this binder thread.
+ */
+void SurfaceInterceptor::saveBufferUpdate(int32_t layerId, uint32_t width,
         uint32_t height, uint64_t frameNumber)
 {
-    if (!mEnabled || layer == nullptr) {
+    if (!mEnabled) {
         return;
     }
     ATRACE_CALL();
     std::lock_guard<std::mutex> protoGuard(mTraceMutex);
-    addBufferUpdateLocked(createTraceIncrementLocked(), layer, width, height, frameNumber);
+    addBufferUpdateLocked(createTraceIncrementLocked(), layerId, width, height, frameNumber);
 }
 
 void SurfaceInterceptor::saveVSyncEvent(nsecs_t timestamp) {
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
index a665f62..896bdcc 100644
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -67,7 +67,7 @@
     // Intercept surface data
     virtual void saveSurfaceCreation(const sp<const Layer>& layer) = 0;
     virtual void saveSurfaceDeletion(const sp<const Layer>& layer) = 0;
-    virtual void saveBufferUpdate(const sp<const Layer>& layer, uint32_t width, uint32_t height,
+    virtual void saveBufferUpdate(int32_t layerId, uint32_t width, uint32_t height,
                                   uint64_t frameNumber) = 0;
 
     // Intercept display data
@@ -102,7 +102,7 @@
     // Intercept surface data
     void saveSurfaceCreation(const sp<const Layer>& layer) override;
     void saveSurfaceDeletion(const sp<const Layer>& layer) override;
-    void saveBufferUpdate(const sp<const Layer>& layer, uint32_t width, uint32_t height,
+    void saveBufferUpdate(int32_t layerId, uint32_t width, uint32_t height,
                           uint64_t frameNumber) override;
 
     // Intercept display data
@@ -130,7 +130,7 @@
     Increment* createTraceIncrementLocked();
     void addSurfaceCreationLocked(Increment* increment, const sp<const Layer>& layer);
     void addSurfaceDeletionLocked(Increment* increment, const sp<const Layer>& layer);
-    void addBufferUpdateLocked(Increment* increment, const sp<const Layer>& layer, uint32_t width,
+    void addBufferUpdateLocked(Increment* increment, int32_t layerId, uint32_t width,
             uint32_t height, uint64_t frameNumber);
     void addVSyncUpdateLocked(Increment* increment, nsecs_t timestamp);
     void addDisplayCreationLocked(Increment* increment, const DisplayDeviceState& info);
diff --git a/services/surfaceflinger/SurfaceTracing.cpp b/services/surfaceflinger/SurfaceTracing.cpp
index eb5c7de..a9c3332 100644
--- a/services/surfaceflinger/SurfaceTracing.cpp
+++ b/services/surfaceflinger/SurfaceTracing.cpp
@@ -45,19 +45,21 @@
 }
 
 void SurfaceTracing::addFirstEntry() {
+    const auto displayDevice = mFlinger.getDefaultDisplayDevice();
     LayersTraceProto entry;
     {
         std::scoped_lock lock(mSfLock);
-        entry = traceLayersLocked("tracing.enable");
+        entry = traceLayersLocked("tracing.enable", displayDevice);
     }
     addTraceToBuffer(entry);
 }
 
 LayersTraceProto SurfaceTracing::traceWhenNotified() {
+    const auto displayDevice = mFlinger.getDefaultDisplayDevice();
     std::unique_lock<std::mutex> lock(mSfLock);
     mCanStartTrace.wait(lock);
     android::base::ScopedLockAssertion assumeLock(mSfLock);
-    LayersTraceProto entry = traceLayersLocked(mWhere);
+    LayersTraceProto entry = traceLayersLocked(mWhere, displayDevice);
     lock.unlock();
     return entry;
 }
@@ -127,7 +129,12 @@
 }
 
 status_t SurfaceTracing::writeToFile() {
-    mThread.join();
+    std::thread thread;
+    {
+        std::scoped_lock lock(mTraceLock);
+        thread = std::move(mThread);
+    }
+    thread.join();
     return mLastErr;
 }
 
@@ -160,16 +167,23 @@
     mTraceFlags = flags;
 }
 
-LayersTraceProto SurfaceTracing::traceLayersLocked(const char* where) {
+LayersTraceProto SurfaceTracing::traceLayersLocked(const char* where,
+                                                   const sp<const DisplayDevice>& displayDevice) {
     ATRACE_CALL();
 
     LayersTraceProto entry;
     entry.set_elapsed_realtime_nanos(elapsedRealtimeNano());
     entry.set_where(where);
-    LayersProto layers(mFlinger.dumpDrawingStateProto(mTraceFlags));
+    LayersProto layers(mFlinger.dumpDrawingStateProto(mTraceFlags, displayDevice));
     mFlinger.dumpOffscreenLayersProto(layers);
     entry.mutable_layers()->Swap(&layers);
 
+    if (mTraceFlags & SurfaceTracing::TRACE_HWC) {
+        std::string hwcDump;
+        mFlinger.dumpHwc(hwcDump);
+        entry.set_hwc_blob(hwcDump);
+    }
+
     return entry;
 }
 
diff --git a/services/surfaceflinger/SurfaceTracing.h b/services/surfaceflinger/SurfaceTracing.h
index 18524f0..83872ed 100644
--- a/services/surfaceflinger/SurfaceTracing.h
+++ b/services/surfaceflinger/SurfaceTracing.h
@@ -16,17 +16,19 @@
 
 #pragma once
 
+#include <android-base/thread_annotations.h>
 #include <layerproto/LayerProtoHeader.h>
 #include <utils/Errors.h>
 #include <utils/StrongPointer.h>
 
-#include <android-base/thread_annotations.h>
 #include <condition_variable>
 #include <memory>
 #include <mutex>
 #include <queue>
 #include <thread>
 
+#include "DisplayDevice.h"
+
 using namespace android::surfaceflinger;
 
 namespace android {
@@ -56,6 +58,7 @@
         TRACE_CRITICAL = 1 << 0,
         TRACE_INPUT = 1 << 1,
         TRACE_EXTRA = 1 << 2,
+        TRACE_HWC = 1 << 3,
         TRACE_ALL = 0xffffffff
     };
     void setTraceFlags(uint32_t flags);
@@ -84,19 +87,21 @@
     void mainLoop();
     void addFirstEntry();
     LayersTraceProto traceWhenNotified();
-    LayersTraceProto traceLayersLocked(const char* where) REQUIRES(mSfLock);
+    LayersTraceProto traceLayersLocked(const char* where,
+                                       const sp<const DisplayDevice>& displayDevice)
+            REQUIRES(mSfLock);
 
     // Returns true if trace is enabled.
     bool addTraceToBuffer(LayersTraceProto& entry);
     void writeProtoFileLocked() REQUIRES(mTraceLock);
 
-    const SurfaceFlinger& mFlinger;
+    SurfaceFlinger& mFlinger;
     status_t mLastErr = NO_ERROR;
     std::thread mThread;
     std::condition_variable mCanStartTrace;
 
     std::mutex& mSfLock;
-    uint32_t mTraceFlags GUARDED_BY(mSfLock) = TRACE_ALL;
+    uint32_t mTraceFlags GUARDED_BY(mSfLock) = TRACE_CRITICAL | TRACE_INPUT;
     const char* mWhere GUARDED_BY(mSfLock) = "";
 
     mutable std::mutex mTraceLock;
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 8038eba..4f59bf2 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -188,17 +188,16 @@
 
 TimeStats::~TimeStats() {
     std::lock_guard<std::mutex> lock(mMutex);
-    mStatsDelegate->unregisterStatsPullAtomCallback(
-            android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
-    mStatsDelegate->unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO);
+    mStatsDelegate->clearStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
+    mStatsDelegate->clearStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO);
 }
 
 void TimeStats::onBootFinished() {
     std::lock_guard<std::mutex> lock(mMutex);
-    mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
-                                                  TimeStats::pullAtomCallback, nullptr, this);
-    mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
-                                                  TimeStats::pullAtomCallback, nullptr, this);
+    mStatsDelegate->setStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+                                             nullptr, TimeStats::pullAtomCallback, this);
+    mStatsDelegate->setStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
+                                             nullptr, TimeStats::pullAtomCallback, this);
 }
 
 void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, std::string& result) {
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index ddebeb1..f9bd90b 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -165,15 +165,15 @@
         virtual AStatsEvent* addStatsEventToPullData(AStatsEventList* data) {
             return AStatsEventList_addStatsEvent(data);
         }
-        virtual void registerStatsPullAtomCallback(int32_t atom_tag,
-                                                   AStatsManager_PullAtomCallback callback,
-                                                   AStatsManager_PullAtomMetadata* metadata,
-                                                   void* cookie) {
-            return AStatsManager_registerPullAtomCallback(atom_tag, callback, metadata, cookie);
+        virtual void setStatsPullAtomCallback(int32_t atom_tag,
+                                              AStatsManager_PullAtomMetadata* metadata,
+                                              AStatsManager_PullAtomCallback callback,
+                                              void* cookie) {
+            return AStatsManager_setPullAtomCallback(atom_tag, metadata, callback, cookie);
         }
 
-        virtual void unregisterStatsPullAtomCallback(int32_t atom_tag) {
-            return AStatsManager_unregisterPullAtomCallback(atom_tag);
+        virtual void clearStatsPullAtomCallback(int32_t atom_tag) {
+            return AStatsManager_clearPullAtomCallback(atom_tag);
         }
 
         virtual void statsEventSetAtomId(AStatsEvent* event, uint32_t atom_id) {
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index 8afe503..7f1f542 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -9,6 +9,23 @@
   repeated LayerProto layers = 1;
 }
 
+// Must match definition in the IComposerClient HAL
+enum HwcCompositionType {
+    // Invalid composition type
+    INVALID = 0;
+    // Layer was composited by the client into the client target buffer
+    CLIENT = 1;
+    // Layer was composited by the device through hardware overlays
+    DEVICE = 2;
+    // Layer was composited by the device using a color
+    SOLID_COLOR = 3;
+    // Similar to DEVICE, but the layer position may have been asynchronously set
+    // through setCursorPosition
+    CURSOR = 4;
+    // Layer was composited by the device via a sideband stream.
+    SIDEBAND = 5;
+}
+
 // Information about each layer.
 message LayerProto {
   // unique id per layer.
@@ -73,7 +90,7 @@
   int32 window_type = 33 [deprecated=true];
   int32 app_id = 34 [deprecated=true];
   // The layer's composition type
-  int32 hwc_composition_type = 35;
+  HwcCompositionType hwc_composition_type = 35;
   // If it's a buffer layer, indicate if the content is protected
   bool is_protected = 36;
   // Current frame number being rendered.
@@ -190,4 +207,4 @@
 message ColorTransformProto {
   // This will be a 4x4 matrix of float values
   repeated float val = 1;
-}
\ No newline at end of file
+}
diff --git a/services/surfaceflinger/layerproto/layerstrace.proto b/services/surfaceflinger/layerproto/layerstrace.proto
index bee17d2..ac33a0e 100644
--- a/services/surfaceflinger/layerproto/layerstrace.proto
+++ b/services/surfaceflinger/layerproto/layerstrace.proto
@@ -48,4 +48,7 @@
     optional string where = 2;
 
     optional LayersProto layers = 3;
+
+    // Blob for the current HWC information for all layers, reported by dumpsys.
+    optional string hwc_blob = 4;
 }
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 155f718..cfc301b 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -67,6 +67,26 @@
     prop_name: "ro.surface_flinger.max_frame_buffer_acquired_buffers"
 }
 
+# Controls the maximum width in pixels that the graphics pipeline can support for GPU fallback
+# composition. For example, 8k displays with 4k GPUs, or 4k displays with 2k GPUs.
+prop {
+    api_name: "max_graphics_width"
+    type: Integer
+    scope: System
+    access: Readonly
+    prop_name: "ro.surface_flinger.max_graphics_width"
+}
+
+# Controls the maximum height in pixels that the graphics pipeline can support for GPU fallback
+# composition. For example, 8k displays with 4k GPUs, or 4k displays with 2k GPUs.
+prop {
+    api_name: "max_graphics_height"
+    type: Integer
+    scope: System
+    access: Readonly
+    prop_name: "ro.surface_flinger.max_graphics_height"
+}
+
 # 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
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index e62c127..ba60a7d 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -62,6 +62,16 @@
     prop_name: "ro.surface_flinger.max_frame_buffer_acquired_buffers"
   }
   prop {
+    api_name: "max_graphics_height"
+    type: Integer
+    prop_name: "ro.surface_flinger.max_graphics_height"
+  }
+  prop {
+    api_name: "max_graphics_width"
+    type: Integer
+    prop_name: "ro.surface_flinger.max_graphics_width"
+  }
+  prop {
     api_name: "max_virtual_display_dimension"
     type: Long
     prop_name: "ro.surface_flinger.max_virtual_display_dimension"
diff --git a/services/surfaceflinger/tests/AndroidTest.xml b/services/surfaceflinger/tests/AndroidTest.xml
index 8315037..000628f 100644
--- a/services/surfaceflinger/tests/AndroidTest.xml
+++ b/services/surfaceflinger/tests/AndroidTest.xml
@@ -18,6 +18,7 @@
         <option name="cleanup" value="true" />
         <option name="push" value="SurfaceFlinger_test->/data/local/tmp/SurfaceFlinger_test" />
     </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
     <option name="test-suite-tag" value="apct" />
     <test class="com.android.tradefed.testtype.GTest" >
         <option name="native-test-device-path" value="/data/local/tmp" />
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 1cd8731..2f2fb20 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -50,6 +50,7 @@
         "PhaseOffsetsTest.cpp",
         "SchedulerTest.cpp",
         "SchedulerUtilsTest.cpp",
+        "SetFrameRateTest.cpp",
         "RefreshRateConfigsTest.cpp",
         "RefreshRateSelectionTest.cpp",
         "RefreshRateStatsTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 06ef8e7..680b0a0 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -39,6 +39,7 @@
 #include "Layer.h"
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
+#include "mock/DisplayHardware/MockPowerAdvisor.h"
 #include "mock/MockDispSync.h"
 #include "mock/MockEventControlThread.h"
 #include "mock/MockEventThread.h"
@@ -186,6 +187,7 @@
     renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
     mock::TimeStats* mTimeStats = new mock::TimeStats();
     mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
+    Hwc2::mock::PowerAdvisor mPowerAdvisor;
 
     sp<Fence> mClientTargetAcquireFence = Fence::NO_FENCE;
 
@@ -284,8 +286,27 @@
         EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1);
         EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1);
         EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1);
+
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+
+        auto ceDisplayArgs =
+                compositionengine::DisplayCreationArgsBuilder()
+                        .setPhysical({DEFAULT_DISPLAY_ID, DisplayConnectionType::Internal})
+                        .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
+                        .setIsSecure(Derived::IS_SECURE)
+                        .setLayerStackId(DEFAULT_LAYER_STACK)
+                        .setPowerAdvisor(&test->mPowerAdvisor)
+                        .setName(std::string("Injected display for ") +
+                                 test_info->test_case_name() + "." + test_info->name())
+                        .build();
+
+        auto compositionDisplay =
+                compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
+                                                       ceDisplayArgs);
+
         test->mDisplay =
-                FakeDisplayDeviceInjector(test->mFlinger, DEFAULT_DISPLAY_ID,
+                FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay,
                                           DisplayConnectionType::Internal, true /* isPrimary */)
                         .setDisplaySurface(test->mDisplaySurface)
                         .setNativeWindow(test->mNativeWindow)
@@ -325,18 +346,17 @@
     template <typename Case>
     static void setupCommonScreensCaptureCallExpectations(CompositionTest* test) {
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
-                .WillRepeatedly(
-                        [](const renderengine::DisplaySettings& displaySettings,
-                           const std::vector<const renderengine::LayerSettings*>&,
-                           ANativeWindowBuffer*, const bool, base::unique_fd&&,
-                           base::unique_fd*) -> status_t {
-                            EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
-                            EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
-                                      displaySettings.physicalDisplay);
-                            EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
-                                      displaySettings.clip);
-                            return NO_ERROR;
-                        });
+                .WillRepeatedly([](const renderengine::DisplaySettings& displaySettings,
+                                   const std::vector<const renderengine::LayerSettings*>&,
+                                   ANativeWindowBuffer*, const bool, base::unique_fd&&,
+                                   base::unique_fd*) -> status_t {
+                    EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
+                    EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                              displaySettings.physicalDisplay);
+                    EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                              displaySettings.clip);
+                    return NO_ERROR;
+                });
     }
 
     static void setupNonEmptyFrameCompositionCallExpectations(CompositionTest* test) {
@@ -375,19 +395,18 @@
                 .WillOnce(DoAll(SetArgPointee<0>(test->mNativeWindowBuffer), SetArgPointee<1>(-1),
                                 Return(0)));
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
-                .WillRepeatedly(
-                        [](const renderengine::DisplaySettings& displaySettings,
-                           const std::vector<const renderengine::LayerSettings*>&,
-                           ANativeWindowBuffer*, const bool, base::unique_fd&&,
-                           base::unique_fd*) -> status_t {
-                            EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
-                            EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
-                                      displaySettings.physicalDisplay);
-                            EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
-                                      displaySettings.clip);
-                            EXPECT_EQ(ui::Dataspace::UNKNOWN, displaySettings.outputDataspace);
-                            return NO_ERROR;
-                        });
+                .WillRepeatedly([](const renderengine::DisplaySettings& displaySettings,
+                                   const std::vector<const renderengine::LayerSettings*>&,
+                                   ANativeWindowBuffer*, const bool, base::unique_fd&&,
+                                   base::unique_fd*) -> status_t {
+                    EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
+                    EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                              displaySettings.physicalDisplay);
+                    EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                              displaySettings.clip);
+                    EXPECT_EQ(ui::Dataspace::UNKNOWN, displaySettings.outputDataspace);
+                    return NO_ERROR;
+                });
     }
 
     template <typename Case>
diff --git a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
index a023367..c2ddfce 100644
--- a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+#include <functional>
+#include <string_view>
+
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
@@ -124,6 +127,10 @@
     return DisplayIdentificationData(bytes, bytes + N - 1);
 }
 
+uint32_t hash(const char* str) {
+    return static_cast<uint32_t>(std::hash<std::string_view>()(str));
+}
+
 } // namespace
 
 const DisplayIdentificationData& getInternalEdid() {
@@ -173,7 +180,8 @@
     EXPECT_EQ(0x4ca3u, edid->manufacturerId);
     EXPECT_STREQ("SEC", edid->pnpId.data());
     // ASCII text should be used as fallback if display name and serial number are missing.
-    EXPECT_EQ("121AT11-801", edid->displayName);
+    EXPECT_EQ(hash("121AT11-801"), edid->modelHash);
+    EXPECT_TRUE(edid->displayName.empty());
     EXPECT_EQ(12610, edid->productId);
     EXPECT_EQ(21, edid->manufactureOrModelYear);
     EXPECT_EQ(0, edid->manufactureWeek);
@@ -182,6 +190,7 @@
     ASSERT_TRUE(edid);
     EXPECT_EQ(0x22f0u, edid->manufacturerId);
     EXPECT_STREQ("HWP", edid->pnpId.data());
+    EXPECT_EQ(hash("HP ZR30w"), edid->modelHash);
     EXPECT_EQ("HP ZR30w", edid->displayName);
     EXPECT_EQ(10348, edid->productId);
     EXPECT_EQ(22, edid->manufactureOrModelYear);
@@ -191,6 +200,7 @@
     ASSERT_TRUE(edid);
     EXPECT_EQ(0x4c2du, edid->manufacturerId);
     EXPECT_STREQ("SAM", edid->pnpId.data());
+    EXPECT_EQ(hash("SAMSUNG"), edid->modelHash);
     EXPECT_EQ("SAMSUNG", edid->displayName);
     EXPECT_EQ(2302, edid->productId);
     EXPECT_EQ(21, edid->manufactureOrModelYear);
@@ -200,6 +210,7 @@
     ASSERT_TRUE(edid);
     EXPECT_EQ(13481, edid->manufacturerId);
     EXPECT_STREQ("MEI", edid->pnpId.data());
+    EXPECT_EQ(hash("Panasonic-TV"), edid->modelHash);
     EXPECT_EQ("Panasonic-TV", edid->displayName);
     EXPECT_EQ(41622, edid->productId);
     EXPECT_EQ(29, edid->manufactureOrModelYear);
@@ -209,6 +220,7 @@
     ASSERT_TRUE(edid);
     EXPECT_EQ(8355, edid->manufacturerId);
     EXPECT_STREQ("HEC", edid->pnpId.data());
+    EXPECT_EQ(hash("Hisense"), edid->modelHash);
     EXPECT_EQ("Hisense", edid->displayName);
     EXPECT_EQ(0, edid->productId);
     EXPECT_EQ(29, edid->manufactureOrModelYear);
@@ -218,6 +230,7 @@
     ASSERT_TRUE(edid);
     EXPECT_EQ(3724, edid->manufacturerId);
     EXPECT_STREQ("CTL", edid->pnpId.data());
+    EXPECT_EQ(hash("LP2361"), edid->modelHash);
     EXPECT_EQ("LP2361", edid->displayName);
     EXPECT_EQ(9373, edid->productId);
     EXPECT_EQ(23, edid->manufactureOrModelYear);
@@ -234,13 +247,15 @@
     auto edid = parseEdid(data);
     ASSERT_TRUE(edid);
     // Serial number should be used as fallback if display name is invalid.
-    EXPECT_EQ("CN4202137Q", edid->displayName);
+    const auto modelHash = hash("CN4202137Q");
+    EXPECT_EQ(modelHash, edid->modelHash);
+    EXPECT_TRUE(edid->displayName.empty());
 
     // Parsing should succeed even if EDID is truncated.
     data.pop_back();
     edid = parseEdid(data);
     ASSERT_TRUE(edid);
-    EXPECT_EQ("CN4202137Q", edid->displayName);
+    EXPECT_EQ(modelHash, edid->modelHash);
 }
 
 TEST(DisplayIdentificationTest, getPnpId) {
@@ -278,7 +293,7 @@
         ASSERT_TRUE(displayIdInfo);
         ASSERT_TRUE(displayIdInfo->deviceProductInfo);
         const auto& info = *displayIdInfo->deviceProductInfo;
-        EXPECT_STREQ("121AT11-801", info.name.data());
+        EXPECT_STREQ("", info.name.data());
         EXPECT_STREQ("SEC", info.manufacturerPnpId.data());
         EXPECT_STREQ("12610", info.productId.data());
         ASSERT_TRUE(std::holds_alternative<ManufactureYear>(info.manufactureOrModelDate));
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 4da0647..6d00ccc 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -25,8 +25,12 @@
 
 #include <compositionengine/Display.h>
 #include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/impl/Display.h>
 #include <compositionengine/impl/OutputCompositionState.h>
+#include <compositionengine/mock/Display.h>
+#include <compositionengine/mock/DisplayColorProfile.h>
 #include <compositionengine/mock/DisplaySurface.h>
+#include <compositionengine/mock/RenderSurface.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <gui/mock/GraphicBufferConsumer.h>
@@ -39,6 +43,7 @@
 #include "TestableScheduler.h"
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
+#include "mock/DisplayHardware/MockPowerAdvisor.h"
 #include "mock/MockDispSync.h"
 #include "mock/MockEventControlThread.h"
 #include "mock/MockEventThread.h"
@@ -51,11 +56,14 @@
 namespace {
 
 using testing::_;
+using testing::AnyNumber;
 using testing::DoAll;
 using testing::Mock;
 using testing::ResultOf;
 using testing::Return;
+using testing::ReturnRefOfCopy;
 using testing::SetArgPointee;
+using testing::StrictMock;
 
 using android::Hwc2::ColorMode;
 using android::Hwc2::Error;
@@ -107,6 +115,7 @@
     void injectMockComposer(int virtualDisplayCount);
     void injectFakeBufferQueueFactory();
     void injectFakeNativeWindowSurfaceFactory();
+    sp<DisplayDevice> injectDefaultInternalDisplay(std::function<void(FakeDisplayDeviceInjector&)>);
 
     // --------------------------------------------------------------------
     // Postcondition helpers
@@ -126,6 +135,7 @@
     TestableSurfaceFlinger mFlinger;
     sp<mock::NativeWindow> mNativeWindow = new mock::NativeWindow();
     sp<GraphicBuffer> mBuffer = new GraphicBuffer();
+    Hwc2::mock::PowerAdvisor mPowerAdvisor;
 
     // These mocks are created by the test, but are destroyed by SurfaceFlinger
     // by virtue of being stored into a std::unique_ptr. However we still need
@@ -231,6 +241,49 @@
     });
 }
 
+sp<DisplayDevice> DisplayTransactionTest::injectDefaultInternalDisplay(
+        std::function<void(FakeDisplayDeviceInjector&)> injectExtra) {
+    constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{777};
+    constexpr int DEFAULT_DISPLAY_WIDTH = 1080;
+    constexpr int DEFAULT_DISPLAY_HEIGHT = 1920;
+
+    // The DisplayDevice is required to have a framebuffer (behind the
+    // ANativeWindow interface) which uses the actual hardware display
+    // size.
+    EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+            .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_WIDTH), Return(0)));
+    EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+            .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_HEIGHT), Return(0)));
+    EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT));
+    EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT));
+    EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64));
+    EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(AnyNumber());
+
+    auto compositionDisplay = compositionengine::impl::
+            createDisplay(mFlinger.getCompositionEngine(),
+                          compositionengine::DisplayCreationArgsBuilder()
+                                  .setPhysical(
+                                          {DEFAULT_DISPLAY_ID, DisplayConnectionType::Internal})
+                                  .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
+                                  .setPowerAdvisor(&mPowerAdvisor)
+                                  .build());
+
+    auto injector =
+            FakeDisplayDeviceInjector(mFlinger, compositionDisplay, DisplayConnectionType::Internal,
+                                      true /* isPrimary */);
+
+    injector.setNativeWindow(mNativeWindow);
+    if (injectExtra) {
+        injectExtra(injector);
+    }
+
+    auto displayDevice = injector.inject();
+
+    Mock::VerifyAndClear(mNativeWindow.get());
+
+    return displayDevice;
+}
+
 bool DisplayTransactionTest::hasPhysicalHwcDisplay(hwc2_display_t hwcDisplayId) {
     return mFlinger.mutableHwcPhysicalDisplayIdMap().count(hwcDisplayId) == 1;
 }
@@ -353,9 +406,21 @@
     static constexpr Primary PRIMARY = primary;
 
     static auto makeFakeExistingDisplayInjector(DisplayTransactionTest* test) {
+        auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder();
+        if (auto displayId = DISPLAY_ID::get()) {
+            ceDisplayArgs.setPhysical({*displayId, DisplayConnectionType::Internal});
+        } else {
+            ceDisplayArgs.setUseHwcVirtualDisplays(false);
+        }
+        ceDisplayArgs.setPixels({WIDTH, HEIGHT}).setPowerAdvisor(&test->mPowerAdvisor).build();
+
+        auto compositionDisplay =
+                compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
+                                                       ceDisplayArgs.build());
+
         auto injector =
-                FakeDisplayDeviceInjector(test->mFlinger, DISPLAY_ID::get(), CONNECTION_TYPE::value,
-                                          static_cast<bool>(PRIMARY));
+                FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay,
+                                          CONNECTION_TYPE::value, static_cast<bool>(PRIMARY));
 
         injector.setSecure(static_cast<bool>(SECURE));
         injector.setNativeWindow(test->mNativeWindow);
@@ -458,6 +523,25 @@
         injectHwcDisplayWithNoDefaultCapabilities(test);
     }
 
+    static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
+            DisplayTransactionTest* test) {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+
+        auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
+                                     .setPhysical({*DisplayVariant::DISPLAY_ID::get(),
+                                                   PhysicalDisplay::CONNECTION_TYPE})
+                                     .setPixels({DisplayVariant::WIDTH, DisplayVariant::HEIGHT})
+                                     .setIsSecure(static_cast<bool>(DisplayVariant::SECURE))
+                                     .setPowerAdvisor(&test->mPowerAdvisor)
+                                     .setName(std::string("Injected display for ") +
+                                              test_info->test_case_name() + "." + test_info->name())
+                                     .build();
+
+        return compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
+                                                      ceDisplayArgs);
+    }
+
     static void setupHwcHotplugCallExpectations(DisplayTransactionTest* test) {
         constexpr auto CONNECTION_TYPE =
                 PhysicalDisplay::CONNECTION_TYPE == DisplayConnectionType::Internal
@@ -577,6 +661,23 @@
 
     static void injectHwcDisplay(DisplayTransactionTest*) {}
 
+    static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
+            DisplayTransactionTest* test) {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+
+        auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
+                                     .setPixels({Base::WIDTH, Base::HEIGHT})
+                                     .setIsSecure(static_cast<bool>(Base::SECURE))
+                                     .setPowerAdvisor(&test->mPowerAdvisor)
+                                     .setName(std::string("Injected display for ") +
+                                              test_info->test_case_name() + "." + test_info->name())
+                                     .build();
+
+        return compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
+                                                      ceDisplayArgs);
+    }
+
     static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) {
         EXPECT_CALL(*test->mComposer, getActiveConfig(_, _)).Times(0);
     }
@@ -602,6 +703,33 @@
                                 secure, Primary::FALSE, GRALLOC_USAGE_HW_COMPOSER>;
     using Self = HwcVirtualDisplayVariant<width, height, secure>;
 
+    static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
+            DisplayTransactionTest* test) {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+
+        auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
+                                     .setUseHwcVirtualDisplays(false)
+                                     .setPixels({Base::WIDTH, Base::HEIGHT})
+                                     .setIsSecure(static_cast<bool>(Base::SECURE))
+                                     .setPowerAdvisor(&test->mPowerAdvisor)
+                                     .setName(std::string("Injected display for ") +
+                                              test_info->test_case_name() + "." + test_info->name())
+                                     .build();
+
+        auto compositionDisplay =
+                compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
+                                                       ceDisplayArgs);
+        compositionDisplay->setDisplayIdForTesting(Base::DISPLAY_ID::get());
+
+        // Insert display data so that the HWC thinks it created the virtual display.
+        if (const auto displayId = Base::DISPLAY_ID::get()) {
+            test->mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
+        }
+
+        return compositionDisplay;
+    }
+
     static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
         Base::setupNativeWindowSurfaceCreationCallExpectations(test);
         EXPECT_CALL(*test->mNativeWindow, setSwapInterval(0)).Times(1);
@@ -1199,14 +1327,6 @@
  */
 class GetBestColorModeTest : public DisplayTransactionTest {
 public:
-    static constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{777};
-
-    GetBestColorModeTest()
-          : DisplayTransactionTest(),
-            mInjector(FakeDisplayDeviceInjector(mFlinger, DEFAULT_DISPLAY_ID,
-                                                DisplayConnectionType::Internal,
-                                                true /* isPrimary */)) {}
-
     void setHasWideColorGamut(bool hasWideColorGamut) { mHasWideColorGamut = hasWideColorGamut; }
 
     void addHwcColorModesMapping(ui::ColorMode colorMode,
@@ -1219,21 +1339,12 @@
     void setInputRenderIntent(ui::RenderIntent renderIntent) { mInputRenderIntent = renderIntent; }
 
     void getBestColorMode() {
-        mInjector.setHwcColorModes(mHwcColorModes);
-        mInjector.setHasWideColorGamut(mHasWideColorGamut);
-        mInjector.setNativeWindow(mNativeWindow);
-
-        // Creating a DisplayDevice requires getting default dimensions from the
-        // native window.
-        EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
-                .WillRepeatedly(DoAll(SetArgPointee<1>(1080 /* arbitrary */), Return(0)));
-        EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
-                .WillRepeatedly(DoAll(SetArgPointee<1>(1920 /* arbitrary */), Return(0)));
-        EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1);
-        EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1);
-        EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1);
-        EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(1);
-        auto displayDevice = mInjector.inject();
+        auto displayDevice =
+                injectDefaultInternalDisplay([this](FakeDisplayDeviceInjector& injector) {
+                    injector.setHwcColorModes(mHwcColorModes);
+                    injector.setHasWideColorGamut(mHasWideColorGamut);
+                    injector.setNativeWindow(mNativeWindow);
+                });
 
         displayDevice->getCompositionDisplay()
                 ->getDisplayColorProfile()
@@ -1250,7 +1361,6 @@
     ui::RenderIntent mInputRenderIntent;
     bool mHasWideColorGamut = false;
     std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> mHwcColorModes;
-    FakeDisplayDeviceInjector mInjector;
 };
 
 TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeSRGB) {
@@ -1305,7 +1415,6 @@
 
 class DisplayDeviceSetProjectionTest : public DisplayTransactionTest {
 public:
-    static constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{777};
     static constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1080;  // arbitrary
     static constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1920; // arbitrary
 
@@ -1323,23 +1432,9 @@
             mDisplayDevice(createDisplayDevice()) {}
 
     sp<DisplayDevice> createDisplayDevice() {
-        // The DisplayDevice is required to have a framebuffer (behind the
-        // ANativeWindow interface) which uses the actual hardware display
-        // size.
-        EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
-                .WillRepeatedly(DoAll(SetArgPointee<1>(mHardwareDisplaySize.width), Return(0)));
-        EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
-                .WillRepeatedly(DoAll(SetArgPointee<1>(mHardwareDisplaySize.height), Return(0)));
-        EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT));
-        EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT));
-        EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64));
-        EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT));
-
-        return FakeDisplayDeviceInjector(mFlinger, DEFAULT_DISPLAY_ID,
-                                         DisplayConnectionType::Internal, true /* isPrimary */)
-                .setNativeWindow(mNativeWindow)
-                .setPhysicalOrientation(mPhysicalOrientation)
-                .inject();
+        return injectDefaultInternalDisplay([this](FakeDisplayDeviceInjector& injector) {
+            injector.setPhysicalOrientation(mPhysicalOrientation);
+        });
     }
 
     ui::Size SwapWH(const ui::Size size) const { return ui::Size(size.height, size.width); }
@@ -1652,6 +1747,9 @@
     // surfaces.
     injectFakeNativeWindowSurfaceFactory();
 
+    // A compositionengine::Display has already been created
+    auto compositionDisplay = Case::Display::injectCompositionDisplay(this);
+
     // --------------------------------------------------------------------
     // Call Expectations
 
@@ -1674,9 +1772,8 @@
 
     state.isSecure = static_cast<bool>(Case::Display::SECURE);
 
-    auto device =
-            mFlinger.setupNewDisplayDeviceInternal(displayToken, Case::Display::DISPLAY_ID::get(),
-                                                   state, displaySurface, producer);
+    auto device = mFlinger.setupNewDisplayDeviceInternal(displayToken, compositionDisplay, state,
+                                                         displaySurface, producer);
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -1715,14 +1812,7 @@
 }
 
 TEST_F(SetupNewDisplayDeviceInternalTest, createHwcVirtualDisplay) {
-    using Case = HwcVirtualDisplayCase;
-
-    // Insert display data so that the HWC thinks it created the virtual display.
-    const auto displayId = Case::Display::DISPLAY_ID::get();
-    ASSERT_TRUE(displayId);
-    mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
-
-    setupNewDisplayDeviceInternalTest<Case>();
+    setupNewDisplayDeviceInternalTest<HwcVirtualDisplayCase>();
 }
 
 TEST_F(SetupNewDisplayDeviceInternalTest, createWideColorP3Display) {
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 65b3e35..ba5c0c2 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -48,6 +48,7 @@
     MOCK_METHOD1(setCallback, void(VSyncSource::Callback*));
     MOCK_METHOD1(setPhaseOffset, void(nsecs_t));
     MOCK_METHOD1(pauseVsyncCallback, void(bool));
+    MOCK_CONST_METHOD1(dump, void(std::string&));
 };
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 18e9941..7557faf 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -84,7 +84,7 @@
     const auto layer = createLayer();
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
     EXPECT_CALL(*layer, getFrameSelectionPriority()).WillRepeatedly(Return(1));
-    EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
 
     EXPECT_EQ(1, layerCount());
     EXPECT_EQ(0, activeLayerCount());
@@ -113,7 +113,7 @@
     const auto layer = createLayer();
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
     EXPECT_CALL(*layer, getFrameSelectionPriority()).WillRepeatedly(Return(1));
-    EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
 
     EXPECT_EQ(1, layerCount());
     EXPECT_EQ(0, activeLayerCount());
@@ -137,15 +137,15 @@
 
     EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
     EXPECT_CALL(*layer1, getFrameSelectionPriority()).WillRepeatedly(Return(1));
-    EXPECT_CALL(*layer1, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+    EXPECT_CALL(*layer1, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
 
     EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
     EXPECT_CALL(*layer2, getFrameSelectionPriority()).WillRepeatedly(Return(1));
-    EXPECT_CALL(*layer2, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+    EXPECT_CALL(*layer2, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
 
     EXPECT_CALL(*layer3, isVisible()).WillRepeatedly(Return(true));
     EXPECT_CALL(*layer3, getFrameSelectionPriority()).WillRepeatedly(Return(1));
-    EXPECT_CALL(*layer3, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+    EXPECT_CALL(*layer3, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
     nsecs_t time = mTime;
 
     EXPECT_EQ(3, layerCount());
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
index 959c256..8559a5e 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
@@ -36,6 +36,7 @@
 protected:
     static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfoV2::HISTORY_SIZE;
     static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = LayerInfoV2::MAX_FREQUENT_LAYER_PERIOD_NS;
+    static constexpr auto FREQUENT_LAYER_WINDOW_SIZE = LayerInfoV2::FREQUENT_LAYER_WINDOW_SIZE;
 
     static constexpr float LO_FPS = 30.f;
     static constexpr auto LO_FPS_PERIOD = static_cast<nsecs_t>(1e9f / LO_FPS);
@@ -90,7 +91,7 @@
 TEST_F(LayerHistoryTestV2, oneLayer) {
     const auto layer = createLayer();
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
 
     EXPECT_EQ(1, layerCount());
     EXPECT_EQ(0, activeLayerCount());
@@ -121,7 +122,7 @@
 TEST_F(LayerHistoryTestV2, oneInvisibleLayer) {
     const auto layer = createLayer();
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
 
     EXPECT_EQ(1, layerCount());
     EXPECT_EQ(0, activeLayerCount());
@@ -145,7 +146,7 @@
 TEST_F(LayerHistoryTestV2, explicitTimestamp) {
     const auto layer = createLayer();
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
 
     EXPECT_EQ(1, layerCount());
     EXPECT_EQ(0, activeLayerCount());
@@ -166,7 +167,7 @@
 TEST_F(LayerHistoryTestV2, oneLayerNoVote) {
     const auto layer = createLayer();
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
 
     setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::NoVote);
 
@@ -193,7 +194,7 @@
 TEST_F(LayerHistoryTestV2, oneLayerMinVote) {
     const auto layer = createLayer();
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
 
     setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Min);
 
@@ -221,7 +222,7 @@
 TEST_F(LayerHistoryTestV2, oneLayerMaxVote) {
     const auto layer = createLayer();
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
 
     setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Max);
 
@@ -249,7 +250,7 @@
 TEST_F(LayerHistoryTestV2, oneLayerExplicitVote) {
     auto layer = createLayer();
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRate())
+    EXPECT_CALL(*layer, getFrameRateForLayerTree())
             .WillRepeatedly(
                     Return(Layer::FrameRate(73.4f, Layer::FrameRateCompatibility::Default)));
 
@@ -272,7 +273,8 @@
     setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
     time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
     ASSERT_TRUE(history().summarize(time).empty());
-    // TODO: activeLayerCount() should be 0 but it is 1 since getFrameRate() returns a value > 0
+    // TODO: activeLayerCount() should be 0 but it is 1 since getFrameRateForLayerTree() returns a
+    // value > 0
     EXPECT_EQ(1, activeLayerCount());
     EXPECT_EQ(0, frequentLayerCount(time));
 }
@@ -280,7 +282,7 @@
 TEST_F(LayerHistoryTestV2, oneLayerExplicitExactVote) {
     auto layer = createLayer();
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRate())
+    EXPECT_CALL(*layer, getFrameRateForLayerTree())
             .WillRepeatedly(Return(
                     Layer::FrameRate(73.4f, Layer::FrameRateCompatibility::ExactOrMultiple)));
 
@@ -304,7 +306,8 @@
     setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
     time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
     ASSERT_TRUE(history().summarize(time).empty());
-    // TODO: activeLayerCount() should be 0 but it is 1 since getFrameRate() returns a value > 0
+    // TODO: activeLayerCount() should be 0 but it is 1 since getFrameRateForLayerTree() returns a
+    // value > 0
     EXPECT_EQ(1, activeLayerCount());
     EXPECT_EQ(0, frequentLayerCount(time));
 }
@@ -315,13 +318,13 @@
     auto layer3 = createLayer();
 
     EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer1, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+    EXPECT_CALL(*layer1, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
 
     EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer2, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+    EXPECT_CALL(*layer2, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
 
     EXPECT_CALL(*layer3, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer3, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+    EXPECT_CALL(*layer3, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
 
     nsecs_t time = systemTime();
 
@@ -451,5 +454,61 @@
     EXPECT_EQ(0, frequentLayerCount(time));
 }
 
+TEST_F(LayerHistoryTestV2, inactiveLayers) {
+    auto layer = createLayer();
+
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    nsecs_t time = systemTime();
+
+    // the very first updates makes the layer frequent
+    for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
+        history().record(layer.get(), time, time);
+        time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+        EXPECT_EQ(1, layerCount());
+        ASSERT_EQ(1, history().summarize(time).size());
+        EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+        EXPECT_EQ(1, activeLayerCount());
+        EXPECT_EQ(1, frequentLayerCount(time));
+    }
+
+    // the next update with the MAX_FREQUENT_LAYER_PERIOD_NS will get us to infrequent
+    history().record(layer.get(), time, time);
+    time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+    EXPECT_EQ(1, layerCount());
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+
+    // advance the time for the previous frame to be inactive
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+
+    // Now event if we post a quick few frame we should stay infrequent
+    for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
+        history().record(layer.get(), time, time);
+        time += HI_FPS_PERIOD;
+
+        EXPECT_EQ(1, layerCount());
+        ASSERT_EQ(1, history().summarize(time).size());
+        EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+        EXPECT_EQ(1, activeLayerCount());
+        EXPECT_EQ(0, frequentLayerCount(time));
+    }
+
+    // More quick frames will get us to frequent again
+    history().record(layer.get(), time, time);
+    time += HI_FPS_PERIOD;
+
+    EXPECT_EQ(1, layerCount());
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+}
+
 } // namespace
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
index 910e73b..8d49201 100644
--- a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
@@ -42,25 +42,25 @@
                                  appEarlyGlDuration) {}
 };
 
-class PhaseOffsetsTest : public testing::Test {
+class PhaseDurationTest : public testing::Test {
 protected:
-    PhaseOffsetsTest()
-          : mPhaseOffsets(60.0f, 10'500'000, 20'500'000, 16'000'000, 33'500'000, 13'500'000,
-                          38'000'000) {}
+    PhaseDurationTest()
+          : mPhaseDurations(60.0f, 10'500'000, 20'500'000, 16'000'000, 33'500'000, 13'500'000,
+                            38'000'000) {}
 
-    ~PhaseOffsetsTest() = default;
+    ~PhaseDurationTest() = default;
 
-    TestablePhaseOffsetsAsDurations mPhaseOffsets;
+    TestablePhaseOffsetsAsDurations mPhaseDurations;
 };
 
 namespace {
 /* ------------------------------------------------------------------------
  * Test cases
  */
-TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_60Hz) {
-    mPhaseOffsets.setRefreshRateFps(60.0f);
-    auto currentOffsets = mPhaseOffsets.getCurrentOffsets();
-    auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(60.0f);
+TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_60Hz) {
+    mPhaseDurations.setRefreshRateFps(60.0f);
+    auto currentOffsets = mPhaseDurations.getCurrentOffsets();
+    auto offsets = mPhaseDurations.getOffsetsForRefreshRate(60.0f);
 
     EXPECT_EQ(currentOffsets, offsets);
     EXPECT_EQ(offsets.late.sf, 6'166'667);
@@ -76,10 +76,10 @@
     EXPECT_EQ(offsets.earlyGl.app, 15'166'668);
 }
 
-TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_90Hz) {
-    mPhaseOffsets.setRefreshRateFps(90.0f);
-    auto currentOffsets = mPhaseOffsets.getCurrentOffsets();
-    auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(90.0f);
+TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_90Hz) {
+    mPhaseDurations.setRefreshRateFps(90.0f);
+    auto currentOffsets = mPhaseDurations.getCurrentOffsets();
+    auto offsets = mPhaseDurations.getOffsetsForRefreshRate(90.0f);
 
     EXPECT_EQ(currentOffsets, offsets);
     EXPECT_EQ(offsets.late.sf, 611'111);
@@ -95,7 +95,7 @@
     EXPECT_EQ(offsets.earlyGl.app, 4'055'555);
 }
 
-TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_DefaultOffsets) {
+TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_DefaultOffsets) {
     TestablePhaseOffsetsAsDurations phaseOffsetsWithDefaultValues(60.0f, -1, -1, -1, -1, -1, -1);
 
     auto validateOffsets = [](auto& offsets) {
@@ -125,6 +125,54 @@
     validateOffsets(offsets);
 }
 
+TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_unknownRefreshRate) {
+    auto offsets = mPhaseDurations.getOffsetsForRefreshRate(14.7f);
+
+    EXPECT_EQ(offsets.late.sf, 57'527'208);
+
+    EXPECT_EQ(offsets.late.app, 37'027'208);
+
+    EXPECT_EQ(offsets.early.sf, 52'027'208);
+
+    EXPECT_EQ(offsets.early.app, 18'527'208);
+
+    EXPECT_EQ(offsets.earlyGl.sf, 54'527'208);
+
+    EXPECT_EQ(offsets.earlyGl.app, 16'527'208);
+}
+
+} // namespace
+
+class TestablePhaseOffsets : public impl::PhaseOffsets {
+public:
+    TestablePhaseOffsets() : impl::PhaseOffsets({60.0f, 90.0f}, 60.0f, 10'000'000) {}
+};
+
+class PhaseOffsetsTest : public testing::Test {
+protected:
+    PhaseOffsetsTest() = default;
+    ~PhaseOffsetsTest() = default;
+
+    TestablePhaseOffsets mPhaseOffsets;
+};
+
+namespace {
+TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_unknownRefreshRate) {
+    auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(14.7f);
+
+    EXPECT_EQ(offsets.late.sf, 1'000'000);
+
+    EXPECT_EQ(offsets.late.app, 1'000'000);
+
+    EXPECT_EQ(offsets.early.sf, 1'000'000);
+
+    EXPECT_EQ(offsets.early.app, 1'000'000);
+
+    EXPECT_EQ(offsets.earlyGl.sf, 1'000'000);
+
+    EXPECT_EQ(offsets.earlyGl.app, 1'000'000);
+}
+
 } // namespace
 } // namespace scheduler
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 7e62513..dd04076 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -21,6 +21,7 @@
 #include <log/log.h>
 #include <thread>
 
+#include "../../Scheduler/RefreshRateConfigs.h"
 #include "DisplayHardware/HWC2.h"
 #include "Scheduler/RefreshRateConfigs.h"
 
@@ -93,8 +94,8 @@
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
 
-    const auto minRate = refreshRateConfigs->getMinRefreshRate();
-    const auto performanceRate = refreshRateConfigs->getMaxRefreshRate();
+    const auto& minRate = refreshRateConfigs->getMinRefreshRate();
+    const auto& performanceRate = refreshRateConfigs->getMaxRefreshRate();
 
     RefreshRate expectedDefaultConfig = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
     ASSERT_EQ(expectedDefaultConfig, minRate);
@@ -102,8 +103,8 @@
                                              90};
     ASSERT_EQ(expectedPerformanceConfig, performanceRate);
 
-    const auto minRateByPolicy = refreshRateConfigs->getMinRefreshRateByPolicy();
-    const auto performanceRateByPolicy = refreshRateConfigs->getMaxRefreshRateByPolicy();
+    const auto& minRateByPolicy = refreshRateConfigs->getMinRefreshRateByPolicy();
+    const auto& performanceRateByPolicy = refreshRateConfigs->getMaxRefreshRateByPolicy();
     ASSERT_EQ(minRateByPolicy, minRate);
     ASSERT_EQ(performanceRateByPolicy, performanceRate);
 }
@@ -115,10 +116,10 @@
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
 
-    const auto minRate = refreshRateConfigs->getMinRefreshRateByPolicy();
-    const auto performanceRate = refreshRateConfigs->getMaxRefreshRate();
-    const auto minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
-    const auto performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
+    const auto& minRate = refreshRateConfigs->getMinRefreshRateByPolicy();
+    const auto& performanceRate = refreshRateConfigs->getMaxRefreshRate();
+    const auto& minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
+    const auto& performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
 
     RefreshRate expectedDefaultConfig = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
     ASSERT_EQ(expectedDefaultConfig, minRate);
@@ -128,8 +129,8 @@
     ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_90, 60, 90, nullptr), 0);
     refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
 
-    const auto minRate90 = refreshRateConfigs->getMinRefreshRateByPolicy();
-    const auto performanceRate90 = refreshRateConfigs->getMaxRefreshRateByPolicy();
+    const auto& minRate90 = refreshRateConfigs->getMinRefreshRateByPolicy();
+    const auto& performanceRate90 = refreshRateConfigs->getMaxRefreshRateByPolicy();
 
     RefreshRate expectedPerformanceConfig = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_1, "90fps",
                                              90};
@@ -145,8 +146,8 @@
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
 
-    auto minRate = refreshRateConfigs->getMinRefreshRateByPolicy();
-    auto performanceRate = refreshRateConfigs->getMaxRefreshRateByPolicy();
+    auto& minRate = refreshRateConfigs->getMinRefreshRateByPolicy();
+    auto& performanceRate = refreshRateConfigs->getMaxRefreshRateByPolicy();
 
     RefreshRate expectedDefaultConfig = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
     ASSERT_EQ(expectedDefaultConfig, minRate);
@@ -156,8 +157,8 @@
 
     ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 60, 60, nullptr), 0);
 
-    auto minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
-    auto performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
+    auto& minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
+    auto& performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
     ASSERT_EQ(expectedDefaultConfig, minRate60);
     ASSERT_EQ(expectedDefaultConfig, performanceRate60);
 }
@@ -169,19 +170,19 @@
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
     {
-        auto current = refreshRateConfigs->getCurrentRefreshRate();
+        auto& current = refreshRateConfigs->getCurrentRefreshRate();
         EXPECT_EQ(current.configId, HWC_CONFIG_ID_60);
     }
 
     refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
     {
-        auto current = refreshRateConfigs->getCurrentRefreshRate();
+        auto& current = refreshRateConfigs->getCurrentRefreshRate();
         EXPECT_EQ(current.configId, HWC_CONFIG_ID_90);
     }
 
     ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_90, 90, 90, nullptr), 0);
     {
-        auto current = refreshRateConfigs->getCurrentRefreshRate();
+        auto& current = refreshRateConfigs->getCurrentRefreshRate();
         EXPECT_EQ(current.configId, HWC_CONFIG_ID_90);
     }
 }
@@ -247,7 +248,34 @@
               refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
 }
 
+TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_noLayers) {
+    bool ignored;
+    std::vector<RefreshRateConfigs::InputConfig> configs{
+            {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+             {HWC_CONFIG_ID_72, HWC_GROUP_ID_0, VSYNC_72},
+             {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+    auto refreshRateConfigs = std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/
+                                                                   HWC_CONFIG_ID_72);
+
+    RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+    RefreshRate expected72Config = {HWC_CONFIG_ID_72, VSYNC_72, HWC_GROUP_ID_0, "72fps", 72};
+
+    // If there are not layers, there is not content detection, so return the current
+    // refresh rate.
+    auto layers = std::vector<LayerRequirement>{};
+    EXPECT_EQ(expected72Config,
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/
+                                                             false, &ignored));
+
+    // Current refresh rate can always be changed.
+    refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_60);
+    EXPECT_EQ(expected60Config,
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/
+                                                             false, &ignored));
+}
+
 TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_60_90) {
+    bool ignored;
     std::vector<RefreshRateConfigs::InputConfig> configs{
             {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
              {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
@@ -263,134 +291,163 @@
     lr.vote = LayerVoteType::Min;
     lr.name = "Min";
     EXPECT_EQ(expected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.vote = LayerVoteType::Max;
     lr.name = "Max";
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 90.0f;
     lr.vote = LayerVoteType::Heuristic;
     lr.name = "90Hz Heuristic";
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 60.0f;
     lr.name = "60Hz Heuristic";
     EXPECT_EQ(expected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 45.0f;
     lr.name = "45Hz Heuristic";
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 30.0f;
     lr.name = "30Hz Heuristic";
     EXPECT_EQ(expected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 24.0f;
     lr.name = "24Hz Heuristic";
     EXPECT_EQ(expected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.name = "";
     ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 60, 60, nullptr), 0);
 
     lr.vote = LayerVoteType::Min;
     EXPECT_EQ(expected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.vote = LayerVoteType::Max;
     EXPECT_EQ(expected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 90.0f;
     lr.vote = LayerVoteType::Heuristic;
     EXPECT_EQ(expected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 60.0f;
     EXPECT_EQ(expected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 45.0f;
     EXPECT_EQ(expected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 30.0f;
     EXPECT_EQ(expected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 24.0f;
     EXPECT_EQ(expected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_90, 90, 90, nullptr), 0);
 
     lr.vote = LayerVoteType::Min;
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.vote = LayerVoteType::Max;
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 90.0f;
     lr.vote = LayerVoteType::Heuristic;
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 60.0f;
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 45.0f;
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 30.0f;
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 24.0f;
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 0, 120, nullptr), 0);
     lr.vote = LayerVoteType::Min;
     EXPECT_EQ(expected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.vote = LayerVoteType::Max;
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 90.0f;
     lr.vote = LayerVoteType::Heuristic;
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 60.0f;
     EXPECT_EQ(expected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 45.0f;
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 30.0f;
     EXPECT_EQ(expected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 24.0f;
     EXPECT_EQ(expected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 }
 
 TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_60_72_90) {
+    bool ignored;
     std::vector<RefreshRateConfigs::InputConfig> configs{
             {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
              {HWC_CONFIG_ID_72, HWC_GROUP_ID_0, VSYNC_72},
@@ -407,35 +464,43 @@
 
     lr.vote = LayerVoteType::Min;
     EXPECT_EQ(expected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.vote = LayerVoteType::Max;
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 90.0f;
     lr.vote = LayerVoteType::Heuristic;
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 60.0f;
     EXPECT_EQ(expected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 45.0f;
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 30.0f;
     EXPECT_EQ(expected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 24.0f;
     EXPECT_EQ(expected72Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 }
 
 TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_30_60_72_90_120) {
+    bool ignored;
     std::vector<RefreshRateConfigs::InputConfig> configs{
             {{HWC_CONFIG_ID_30, HWC_GROUP_ID_0, VSYNC_30},
              {HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
@@ -461,24 +526,28 @@
     lr2.desiredRefreshRate = 60.0f;
     lr2.vote = LayerVoteType::Heuristic;
     EXPECT_EQ(expected120Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr1.desiredRefreshRate = 24.0f;
     lr1.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 48.0f;
     lr2.vote = LayerVoteType::Heuristic;
     EXPECT_EQ(expected72Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr1.desiredRefreshRate = 24.0f;
     lr1.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 48.0f;
     lr2.vote = LayerVoteType::Heuristic;
     EXPECT_EQ(expected72Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 }
 
 TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_30_60_90_120_DifferentTypes) {
+    bool ignored;
     std::vector<RefreshRateConfigs::InputConfig> configs{
             {{HWC_CONFIG_ID_30, HWC_GROUP_ID_0, VSYNC_30},
              {HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
@@ -506,7 +575,8 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "60Hz Heuristic";
     EXPECT_EQ(expected120Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr1.desiredRefreshRate = 24.0f;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -515,7 +585,8 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "60Hz Heuristic";
     EXPECT_EQ(expected120Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr1.desiredRefreshRate = 24.0f;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -524,7 +595,8 @@
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "60Hz ExplicitDefault";
     EXPECT_EQ(expected120Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr1.desiredRefreshRate = 24.0f;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -533,7 +605,8 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "90Hz Heuristic";
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr1.desiredRefreshRate = 24.0f;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -542,7 +615,8 @@
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz Heuristic";
     EXPECT_EQ(expected72Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr1.desiredRefreshRate = 24.0f;
     lr1.vote = LayerVoteType::ExplicitDefault;
@@ -551,7 +625,8 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "90Hz Heuristic";
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr1.desiredRefreshRate = 24.0f;
     lr1.vote = LayerVoteType::Heuristic;
@@ -560,7 +635,8 @@
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz ExplicitDefault";
     EXPECT_EQ(expected72Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr1.desiredRefreshRate = 24.0f;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -569,7 +645,8 @@
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz ExplicitDefault";
     EXPECT_EQ(expected72Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr1.desiredRefreshRate = 24.0f;
     lr1.vote = LayerVoteType::ExplicitDefault;
@@ -578,10 +655,12 @@
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.name = "90Hz ExplicitExactOrMultiple";
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 }
 
 TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_30_60) {
+    bool ignored;
     std::vector<RefreshRateConfigs::InputConfig> configs{
             {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
              {HWC_CONFIG_ID_30, HWC_GROUP_ID_0, VSYNC_30}}};
@@ -596,35 +675,43 @@
 
     lr.vote = LayerVoteType::Min;
     EXPECT_EQ(expected30Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.vote = LayerVoteType::Max;
     EXPECT_EQ(expected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 90.0f;
     lr.vote = LayerVoteType::Heuristic;
     EXPECT_EQ(expected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 60.0f;
     EXPECT_EQ(expected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 45.0f;
     EXPECT_EQ(expected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 30.0f;
     EXPECT_EQ(expected30Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 24.0f;
     EXPECT_EQ(expected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 }
 
 TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_30_60_72_90) {
+    bool ignored;
     std::vector<RefreshRateConfigs::InputConfig> configs{
             {{HWC_CONFIG_ID_30, HWC_GROUP_ID_0, VSYNC_30},
              {HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
@@ -644,57 +731,71 @@
     lr.vote = LayerVoteType::Min;
     lr.name = "Min";
     EXPECT_EQ(expected30Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.vote = LayerVoteType::Max;
     lr.name = "Max";
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 90.0f;
     lr.vote = LayerVoteType::Heuristic;
     lr.name = "90Hz Heuristic";
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 60.0f;
     lr.name = "60Hz Heuristic";
     EXPECT_EQ(expected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ true));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ true,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 45.0f;
     lr.name = "45Hz Heuristic";
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ true));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ true,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 30.0f;
     lr.name = "30Hz Heuristic";
     EXPECT_EQ(expected30Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ true));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ true,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 24.0f;
     lr.name = "24Hz Heuristic";
     EXPECT_EQ(expected72Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ true));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ true,
+                                                             &ignored));
 
     lr.desiredRefreshRate = 24.0f;
     lr.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr.name = "24Hz ExplicitExactOrMultiple";
     EXPECT_EQ(expected72Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ true));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ true,
+                                                             &ignored));
 }
 
 TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_PriorityTest) {
+    bool ignored;
     std::vector<RefreshRateConfigs::InputConfig> configs{
             {{HWC_CONFIG_ID_30, HWC_GROUP_ID_0, VSYNC_30},
              {HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
@@ -714,48 +815,56 @@
     lr1.vote = LayerVoteType::Min;
     lr2.vote = LayerVoteType::Max;
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr1.vote = LayerVoteType::Min;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 24.0f;
     EXPECT_EQ(expected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr1.vote = LayerVoteType::Min;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 24.0f;
     EXPECT_EQ(expected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr1.vote = LayerVoteType::Max;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 60.0f;
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr1.vote = LayerVoteType::Max;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 60.0f;
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr1.vote = LayerVoteType::Heuristic;
     lr1.desiredRefreshRate = 15.0f;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 45.0f;
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr1.vote = LayerVoteType::Heuristic;
     lr1.desiredRefreshRate = 30.0f;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 45.0f;
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 }
 
 TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_24FpsVideo) {
+    bool ignored;
     std::vector<RefreshRateConfigs::InputConfig> configs{
             {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
              {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
@@ -773,7 +882,8 @@
     for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
         lr.desiredRefreshRate = fps;
         const auto& refreshRate =
-                refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false);
+                refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                               &ignored);
         printf("%.2fHz chooses %s\n", fps, refreshRate.name.c_str());
         EXPECT_EQ(expected60Config, refreshRate);
     }
@@ -808,6 +918,7 @@
 }
 
 TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_Explicit) {
+    bool ignored;
     std::vector<RefreshRateConfigs::InputConfig> configs{
             {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
              {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
@@ -827,21 +938,24 @@
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 90.0f;
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr1.vote = LayerVoteType::ExplicitDefault;
     lr1.desiredRefreshRate = 90.0f;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 60.0f;
     EXPECT_EQ(expected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr1.vote = LayerVoteType::Heuristic;
     lr1.desiredRefreshRate = 90.0f;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 60.0f;
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 }
 
 TEST_F(RefreshRateConfigsTest, testInPolicy) {
@@ -855,6 +969,7 @@
 }
 
 TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_75HzContent) {
+    bool ignored;
     std::vector<RefreshRateConfigs::InputConfig> configs{
             {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
              {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
@@ -872,13 +987,15 @@
     for (float fps = 75.0f; fps < 100.0f; fps += 0.1f) {
         lr.desiredRefreshRate = fps;
         const auto& refreshRate =
-                refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false);
+                refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                               &ignored);
         printf("%.2fHz chooses %s\n", fps, refreshRate.name.c_str());
         EXPECT_EQ(expected90Config, refreshRate);
     }
 }
 
 TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_Multiples) {
+    bool ignored;
     std::vector<RefreshRateConfigs::InputConfig> configs{
             {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
              {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
@@ -900,7 +1017,8 @@
     lr2.desiredRefreshRate = 90.0f;
     lr2.name = "90Hz Heuristic";
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60.0f;
@@ -909,7 +1027,8 @@
     lr2.desiredRefreshRate = 90.0f;
     lr2.name = "90Hz ExplicitDefault";
     EXPECT_EQ(expected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60.0f;
@@ -917,7 +1036,8 @@
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 30.0f;
@@ -926,7 +1046,8 @@
     lr2.desiredRefreshRate = 90.0f;
     lr2.name = "90Hz Heuristic";
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 30.0f;
@@ -934,10 +1055,12 @@
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
     EXPECT_EQ(expected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+                                                             &ignored));
 }
 
 TEST_F(RefreshRateConfigsTest, scrollWhileWatching60fps_60_90) {
+    bool ignored;
     std::vector<RefreshRateConfigs::InputConfig> configs{
             {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
              {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
@@ -957,28 +1080,32 @@
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::NoVote;
     lr2.name = "NoVote";
-    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers, false));
+    EXPECT_EQ(expected60Config,
+              refreshRateConfigs->getRefreshRateForContentV2(layers, false, &ignored));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60.0f;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::NoVote;
     lr2.name = "NoVote";
-    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers, true));
+    EXPECT_EQ(expected90Config,
+              refreshRateConfigs->getRefreshRateForContentV2(layers, true, &ignored));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60.0f;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
-    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers, true));
+    EXPECT_EQ(expected90Config,
+              refreshRateConfigs->getRefreshRateForContentV2(layers, true, &ignored));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60.0f;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
-    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers, false));
+    EXPECT_EQ(expected90Config,
+              refreshRateConfigs->getRefreshRateForContentV2(layers, false, &ignored));
 
     // The other layer starts to provide buffers
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -987,7 +1114,108 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 90.0f;
     lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers, false));
+    EXPECT_EQ(expected90Config,
+              refreshRateConfigs->getRefreshRateForContentV2(layers, false, &ignored));
+}
+
+TEST_F(RefreshRateConfigsTest, touchConsidered) {
+    bool touchConsidered;
+    std::vector<RefreshRateConfigs::InputConfig> configs{
+            {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+             {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    refreshRateConfigs->getRefreshRateForContentV2({}, false, &touchConsidered);
+    EXPECT_EQ(false, touchConsidered);
+
+    refreshRateConfigs->getRefreshRateForContentV2({}, true, &touchConsidered);
+    EXPECT_EQ(true, touchConsidered);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+                                                LayerRequirement{.weight = 1.0f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 60.0f;
+    lr1.name = "60Hz ExplicitExactOrMultiple";
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.name = "NoVote";
+    refreshRateConfigs->getRefreshRateForContentV2(layers, true, &touchConsidered);
+    EXPECT_EQ(true, touchConsidered);
+
+    lr1.vote = LayerVoteType::ExplicitDefault;
+    lr1.desiredRefreshRate = 60.0f;
+    lr1.name = "60Hz ExplicitExactOrMultiple";
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.name = "NoVote";
+    refreshRateConfigs->getRefreshRateForContentV2(layers, true, &touchConsidered);
+    EXPECT_EQ(false, touchConsidered);
+
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 60.0f;
+    lr1.name = "60Hz ExplicitExactOrMultiple";
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.name = "NoVote";
+    refreshRateConfigs->getRefreshRateForContentV2(layers, true, &touchConsidered);
+    EXPECT_EQ(true, touchConsidered);
+
+    lr1.vote = LayerVoteType::ExplicitDefault;
+    lr1.desiredRefreshRate = 60.0f;
+    lr1.name = "60Hz ExplicitExactrMultiple";
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.name = "NoVote";
+    refreshRateConfigs->getRefreshRateForContentV2(layers, true, &touchConsidered);
+    EXPECT_EQ(false, touchConsidered);
+}
+
+TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_ExplicitDefault) {
+    bool ignored;
+    std::vector<RefreshRateConfigs::InputConfig> configs{
+            {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+             {HWC_CONFIG_ID_72, HWC_GROUP_ID_0, VSYNC_72},
+             {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90},
+             {HWC_CONFIG_ID_120, HWC_GROUP_ID_0, VSYNC_120}}};
+
+    auto refreshRateConfigs = std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/
+                                                                   HWC_CONFIG_ID_60);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& lr = layers[0];
+
+    // Prepare a table with the vote and the expected refresh rate
+    const std::vector<std::pair<float, float>> testCases = {
+            {130, 120}, {120, 120}, {119, 120}, {110, 120},
+
+            {100, 90},  {90, 90},   {89, 90},
+
+            {80, 72},   {73, 72},   {72, 72},   {71, 72},   {70, 72},
+
+            {65, 60},   {60, 60},   {59, 60},   {58, 60},
+
+            {55, 90},   {50, 90},   {45, 90},
+
+            {42, 120},  {40, 120},  {39, 120},
+
+            {37, 72},   {36, 72},   {35, 72},
+
+            {30, 60},
+    };
+
+    for (const auto& test : testCases) {
+        lr.vote = LayerVoteType::ExplicitDefault;
+        lr.desiredRefreshRate = test.first;
+
+        std::stringstream ss;
+        ss << "ExplicitDefault " << test.first << " fps";
+        lr.name = ss.str();
+
+        const auto& refreshRate =
+                refreshRateConfigs->getRefreshRateForContentV2(layers, false, &ignored);
+        EXPECT_FLOAT_EQ(refreshRate.fps, test.second)
+                << "Expecting " << test.first << "fps => " << test.second << "Hz";
+    }
 }
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
new file mode 100644
index 0000000..b069085
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -0,0 +1,473 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/LayerMetadata.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+#include "BufferQueueLayer.h"
+#include "BufferStateLayer.h"
+#include "EffectLayer.h"
+#include "Layer.h"
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/MockDispSync.h"
+#include "mock/MockEventControlThread.h"
+#include "mock/MockEventThread.h"
+#include "mock/MockMessageQueue.h"
+
+namespace android {
+
+using testing::_;
+using testing::DoAll;
+using testing::Mock;
+using testing::Return;
+using testing::SetArgPointee;
+
+using android::Hwc2::IComposer;
+using android::Hwc2::IComposerClient;
+
+using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+
+using FrameRate = Layer::FrameRate;
+using FrameRateCompatibility = Layer::FrameRateCompatibility;
+
+class LayerFactory {
+public:
+    virtual ~LayerFactory() = default;
+
+    virtual std::string name() = 0;
+    virtual sp<Layer> createLayer(TestableSurfaceFlinger& flinger) = 0;
+
+protected:
+    static constexpr uint32_t WIDTH = 100;
+    static constexpr uint32_t HEIGHT = 100;
+    static constexpr uint32_t LAYER_FLAGS = 0;
+};
+
+class BufferQueueLayerFactory : public LayerFactory {
+public:
+    std::string name() override { return "BufferQueueLayer"; }
+    sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override {
+        sp<Client> client;
+        LayerCreationArgs args(flinger.flinger(), client, "buffer-queue-layer", WIDTH, HEIGHT,
+                               LAYER_FLAGS, LayerMetadata());
+        return new BufferQueueLayer(args);
+    }
+};
+
+class BufferStateLayerFactory : public LayerFactory {
+public:
+    std::string name() override { return "BufferStateLayer"; }
+    sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override {
+        sp<Client> client;
+        LayerCreationArgs args(flinger.flinger(), client, "buffer-queue-layer", WIDTH, HEIGHT,
+                               LAYER_FLAGS, LayerMetadata());
+        return new BufferStateLayer(args);
+    }
+};
+
+class EffectLayerFactory : public LayerFactory {
+public:
+    std::string name() override { return "EffectLayer"; }
+    sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override {
+        sp<Client> client;
+        LayerCreationArgs args(flinger.flinger(), client, "color-layer", WIDTH, HEIGHT, LAYER_FLAGS,
+                               LayerMetadata());
+        return new EffectLayer(args);
+    }
+};
+
+std::string PrintToStringParamName(
+        const ::testing::TestParamInfo<std::shared_ptr<LayerFactory>>& info) {
+    return info.param->name();
+}
+
+/**
+ * This class tests the behaviour of Layer::SetFrameRate and Layer::GetFrameRate
+ */
+class SetFrameRateTest : public ::testing::TestWithParam<std::shared_ptr<LayerFactory>> {
+protected:
+    const FrameRate FRAME_RATE_VOTE1 = FrameRate(67.f, FrameRateCompatibility::Default);
+    const FrameRate FRAME_RATE_VOTE2 = FrameRate(14.f, FrameRateCompatibility::ExactOrMultiple);
+    const FrameRate FRAME_RATE_VOTE3 = FrameRate(99.f, FrameRateCompatibility::NoVote);
+    const FrameRate FRAME_RATE_TREE = FrameRate(0, FrameRateCompatibility::NoVote);
+    const FrameRate FRAME_RATE_NO_VOTE = FrameRate(0, FrameRateCompatibility::Default);
+
+    SetFrameRateTest();
+
+    void setupScheduler();
+    void setupComposer(uint32_t virtualDisplayCount);
+
+    void addChild(sp<Layer> layer, sp<Layer> child);
+    void removeChild(sp<Layer> layer, sp<Layer> child);
+    void reparentChildren(sp<Layer> layer, sp<Layer> child);
+    void commitTransaction();
+
+    TestableSurfaceFlinger mFlinger;
+    Hwc2::mock::Composer* mComposer = nullptr;
+    mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
+
+    std::vector<sp<Layer>> mLayers;
+};
+
+SetFrameRateTest::SetFrameRateTest() {
+    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());
+
+    mFlinger.mutableUseFrameRateApi() = true;
+
+    setupScheduler();
+    setupComposer(0);
+
+    mFlinger.mutableEventQueue().reset(mMessageQueue);
+}
+void SetFrameRateTest::addChild(sp<Layer> layer, sp<Layer> child) {
+    layer.get()->addChild(child.get());
+}
+
+void SetFrameRateTest::removeChild(sp<Layer> layer, sp<Layer> child) {
+    layer.get()->removeChild(child.get());
+}
+
+void SetFrameRateTest::reparentChildren(sp<Layer> parent, sp<Layer> newParent) {
+    parent.get()->reparentChildren(newParent);
+}
+
+void SetFrameRateTest::commitTransaction() {
+    for (auto layer : mLayers) {
+        layer.get()->commitTransaction(layer.get()->getCurrentState());
+    }
+}
+
+void SetFrameRateTest::setupScheduler() {
+    auto eventThread = std::make_unique<mock::EventThread>();
+    auto sfEventThread = std::make_unique<mock::EventThread>();
+
+    EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
+    EXPECT_CALL(*eventThread, createEventConnection(_, _))
+            .WillOnce(Return(new EventThreadConnection(eventThread.get(), ResyncCallback(),
+                                                       ISurfaceComposer::eConfigChangedSuppress)));
+
+    EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
+    EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
+            .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
+                                                       ISurfaceComposer::eConfigChangedSuppress)));
+
+    auto primaryDispSync = std::make_unique<mock::DispSync>();
+
+    EXPECT_CALL(*primaryDispSync, computeNextRefresh(0)).WillRepeatedly(Return(0));
+    EXPECT_CALL(*primaryDispSync, getPeriod())
+            .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
+    EXPECT_CALL(*primaryDispSync, expectedPresentTime()).WillRepeatedly(Return(0));
+    mFlinger.setupScheduler(std::move(primaryDispSync),
+                            std::make_unique<mock::EventControlThread>(), std::move(eventThread),
+                            std::move(sfEventThread));
+}
+
+void SetFrameRateTest::setupComposer(uint32_t virtualDisplayCount) {
+    mComposer = new Hwc2::mock::Composer();
+    EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount));
+    mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
+
+    Mock::VerifyAndClear(mComposer);
+}
+
+namespace {
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+TEST_P(SetFrameRateTest, SetAndGet) {
+    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+    const auto& layerFactory = GetParam();
+
+    auto layer = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    layer->setFrameRate(FRAME_RATE_VOTE1);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_VOTE1, layer->getFrameRateForLayerTree());
+}
+
+TEST_P(SetFrameRateTest, SetAndGetParent) {
+    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+    const auto& layerFactory = GetParam();
+
+    auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+
+    addChild(parent, child1);
+    addChild(child1, child2);
+
+    child2->setFrameRate(FRAME_RATE_VOTE1);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+
+    child2->setFrameRate(FRAME_RATE_NO_VOTE);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+}
+
+TEST_P(SetFrameRateTest, SetAndGetParentAllVote) {
+    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+    const auto& layerFactory = GetParam();
+
+    auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+
+    addChild(parent, child1);
+    addChild(child1, child2);
+
+    child2->setFrameRate(FRAME_RATE_VOTE1);
+    child1->setFrameRate(FRAME_RATE_VOTE2);
+    parent->setFrameRate(FRAME_RATE_VOTE3);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+
+    child2->setFrameRate(FRAME_RATE_NO_VOTE);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child2->getFrameRateForLayerTree());
+
+    child1->setFrameRate(FRAME_RATE_NO_VOTE);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child2->getFrameRateForLayerTree());
+
+    parent->setFrameRate(FRAME_RATE_NO_VOTE);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+}
+
+TEST_P(SetFrameRateTest, SetAndGetChild) {
+    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+    const auto& layerFactory = GetParam();
+
+    auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+
+    addChild(parent, child1);
+    addChild(child1, child2);
+
+    parent->setFrameRate(FRAME_RATE_VOTE1);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child2->getFrameRateForLayerTree());
+
+    parent->setFrameRate(FRAME_RATE_NO_VOTE);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+}
+
+TEST_P(SetFrameRateTest, SetAndGetChildAllVote) {
+    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+    const auto& layerFactory = GetParam();
+
+    auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+
+    addChild(parent, child1);
+    addChild(child1, child2);
+
+    child2->setFrameRate(FRAME_RATE_VOTE1);
+    child1->setFrameRate(FRAME_RATE_VOTE2);
+    parent->setFrameRate(FRAME_RATE_VOTE3);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+
+    parent->setFrameRate(FRAME_RATE_NO_VOTE);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+
+    child1->setFrameRate(FRAME_RATE_NO_VOTE);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+
+    child2->setFrameRate(FRAME_RATE_NO_VOTE);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+}
+
+TEST_P(SetFrameRateTest, SetAndGetChildAddAfterVote) {
+    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+    const auto& layerFactory = GetParam();
+
+    auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+
+    addChild(parent, child1);
+
+    parent->setFrameRate(FRAME_RATE_VOTE1);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+
+    addChild(child1, child2);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child2->getFrameRateForLayerTree());
+
+    parent->setFrameRate(FRAME_RATE_NO_VOTE);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+}
+
+TEST_P(SetFrameRateTest, SetAndGetChildRemoveAfterVote) {
+    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+    const auto& layerFactory = GetParam();
+
+    auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+
+    addChild(parent, child1);
+    addChild(child1, child2);
+
+    parent->setFrameRate(FRAME_RATE_VOTE1);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child2->getFrameRateForLayerTree());
+
+    removeChild(child1, child2);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+
+    parent->setFrameRate(FRAME_RATE_NO_VOTE);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+}
+
+TEST_P(SetFrameRateTest, SetAndGetParentNotInTree) {
+    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+    const auto& layerFactory = GetParam();
+
+    auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child2_1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+
+    addChild(parent, child1);
+    addChild(child1, child2);
+    addChild(child1, child2_1);
+
+    child2->setFrameRate(FRAME_RATE_VOTE1);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2_1->getFrameRateForLayerTree());
+
+    child2->setFrameRate(FRAME_RATE_NO_VOTE);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2_1->getFrameRateForLayerTree());
+}
+
+TEST_P(SetFrameRateTest, SetAndGetRearentChildren) {
+    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+    const auto& layerFactory = GetParam();
+
+    auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto parent2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+
+    addChild(parent, child1);
+    addChild(child1, child2);
+
+    child2->setFrameRate(FRAME_RATE_VOTE1);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent2->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+
+    reparentChildren(parent, parent2);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, parent2->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+
+    child2->setFrameRate(FRAME_RATE_NO_VOTE);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent2->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+}
+
+INSTANTIATE_TEST_SUITE_P(PerLayerType, SetFrameRateTest,
+                         testing::Values(std::make_shared<BufferQueueLayerFactory>(),
+                                         std::make_shared<BufferStateLayerFactory>(),
+                                         std::make_shared<EffectLayerFactory>()),
+                         PrintToStringParamName);
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 52da34b..41b5d49 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -29,7 +29,7 @@
 class TestableScheduler : public Scheduler, private ISchedulerCallback {
 public:
     TestableScheduler(const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2)
-          : Scheduler([](bool) {}, configs, *this, useContentDetectionV2) {
+          : Scheduler([](bool) {}, configs, *this, useContentDetectionV2, true) {
         if (mUseContentDetectionV2) {
             mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>();
         } else {
@@ -41,7 +41,7 @@
                       std::unique_ptr<EventControlThread> eventControlThread,
                       const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2)
           : Scheduler(std::move(primaryDispSync), std::move(eventControlThread), configs, *this,
-                      useContentDetectionV2) {
+                      useContentDetectionV2, true) {
         if (mUseContentDetectionV2) {
             mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>();
         } else {
@@ -92,6 +92,7 @@
 private:
     void changeRefreshRate(const RefreshRate&, ConfigEvent) override {}
     void repaintEverythingForHWC() override {}
+    void kernelTimerChanged(bool /*expired*/) override {}
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 3a4f349..414085c 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -20,6 +20,7 @@
 #include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/OutputLayer.h>
 #include <compositionengine/impl/CompositionEngine.h>
+#include <compositionengine/impl/Display.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
 #include <compositionengine/mock/DisplaySurface.h>
 
@@ -97,8 +98,8 @@
         return new StartPropertySetThread(timestampPropertyValue);
     }
 
-    sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&& creationArgs) override {
-        return new DisplayDevice(std::move(creationArgs));
+    sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs& creationArgs) override {
+        return new DisplayDevice(creationArgs);
     }
 
     sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format,
@@ -277,13 +278,14 @@
 
     auto resetDisplayState() { return mFlinger->resetDisplayState(); }
 
-    auto setupNewDisplayDeviceInternal(const wp<IBinder>& displayToken,
-                                       const std::optional<DisplayId>& displayId,
-                                       const DisplayDeviceState& state,
-                                       const sp<compositionengine::DisplaySurface>& dispSurface,
-                                       const sp<IGraphicBufferProducer>& producer) {
-        return mFlinger->setupNewDisplayDeviceInternal(displayToken, displayId, state, dispSurface,
-                                                       producer);
+    auto setupNewDisplayDeviceInternal(
+            const wp<IBinder>& displayToken,
+            std::shared_ptr<compositionengine::Display> compositionDisplay,
+            const DisplayDeviceState& state,
+            const sp<compositionengine::DisplaySurface>& dispSurface,
+            const sp<IGraphicBufferProducer>& producer) {
+        return mFlinger->setupNewDisplayDeviceInternal(displayToken, compositionDisplay, state,
+                                                       dispSurface, producer);
     }
 
     auto handleTransactionLocked(uint32_t transactionFlags) {
@@ -359,6 +361,7 @@
     auto& getHwComposer() const {
         return static_cast<impl::HWComposer&>(mFlinger->getHwComposer());
     }
+    auto& getCompositionEngine() const { return mFlinger->getCompositionEngine(); }
 
     const auto& getCompositorTiming() const { return mFlinger->getBE().mCompositorTiming; }
 
@@ -391,6 +394,7 @@
     auto& mutableHwcPhysicalDisplayIdMap() { return getHwComposer().mPhysicalDisplayIdMap; }
     auto& mutableInternalHwcDisplayId() { return getHwComposer().mInternalHwcDisplayId; }
     auto& mutableExternalHwcDisplayId() { return getHwComposer().mExternalHwcDisplayId; }
+    auto& mutableUseFrameRateApi() { return mFlinger->useFrameRateApi; }
 
     auto fromHandle(const sp<IBinder>& handle) {
         Mutex::Autolock _l(mFlinger->mStateLock);
@@ -543,10 +547,11 @@
     class FakeDisplayDeviceInjector {
     public:
         FakeDisplayDeviceInjector(TestableSurfaceFlinger& flinger,
-                                  std::optional<DisplayId> displayId,
+                                  std::shared_ptr<compositionengine::Display> compositionDisplay,
                                   std::optional<DisplayConnectionType> connectionType,
                                   bool isPrimary)
-              : mFlinger(flinger), mCreationArgs(flinger.mFlinger.get(), mDisplayToken, displayId) {
+              : mFlinger(flinger),
+                mCreationArgs(flinger.mFlinger.get(), mDisplayToken, compositionDisplay) {
             mCreationArgs.connectionType = connectionType;
             mCreationArgs.isPrimary = isPrimary;
         }
@@ -609,16 +614,17 @@
         }
 
         sp<DisplayDevice> inject() {
+            const auto displayId = mCreationArgs.compositionDisplay->getDisplayId();
+
             DisplayDeviceState state;
             if (const auto type = mCreationArgs.connectionType) {
-                const auto id = mCreationArgs.displayId;
-                LOG_ALWAYS_FATAL_IF(!id);
-                state.physical = {*id, *type};
+                LOG_ALWAYS_FATAL_IF(!displayId);
+                state.physical = {*displayId, *type};
             }
 
             state.isSecure = mCreationArgs.isSecure;
 
-            sp<DisplayDevice> device = new DisplayDevice(std::move(mCreationArgs));
+            sp<DisplayDevice> device = new DisplayDevice(mCreationArgs);
             mFlinger.mutableDisplays().emplace(mDisplayToken, device);
             mFlinger.mutableCurrentState().displays.add(mDisplayToken, state);
             mFlinger.mutableDrawingState().displays.add(mDisplayToken, state);
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index a7a4d48..4f65aee 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -152,9 +152,9 @@
         struct AStatsEvent* addStatsEventToPullData(AStatsEventList*) override {
             return mEvent;
         }
-        void registerStatsPullAtomCallback(int32_t atom_tag,
-                                           AStatsManager_PullAtomCallback callback,
-                                           AStatsManager_PullAtomMetadata*, void* cookie) override {
+        void setStatsPullAtomCallback(int32_t atom_tag, AStatsManager_PullAtomMetadata*,
+                                      AStatsManager_PullAtomCallback callback,
+                                      void* cookie) override {
             mAtomTags.push_back(atom_tag);
             mCallback = callback;
             mCookie = cookie;
@@ -164,7 +164,7 @@
             return (*mCallback)(atom_tag, nullptr, cookie);
         }
 
-        MOCK_METHOD1(unregisterStatsPullAtomCallback, void(int32_t));
+        MOCK_METHOD1(clearStatsPullAtomCallback, void(int32_t));
         MOCK_METHOD2(statsEventSetAtomId, void(AStatsEvent*, uint32_t));
         MOCK_METHOD2(statsEventWriteInt32, void(AStatsEvent*, int32_t));
         MOCK_METHOD2(statsEventWriteInt64, void(AStatsEvent*, int64_t));
@@ -261,18 +261,18 @@
     ASSERT_FALSE(mTimeStats->isEnabled());
 }
 
-TEST_F(TimeStatsTest, registersCallbacksAfterBoot) {
+TEST_F(TimeStatsTest, setsCallbacksAfterBoot) {
     mTimeStats->onBootFinished();
     EXPECT_THAT(mDelegate->mAtomTags,
                 UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
                                      android::util::SURFACEFLINGER_STATS_LAYER_INFO));
 }
 
-TEST_F(TimeStatsTest, unregistersCallbacksOnDestruction) {
+TEST_F(TimeStatsTest, clearsCallbacksOnDestruction) {
     EXPECT_CALL(*mDelegate,
-                unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO));
+                clearStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO));
     EXPECT_CALL(*mDelegate,
-                unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+                clearStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO));
     mTimeStats.reset();
 }
 
@@ -1039,8 +1039,7 @@
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 4000000);
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 5000000);
 
-    // Now make sure that TimeStats flushes global stats to register the
-    // callback.
+    // Now make sure that TimeStats flushes global stats to set the callback.
     mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL);
     mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
     mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index caac61d..be49ef3 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -51,6 +51,7 @@
 
     void setPeriod(nsecs_t) final {}
     void resetModel() final {}
+    void dump(std::string&) const final {}
 
 private:
     nsecs_t const mPeriod;
@@ -85,6 +86,7 @@
 
     void setPeriod(nsecs_t) final {}
     void resetModel() final {}
+    void dump(std::string&) const final {}
 
 private:
     std::mutex mutable mMutex;
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index 3ab38e4..3543361 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -47,6 +47,7 @@
     MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
     MOCK_METHOD1(setPeriod, void(nsecs_t));
     MOCK_METHOD0(resetModel, void());
+    MOCK_CONST_METHOD1(dump, void(std::string&));
 
     nsecs_t nextVSyncTime(nsecs_t timePoint) const {
         if (timePoint % mPeriod == 0) {
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index f834af8..bf2a889 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -204,7 +204,7 @@
     };
     auto idealPeriod = 2000000;
     auto expectedPeriod = 1999892;
-    auto expectedIntercept = 175409;
+    auto expectedIntercept = 86342;
 
     tracker.setPeriod(idealPeriod);
     for (auto const& timestamp : simulatedVsyncs) {
@@ -335,8 +335,8 @@
             158929706370359,
     };
     auto const idealPeriod = 11111111;
-    auto const expectedPeriod = 11113500;
-    auto const expectedIntercept = -395335;
+    auto const expectedPeriod = 11113919;
+    auto const expectedIntercept = -1195945;
 
     tracker.setPeriod(idealPeriod);
     for (auto const& timestamp : simulatedVsyncs) {
@@ -355,6 +355,32 @@
     EXPECT_THAT(prediction, Ge(timePoint));
 }
 
+// See b/151146131
+TEST_F(VSyncPredictorTest, hasEnoughPrecision) {
+    VSyncPredictor tracker{mPeriod, 20, kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+    std::vector<nsecs_t> const simulatedVsyncs{840873348817, 840890049444, 840906762675,
+                                               840923581635, 840940161584, 840956868096,
+                                               840973702473, 840990256277, 841007116851,
+                                               841023722530, 841040452167, 841057073002,
+                                               841073800920, 841090474360, 841107278632,
+                                               841123898634, 841140750875, 841157287127,
+                                               841591357014, 840856664232
+
+    };
+    auto const idealPeriod = 16666666;
+    auto const expectedPeriod = 16698426;
+    auto const expectedIntercept = 58055;
+
+    tracker.setPeriod(idealPeriod);
+    for (auto const& timestamp : simulatedVsyncs) {
+        tracker.addVsyncTimestamp(timestamp);
+    }
+
+    auto [slope, intercept] = tracker.getVSyncPredictionModel();
+    EXPECT_THAT(slope, IsCloseTo(expectedPeriod, mMaxRoundingError));
+    EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
+}
+
 TEST_F(VSyncPredictorTest, resetsWhenInstructed) {
     auto const idealPeriod = 10000;
     auto const realPeriod = 10500;
@@ -390,6 +416,22 @@
     }
 }
 
+constexpr nsecs_t operator""_years(unsigned long long years) noexcept {
+    using namespace std::chrono_literals;
+    return years * 365 * 24 * 3600 *
+            std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
+}
+TEST_F(VSyncPredictorTest, aPhoneThatHasBeenAroundAWhileCanStillComputePeriod) {
+    constexpr nsecs_t timeBase = 100_years;
+
+    for (auto i = 0; i < kHistorySize; i++) {
+        tracker.addVsyncTimestamp(timeBase + i * mPeriod);
+    }
+    auto [slope, intercept] = tracker.getVSyncPredictionModel();
+    EXPECT_THAT(slope, IsCloseTo(mPeriod, mMaxRoundingError));
+    EXPECT_THAT(intercept, Eq(0));
+}
+
 } // namespace android::scheduler
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index ac95938..4f150ef 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -41,6 +41,7 @@
     MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
     MOCK_METHOD1(setPeriod, void(nsecs_t));
     MOCK_METHOD0(resetModel, void());
+    MOCK_CONST_METHOD1(dump, void(std::string&));
 };
 
 class VSyncTrackerWrapper : public VSyncTracker {
@@ -56,6 +57,7 @@
     nsecs_t currentPeriod() const final { return mTracker->currentPeriod(); }
     void setPeriod(nsecs_t period) final { mTracker->setPeriod(period); }
     void resetModel() final { mTracker->resetModel(); }
+    void dump(std::string& result) const final { mTracker->dump(result); }
 
 private:
     std::shared_ptr<VSyncTracker> const mTracker;
@@ -83,6 +85,7 @@
     MOCK_METHOD1(unregisterCallback, void(CallbackToken));
     MOCK_METHOD3(schedule, ScheduleResult(CallbackToken, nsecs_t, nsecs_t));
     MOCK_METHOD1(cancel, CancelResult(CallbackToken token));
+    MOCK_CONST_METHOD1(dump, void(std::string&));
 };
 
 class VSyncDispatchWrapper : public VSyncDispatch {
@@ -102,6 +105,8 @@
 
     CancelResult cancel(CallbackToken token) final { return mDispatch->cancel(token); }
 
+    void dump(std::string& result) const final { return mDispatch->dump(result); }
+
 private:
     std::shared_ptr<VSyncDispatch> const mDispatch;
 };
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
index fe57c98..e22d0cf 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
@@ -29,6 +29,7 @@
     PowerAdvisor();
     ~PowerAdvisor() override;
 
+    MOCK_METHOD0(onBootFinished, void());
     MOCK_METHOD2(setExpensiveRenderingExpected, void(DisplayId displayId, bool expected));
     MOCK_METHOD0(notifyDisplayUpdateImminent, void());
 };
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index e2f6abd..119f580 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -31,7 +31,7 @@
     MOCK_METHOD0(getFrameSelectionPriority, int32_t());
     MOCK_CONST_METHOD0(isVisible, bool());
     MOCK_METHOD0(createClone, sp<Layer>());
-    MOCK_CONST_METHOD0(getFrameRate, FrameRate());
+    MOCK_CONST_METHOD0(getFrameRateForLayerTree, FrameRate());
 };
 
 } // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
index 458b2f3..5beee1c 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
@@ -39,7 +39,7 @@
                       const Vector<DisplayState>&, uint32_t));
     MOCK_METHOD1(saveSurfaceCreation, void(const sp<const Layer>&));
     MOCK_METHOD1(saveSurfaceDeletion, void(const sp<const Layer>&));
-    MOCK_METHOD4(saveBufferUpdate, void(const sp<const Layer>&, uint32_t, uint32_t, uint64_t));
+    MOCK_METHOD4(saveBufferUpdate, void(int32_t, uint32_t, uint32_t, uint64_t));
     MOCK_METHOD1(saveDisplayCreation, void(const DisplayDeviceState&));
     MOCK_METHOD1(saveDisplayDeletion, void(int32_t));
     MOCK_METHOD2(savePowerModeUpdate, void(int32_t, int32_t));
diff --git a/vulkan/TEST_MAPPING b/vulkan/TEST_MAPPING
new file mode 100644
index 0000000..d391dce
--- /dev/null
+++ b/vulkan/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsGpuToolsHostTestCases"
+    }
+  ]
+}
diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp
index aae72db..5b9affd 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -29,6 +29,8 @@
 #include <algorithm>
 #include <mutex>
 #include <new>
+#include <string>
+#include <unordered_set>
 #include <utility>
 
 #include <android-base/strings.h>
@@ -1194,6 +1196,23 @@
     return initialized;
 }
 
+template <typename Functor>
+void ForEachLayerFromSettings(Functor functor) {
+    const std::string layersSetting =
+        android::GraphicsEnv::getInstance().getDebugLayers();
+    if (!layersSetting.empty()) {
+        std::vector<std::string> layers =
+            android::base::Split(layersSetting, ":");
+        for (uint32_t i = 0; i < layers.size(); i++) {
+            const Layer* layer = FindLayer(layers[i].c_str());
+            if (!layer) {
+                continue;
+            }
+            functor(layer);
+        }
+    }
+}
+
 }  // anonymous namespace
 
 VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo,
@@ -1280,9 +1299,56 @@
         return *pPropertyCount < count ? VK_INCOMPLETE : VK_SUCCESS;
     }
 
-    // TODO(b/143293104): expose extensions from implicitly enabled layers
-    return vulkan::driver::EnumerateInstanceExtensionProperties(
-        nullptr, pPropertyCount, pProperties);
+    // If the pLayerName is nullptr, we must advertise all instance extensions
+    // from all implicitly enabled layers and the driver implementation. If
+    // there are duplicates among layers and the driver implementation, always
+    // only preserve the top layer closest to the application regardless of the
+    // spec version.
+    std::vector<VkExtensionProperties> properties;
+    std::unordered_set<std::string> extensionNames;
+
+    // Expose extensions from implicitly enabled layers.
+    ForEachLayerFromSettings([&](const Layer* layer) {
+        uint32_t count = 0;
+        const VkExtensionProperties* props =
+            GetLayerInstanceExtensions(*layer, count);
+        if (count > 0) {
+            for (uint32_t i = 0; i < count; ++i) {
+                if (extensionNames.emplace(props[i].extensionName).second) {
+                    properties.push_back(props[i]);
+                }
+            }
+        }
+    });
+
+    // TODO(b/143293104): Parse debug.vulkan.layers properties
+
+    // Expose extensions from driver implementation.
+    {
+        uint32_t count = 0;
+        VkResult result = vulkan::driver::EnumerateInstanceExtensionProperties(
+            nullptr, &count, nullptr);
+        if (result == VK_SUCCESS && count > 0) {
+            std::vector<VkExtensionProperties> props(count);
+            result = vulkan::driver::EnumerateInstanceExtensionProperties(
+                nullptr, &count, props.data());
+            for (auto prop : props) {
+                if (extensionNames.emplace(prop.extensionName).second) {
+                    properties.push_back(prop);
+                }
+            }
+        }
+    }
+
+    uint32_t totalCount = properties.size();
+    if (!pProperties || *pPropertyCount > totalCount) {
+        *pPropertyCount = totalCount;
+    }
+    if (pProperties) {
+        std::copy(properties.data(), properties.data() + *pPropertyCount,
+                  pProperties);
+    }
+    return *pPropertyCount < totalCount ? VK_INCOMPLETE : VK_SUCCESS;
 }
 
 VkResult EnumerateDeviceLayerProperties(VkPhysicalDevice physicalDevice,
@@ -1334,10 +1400,57 @@
         return *pPropertyCount < count ? VK_INCOMPLETE : VK_SUCCESS;
     }
 
-    // TODO(b/143293104): expose extensions from implicitly enabled layers
-    const InstanceData& data = GetData(physicalDevice);
-    return data.dispatch.EnumerateDeviceExtensionProperties(
-        physicalDevice, nullptr, pPropertyCount, pProperties);
+    // If the pLayerName is nullptr, we must advertise all device extensions
+    // from all implicitly enabled layers and the driver implementation. If
+    // there are duplicates among layers and the driver implementation, always
+    // only preserve the top layer closest to the application regardless of the
+    // spec version.
+    std::vector<VkExtensionProperties> properties;
+    std::unordered_set<std::string> extensionNames;
+
+    // Expose extensions from implicitly enabled layers.
+    ForEachLayerFromSettings([&](const Layer* layer) {
+        uint32_t count = 0;
+        const VkExtensionProperties* props =
+            GetLayerDeviceExtensions(*layer, count);
+        if (count > 0) {
+            for (uint32_t i = 0; i < count; ++i) {
+                if (extensionNames.emplace(props[i].extensionName).second) {
+                    properties.push_back(props[i]);
+                }
+            }
+        }
+    });
+
+    // TODO(b/143293104): Parse debug.vulkan.layers properties
+
+    // Expose extensions from driver implementation.
+    {
+        const InstanceData& data = GetData(physicalDevice);
+        uint32_t count = 0;
+        VkResult result = data.dispatch.EnumerateDeviceExtensionProperties(
+            physicalDevice, nullptr, &count, nullptr);
+        if (result == VK_SUCCESS && count > 0) {
+            std::vector<VkExtensionProperties> props(count);
+            result = data.dispatch.EnumerateDeviceExtensionProperties(
+                physicalDevice, nullptr, &count, props.data());
+            for (auto prop : props) {
+                if (extensionNames.emplace(prop.extensionName).second) {
+                    properties.push_back(prop);
+                }
+            }
+        }
+    }
+
+    uint32_t totalCount = properties.size();
+    if (!pProperties || *pPropertyCount > totalCount) {
+        *pPropertyCount = totalCount;
+    }
+    if (pProperties) {
+        std::copy(properties.data(), properties.data() + *pPropertyCount,
+                  pProperties);
+    }
+    return *pPropertyCount < totalCount ? VK_INCOMPLETE : VK_SUCCESS;
 }
 
 VkResult EnumerateInstanceVersion(uint32_t* pApiVersion) {
diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc
index 8f714d8..b0b466c 100644
--- a/vulkan/vkjson/vkjson.cc
+++ b/vulkan/vkjson/vkjson.cc
@@ -591,6 +591,13 @@
 }
 
 template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+                    VkJsonExtShaderFloat16Int8Features* features) {
+  return visitor->Visit("shaderFloat16Int8FeaturesKHR",
+                        &features->shader_float16_int8_features_khr);
+}
+
+template <typename Visitor>
 inline bool Iterate(Visitor* visitor, VkMemoryType* type) {
   return
     visitor->Visit("propertyFlags", &type->propertyFlags) &&
@@ -692,6 +699,13 @@
 
 template <typename Visitor>
 inline bool Iterate(Visitor* visitor,
+                    VkPhysicalDeviceShaderFloat16Int8FeaturesKHR* features) {
+  return visitor->Visit("shaderFloat16", &features->shaderFloat16) &&
+         visitor->Visit("shaderInt8", &features->shaderInt8);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
                     VkPhysicalDeviceProtectedMemoryFeatures* features) {
   return visitor->Visit("protectedMemory", &features->protectedMemory);
 }
@@ -824,6 +838,10 @@
         ret &= visitor->Visit("VK_KHR_variable_pointers",
                             &device->ext_variable_pointer_features);
       }
+      if (device->ext_shader_float16_int8_features.reported) {
+        ret &= visitor->Visit("VK_KHR_shader_float16_int8",
+                              &device->ext_shader_float16_int8_features);
+      }
   }
   return ret;
 }
diff --git a/vulkan/vkjson/vkjson.h b/vulkan/vkjson/vkjson.h
index 450fb24..a283b83 100644
--- a/vulkan/vkjson/vkjson.h
+++ b/vulkan/vkjson/vkjson.h
@@ -72,6 +72,16 @@
   VkPhysicalDeviceVariablePointerFeaturesKHR variable_pointer_features_khr;
 };
 
+struct VkJsonExtShaderFloat16Int8Features {
+  VkJsonExtShaderFloat16Int8Features() {
+    reported = false;
+    memset(&shader_float16_int8_features_khr, 0,
+           sizeof(VkPhysicalDeviceShaderFloat16Int8FeaturesKHR));
+  }
+  bool reported;
+  VkPhysicalDeviceShaderFloat16Int8FeaturesKHR shader_float16_int8_features_khr;
+};
+
 struct VkJsonDevice {
   VkJsonDevice() {
     memset(&properties, 0, sizeof(VkPhysicalDeviceProperties));
@@ -101,6 +111,7 @@
   VkPhysicalDeviceFeatures features;
   VkJsonExtDriverProperties ext_driver_properties;
   VkJsonExtVariablePointerFeatures ext_variable_pointer_features;
+  VkJsonExtShaderFloat16Int8Features ext_shader_float16_int8_features;
   VkPhysicalDeviceMemoryProperties memory;
   std::vector<VkQueueFamilyProperties> queues;
   std::vector<VkExtensionProperties> extensions;
diff --git a/vulkan/vkjson/vkjson_instance.cc b/vulkan/vkjson/vkjson_instance.cc
index 05d4dfe..84cfe5e 100644
--- a/vulkan/vkjson/vkjson_instance.cc
+++ b/vulkan/vkjson/vkjson_instance.cc
@@ -136,6 +136,16 @@
       features.pNext =
           &device.ext_variable_pointer_features.variable_pointer_features_khr;
     }
+    if (HasExtension("VK_KHR_shader_float16_int8", device.extensions)) {
+      device.ext_shader_float16_int8_features.reported = true;
+      device.ext_shader_float16_int8_features.shader_float16_int8_features_khr
+          .sType =
+          VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR;
+      device.ext_shader_float16_int8_features.shader_float16_int8_features_khr
+          .pNext = features.pNext;
+      features.pNext = &device.ext_shader_float16_int8_features
+                            .shader_float16_int8_features_khr;
+    }
     vkpGetPhysicalDeviceFeatures2KHR(physical_device, &features);
     device.features = features.features;
   } else {