Merge "[AChoreographer] Resolves a deadlock" 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..466575f 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -120,7 +120,7 @@
     options->Initialize(static_cast<Dumpstate::BugreportMode>(bugreport_mode), bugreport_fd,
                         screenshot_fd);
 
-    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/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..3440116 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();
     }
@@ -2149,7 +2167,7 @@
         ds.base_name_ += "-wifi";
     }
 
-    if (ds.options_->do_fb) {
+    if (ds.options_->do_screenshot) {
         ds.screenshot_path_ = ds.GetPath(ds.CalledByApi() ? "-tmp.png" : ".png");
     }
     ds.tmp_path_ = ds.GetPath(".tmp");
@@ -2229,43 +2247,45 @@
 }
 
 static void SetOptionsFromMode(Dumpstate::BugreportMode mode, Dumpstate::DumpOptions* options) {
+    // 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 = true;
             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 = true;
             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 = true;
             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 +2295,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());
@@ -2313,7 +2334,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 +2564,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 +2609,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 +2657,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 +2713,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 +3657,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..7e27787 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;
@@ -494,6 +495,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..f26e4db 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_;
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 76b9960..0a0c40e 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,7 +226,7 @@
     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);
@@ -234,7 +235,7 @@
 TEST_F(DumpOptionsTest, InitializeFullBugReport) {
     options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_FULL, fd, fd);
     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);
 
@@ -254,7 +255,7 @@
     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
@@ -271,7 +272,7 @@
     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
@@ -284,7 +285,7 @@
 TEST_F(DumpOptionsTest, InitializeWearBugReport) {
     options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WEAR, fd, fd);
     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);
@@ -301,7 +302,7 @@
 TEST_F(DumpOptionsTest, InitializeTelephonyBugReport) {
     options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_TELEPHONY, fd, fd);
     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);
@@ -318,7 +319,7 @@
 TEST_F(DumpOptionsTest, InitializeWifiBugReport) {
     options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WIFI, fd, fd);
     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/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp
index a7ccf64..f426dbb 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+)$");
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/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/libs/gralloc/types/Gralloc4.cpp b/libs/gralloc/types/Gralloc4.cpp
index 603e7e5..53c68b7 100644
--- a/libs/gralloc/types/Gralloc4.cpp
+++ b/libs/gralloc/types/Gralloc4.cpp
@@ -1317,6 +1317,8 @@
             return "G";
         case PlaneLayoutComponentType::B:
             return "B";
+        case PlaneLayoutComponentType::RAW:
+            return "RAW";
         case PlaneLayoutComponentType::A:
             return "A";
     }
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..04c21a9 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -1281,6 +1281,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/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/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/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/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/renderengine/Android.bp b/libs/renderengine/Android.bp
index 3d77059..1075f16 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -59,8 +59,6 @@
         "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..416ebfe 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);
 
@@ -430,17 +428,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();
 }
 
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index ebf78fe..4cd0b3d 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -261,8 +261,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/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..e704907 100644
--- a/libs/renderengine/gl/filters/BlurFilter.cpp
+++ b/libs/renderengine/gl/filters/BlurFilter.cpp
@@ -31,13 +31,24 @@
 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");
 }
 
 status_t BlurFilter::setAsDrawTarget(const DisplaySettings& display, uint32_t radius) {
@@ -51,14 +62,14 @@
 
         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) {
+    if (mPingFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
         ALOGE("Invalid blur buffer");
-        return mBlurredFbo.getStatus();
+        return mPingFbo.getStatus();
     }
     if (mCompositionFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
         ALOGE("Invalid composition buffer");
@@ -96,6 +107,61 @@
     mEngine.checkErrors("Drawing blur mesh");
 }
 
+status_t BlurFilter::prepare() {
+    ATRACE_NAME("BlurFilter::prepare");
+
+    if (mPongFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
+        ALOGE("Invalid FBO");
+        return mPongFbo.getStatus();
+    }
+    if (!mBlurProgram.isValid()) {
+        ALOGE("Invalid shader");
+        return GL_INVALID_OPERATION;
+    }
+
+    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;
+    glActiveTexture(GL_TEXTURE0);
+    glUniform1i(mBTextureLoc, 0);
+    for (auto i = 0; i < passes; i++) {
+        ATRACE_NAME("BlurFilter::renderPass");
+        draw->bind();
+
+        glViewport(0, 0, draw->getBufferWidth(), draw->getBufferHeight());
+        glBindTexture(GL_TEXTURE_2D, read->getTextureName());
+        glUniform2f(mBOffsetLoc, stepX * i, stepY * i);
+        mEngine.checkErrors("Setting uniforms");
+
+        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) {
     ATRACE_NAME("BlurFilter::render");
 
@@ -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,7 +184,7 @@
     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());
@@ -145,6 +212,28 @@
     )SHADER";
 }
 
+string BlurFilter::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";
+}
+
 string BlurFilter::getMixFragShader() const {
     string shader = R"SHADER(#version 310 es
         precision mediump float;
@@ -165,6 +254,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..eb6120b 100644
--- a/libs/renderengine/gl/filters/BlurFilter.h
+++ b/libs/renderengine/gl/filters/BlurFilter.h
@@ -27,10 +27,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,28 +47,29 @@
 
     // 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;
 
     GenericProgram mMixProgram;
@@ -70,6 +78,12 @@
     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..6fd4b80 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -364,7 +364,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/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/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..e5e83d7 100644
--- a/services/inputflinger/InputClassifier.cpp
+++ b/services/inputflinger/InputClassifier.cpp
@@ -250,7 +250,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 +321,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);
@@ -455,6 +461,7 @@
 void InputClassifier::dump(std::string& dump) {
     std::scoped_lock lock(mLock);
     dump += "Input Classifier State:\n";
+    dump += StringPrintf(INDENT1 "Deep press: %s\n", deepPressEnabled() ? "enabled" : "disabled");
 
     dump += INDENT1 "Motion Classifier:\n";
     if (mMotionClassifier) {
diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h
index 47e20db..9692352 100644
--- a/services/inputflinger/InputClassifier.h
+++ b/services/inputflinger/InputClassifier.h
@@ -212,6 +212,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.
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/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/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index f5a99ca..18f7f44 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -564,8 +564,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/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/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/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/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 550ec61..06e0cbb 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -124,24 +124,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 +158,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 +219,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 +243,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/Layer.cpp b/services/surfaceflinger/Layer.cpp
index d7647d7..64cfb3d 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -677,6 +677,7 @@
     layerSettings.source.buffer.buffer = nullptr;
     layerSettings.source.solidColor = half3(0.0, 0.0, 0.0);
     layerSettings.disableBlending = true;
+    layerSettings.bufferId = 0;
     layerSettings.frameNumber = 0;
 
     // If layer is blacked out, force alpha to 1 so that we draw a black color layer.
@@ -718,9 +719,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 {
@@ -2051,13 +2057,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;
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 5d2144a..20d7232 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -506,7 +506,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
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/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/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index b876ccd..15b158d 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -107,6 +107,12 @@
         return *mAvailableRefreshRates.back();
     }
 
+    // If there are not layers, there is not content detection, so return the current
+    // refresh rate.
+    if (layers.empty()) {
+        return getCurrentRefreshRateByPolicyLocked();
+    }
+
     int noVoteLayers = 0;
     int minVoteLayers = 0;
     int maxVoteLayers = 0;
@@ -229,13 +235,13 @@
             ? 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;
+    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);
@@ -272,6 +278,10 @@
 
 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;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 0b5c73c..c8aec86 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -199,6 +199,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..5444239 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);
@@ -512,6 +528,8 @@
         // need to update the DispSync model anyway.
         disableHardwareVsync(false /* makeUnavailable */);
     }
+
+    mSchedulerCallback.kernelTimerChanged(state == TimerState::Expired);
 }
 
 void Scheduler::idleTimerCallback(TimerState state) {
@@ -537,8 +555,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>
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/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 949ba4c..8987dcd 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -19,8 +19,10 @@
 #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"
@@ -32,12 +34,35 @@
     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;
 
@@ -287,7 +312,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());
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index aa8a38d..7d8a8e3 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 {
@@ -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/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index ce0a8a1..163a1c7 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;
@@ -334,6 +327,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);
 
@@ -371,7 +367,7 @@
     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;
 
@@ -857,6 +853,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
@@ -909,15 +906,24 @@
     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 +936,6 @@
         mPhaseConfiguration->setRefreshRateFps(refreshRate.fps);
         mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
     }
-    mDesiredActiveConfigChanged = true;
 
     if (mRefreshRateOverlay) {
         mRefreshRateOverlay->changeRefreshRate(refreshRate);
@@ -1069,7 +1074,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;
     }
 
@@ -1812,6 +1819,10 @@
             if (frameMissed) {
                 mFrameMissedCount++;
                 mTimeStats->incrementMissedFrames();
+                if (mMissedFrameJankCount == 0) {
+                    mMissedFrameJankStart = systemTime();
+                }
+                mMissedFrameJankCount++;
             }
 
             if (hwcFrameMissed) {
@@ -1830,6 +1841,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 +2370,17 @@
 }
 
 sp<DisplayDevice> SurfaceFlinger::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<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.hasWideColorGamut = false;
     creationArgs.supportedPerFrameMetadata = 0;
-    creationArgs.powerAdvisor = displayId ? &mPowerAdvisor : nullptr;
 
     if (const auto& physical = state.physical) {
         creationArgs.connectionType = physical->type;
@@ -2379,7 +2425,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();
@@ -2485,53 +2531,68 @@
             if (draw.indexOfKey(curr.keyAt(i)) < 0) {
                 const DisplayDeviceState& state(curr[i]);
 
+                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).
+                    continue;
+                }
+
+                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);
+                auto compositionDisplay = getCompositionEngine().createDisplay(builder.build());
+
                 sp<compositionengine::DisplaySurface> dispSurface;
                 sp<IGraphicBufferProducer> producer;
                 sp<IGraphicBufferProducer> bqProducer;
                 sp<IGraphicBufferConsumer> bqConsumer;
                 getFactory().createBufferQueue(&bqProducer, &bqConsumer, false);
 
-                std::optional<DisplayId> displayId;
+                std::optional<DisplayId> displayId = compositionDisplay->getId();
+
                 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);
+                    sp<VirtualDisplaySurface> vds =
+                            new VirtualDisplaySurface(getHwComposer(), displayId, state.surface,
+                                                      bqProducer, bqConsumer, state.displayName);
 
-                            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;
-                    }
+                    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;
+                    LOG_ALWAYS_FATAL_IF(!displayId);
                     dispSurface = new FramebufferSurface(getHwComposer(), *displayId, bqConsumer);
                     producer = bqProducer;
                 }
@@ -2539,7 +2600,8 @@
                 const wp<IBinder>& displayToken = curr.keyAt(i);
                 if (dispSurface != nullptr) {
                     mDisplays.emplace(displayToken,
-                                      setupNewDisplayDeviceInternal(displayToken, displayId, state,
+                                      setupNewDisplayDeviceInternal(displayToken,
+                                                                    compositionDisplay, state,
                                                                     dispSurface, producer));
                     if (!state.isVirtual()) {
                         LOG_ALWAYS_FATAL_IF(!displayId);
@@ -3006,9 +3068,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;
         }
 
@@ -4398,13 +4460,20 @@
 }
 
 LayersProto SurfaceFlinger::dumpDrawingStateProto(uint32_t traceFlags) const {
+    Mutex::Autolock _l(mStateLock);
+    const auto device = getDefaultDisplayDeviceLocked();
     LayersProto layersProto;
     for (const sp<Layer>& layer : mDrawingState.layersSortedByZ) {
-        layer->writeToProto(layersProto, traceFlags);
+        layer->writeToProto(layersProto, traceFlags, device);
     }
+
     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,7 +4488,8 @@
         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);
     }
 }
@@ -5106,6 +5176,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]() {
+                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:
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index c79621b..e7f9930 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -351,7 +351,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 +523,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,7 +811,8 @@
      * 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<IGraphicBufferProducer>& producer);
@@ -918,6 +920,8 @@
     LayersProto dumpDrawingStateProto(uint32_t traceFlags = SurfaceTracing::TRACE_ALL) 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 +975,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 +986,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
@@ -1225,6 +1230,11 @@
     // 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;
 };
 
 } // 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/SurfaceTracing.cpp b/services/surfaceflinger/SurfaceTracing.cpp
index eb5c7de..c5556ec 100644
--- a/services/surfaceflinger/SurfaceTracing.cpp
+++ b/services/surfaceflinger/SurfaceTracing.cpp
@@ -170,6 +170,12 @@
     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..3c24881 100644
--- a/services/surfaceflinger/SurfaceTracing.h
+++ b/services/surfaceflinger/SurfaceTracing.h
@@ -56,6 +56,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);
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/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/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/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
index 959c256..37da76b 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);
@@ -451,5 +452,61 @@
     EXPECT_EQ(0, frequentLayerCount(time));
 }
 
+TEST_F(LayerHistoryTestV2, inactiveLayers) {
+    auto layer = createLayer();
+
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRate()).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/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 7e62513..e7e7f66 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -247,6 +247,31 @@
               refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
 }
 
+TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_noLayers) {
+    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));
+
+    // Current refresh rate can always be changed.
+    refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_60);
+    EXPECT_EQ(expected60Config,
+              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/
+                                                             false));
+}
+
 TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_60_90) {
     std::vector<RefreshRateConfigs::InputConfig> configs{
             {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
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..84e55ae 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; }
 
@@ -543,10 +546,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 +613,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/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 {