Merge "Adding audiocontrol@2.0 to hal interfaces to dump" into rvc-dev
diff --git a/cmds/cmd/cmd.cpp b/cmds/cmd/cmd.cpp
index 8dad475..be2c702 100644
--- a/cmds/cmd/cmd.cpp
+++ b/cmds/cmd/cmd.cpp
@@ -185,7 +185,7 @@
     int argc = argv.size();
 
     if (argc == 0) {
-        errorLog << "cmd: No service specified; use -l to list all services" << endl;
+        errorLog << "cmd: No service specified; use -l to list all running services. Use -w to start and wait for a service." << endl;
         return 20;
     }
 
@@ -203,14 +203,22 @@
         return 0;
     }
 
-    const auto cmd = argv[0];
+    bool waitForService = ((argc > 1) && (argv[0] == "-w"));
+    int serviceIdx = (waitForService) ? 1 : 0;
+    const auto cmd = argv[serviceIdx];
 
     Vector<String16> args;
     String16 serviceName = String16(cmd.data(), cmd.size());
-    for (int i = 1; i < argc; i++) {
+    for (int i = serviceIdx + 1; i < argc; i++) {
         args.add(String16(argv[i].data(), argv[i].size()));
     }
-    sp<IBinder> service = sm->checkService(serviceName);
+    sp<IBinder> service;
+    if(waitForService) {
+        service = sm->waitForService(serviceName);
+    } else {
+        service = sm->checkService(serviceName);
+    }
+
     if (service == nullptr) {
         if (runMode == RunMode::kStandalone) {
             ALOGW("Can't find service %.*s", static_cast<int>(cmd.size()), cmd.data());
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
index a0b9cbb..1824943 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -148,14 +148,13 @@
 }
 
 binder::Status DumpstateService::cancelBugreport() {
-    // This is a no-op since the cancellation is done from java side via setting sys properties.
-    // See BugreportManagerServiceImpl.
-    // TODO(b/111441001): maybe make native and java sides use different binder interface
-    // to avoid these annoyances.
+    std::lock_guard<std::mutex> lock(lock_);
+    ds_->Cancel();
     return binder::Status::ok();
 }
 
 status_t DumpstateService::dump(int fd, const Vector<String16>&) {
+    std::lock_guard<std::mutex> lock(lock_);
     if (ds_ == nullptr) {
         dprintf(fd, "Bugreport not in progress yet");
         return NO_ERROR;
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index ec2b922..772b9fe 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -239,6 +239,9 @@
 }
 
 static bool UnlinkAndLogOnError(const std::string& file) {
+    if (file.empty()) {
+        return false;
+    }
     if (unlink(file.c_str())) {
         MYLOGE("Failed to unlink file (%s): %s\n", file.c_str(), strerror(errno));
         return false;
@@ -246,7 +249,6 @@
     return true;
 }
 
-
 int64_t GetModuleMetadataVersion() {
     auto binder = defaultServiceManager()->getService(android::String16("package_native"));
     if (binder == nullptr) {
@@ -2171,7 +2173,7 @@
     }
 
     if (ds.options_->do_screenshot) {
-        ds.screenshot_path_ = ds.GetPath(ds.CalledByApi() ? "-tmp.png" : ".png");
+        ds.screenshot_path_ = ds.GetPath(ds.CalledByApi() ? "-png.tmp" : ".png");
     }
     ds.tmp_path_ = ds.GetPath(".tmp");
     ds.log_path_ = ds.GetPath("-dumpstate_log-" + std::to_string(ds.pid_) + ".txt");
@@ -2190,7 +2192,7 @@
         ds.tmp_path_.c_str(), ds.screenshot_path_.c_str());
 
     if (ds.options_->do_zip_file) {
-        ds.path_ = ds.GetPath(ds.CalledByApi() ? "-tmp.zip" : ".zip");
+        ds.path_ = ds.GetPath(ds.CalledByApi() ? "-zip.tmp" : ".zip");
         MYLOGD("Creating initial .zip file (%s)\n", ds.path_.c_str());
         create_parent_dirs(ds.path_.c_str());
         ds.zip_file.reset(fopen(ds.path_.c_str(), "wb"));
@@ -2419,6 +2421,17 @@
     return status;
 }
 
+void Dumpstate::Cancel() {
+    CleanupTmpFiles();
+    android::os::UnlinkAndLogOnError(log_path_);
+    for (int i = 0; i < NUM_OF_DUMPS; i++) {
+        android::os::UnlinkAndLogOnError(ds.bugreport_internal_dir_ + "/" +
+                                         kDumpstateBoardFiles[i]);
+    }
+    tombstone_data_.clear();
+    anr_data_.clear();
+}
+
 /*
  * Dumps relevant information to a bugreport based on the given options.
  *
@@ -2755,7 +2768,7 @@
     return ds.options_->bugreport_fd.get() != -1 ? true : false;
 }
 
-void Dumpstate::CleanupFiles() {
+void Dumpstate::CleanupTmpFiles() {
     android::os::UnlinkAndLogOnError(tmp_path_);
     android::os::UnlinkAndLogOnError(screenshot_path_);
     android::os::UnlinkAndLogOnError(path_);
@@ -2763,7 +2776,7 @@
 
 Dumpstate::RunStatus Dumpstate::HandleUserConsentDenied() {
     MYLOGD("User denied consent; deleting files and returning\n");
-    CleanupFiles();
+    CleanupTmpFiles();
     return USER_CONSENT_DENIED;
 }
 
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 7b8d282..9ce662b 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -334,6 +334,9 @@
 
     RunStatus ParseCommandlineAndRun(int argc, char* argv[]);
 
+    /* Deletes in-progress files */
+    void Cancel();
+
     /* Sets runtime options. */
     void SetOptions(std::unique_ptr<DumpOptions> options);
 
@@ -502,7 +505,7 @@
 
     // Removes the in progress files output files (tmp file, zip/txt file, screenshot),
     // but leaves the log file alone.
-    void CleanupFiles();
+    void CleanupTmpFiles();
 
     RunStatus HandleUserConsentDenied();
 
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index eefbe4f..d773790 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -138,10 +138,10 @@
             return 4;
         }
 
-        PrepareEnvironment();
+        PrepareEnvironmentVariables();
 
-        if (!PrepareBootImage(/* force */ false)) {
-            LOG(ERROR) << "Failed preparing boot image.";
+        if (!EnsureBootImageAndDalvikCache()) {
+            LOG(ERROR) << "Bad boot image.";
             return 5;
         }
 
@@ -302,7 +302,7 @@
         return parameters_.ReadArguments(argc, const_cast<const char**>(argv));
     }
 
-    void PrepareEnvironment() {
+    void PrepareEnvironmentVariables() {
         environ_.push_back(StringPrintf("BOOTCLASSPATH=%s", boot_classpath_.c_str()));
         environ_.push_back(StringPrintf("ANDROID_DATA=%s", GetOTADataDirectory().c_str()));
         environ_.push_back(StringPrintf("ANDROID_ROOT=%s", android_root_.c_str()));
@@ -312,9 +312,8 @@
         }
     }
 
-    // Ensure that we have the right boot image. The first time any app is
-    // compiled, we'll try to generate it.
-    bool PrepareBootImage(bool force) const {
+    // Ensure that we have the right boot image and cache file structures.
+    bool EnsureBootImageAndDalvikCache() const {
         if (parameters_.instruction_set == nullptr) {
             LOG(ERROR) << "Instruction set missing.";
             return false;
@@ -340,34 +339,19 @@
             }
         }
 
-        // Check whether we have files in /data.
+        // Clear cached artifacts.
+        ClearDirectory(isa_path);
+
+        // Check whether we have a boot image.
         // TODO: check that the files are correct wrt/ jars.
-        std::string art_path = isa_path + "/system@framework@boot.art";
-        std::string oat_path = isa_path + "/system@framework@boot.oat";
-        bool cleared = false;
-        if (access(art_path.c_str(), F_OK) == 0 && access(oat_path.c_str(), F_OK) == 0) {
-            // Files exist, assume everything is alright if not forced. Otherwise clean up.
-            if (!force) {
-                return true;
-            }
-            ClearDirectory(isa_path);
-            cleared = true;
+        std::string preopted_boot_art_path =
+            StringPrintf("/apex/com.android.art/javalib/%s/boot.art", isa);
+        if (access(preopted_boot_art_path.c_str(), F_OK) != 0) {
+            PLOG(ERROR) << "Bad access() to " << preopted_boot_art_path;
+            return false;
         }
 
-        // Check whether we have an image in /system.
-        // TODO: check that the files are correct wrt/ jars.
-        std::string preopted_boot_art_path = StringPrintf("/system/framework/%s/boot.art", isa);
-        if (access(preopted_boot_art_path.c_str(), F_OK) == 0) {
-            // Note: we ignore |force| here.
-            return true;
-        }
-
-
-        if (!cleared) {
-            ClearDirectory(isa_path);
-        }
-
-        return Dex2oatBootImage(boot_classpath_, art_path, oat_path, isa);
+        return true;
     }
 
     static bool CreatePath(const std::string& path) {
@@ -432,77 +416,6 @@
         CHECK_EQ(0, closedir(c_dir)) << "Unable to close directory.";
     }
 
-    bool Dex2oatBootImage(const std::string& boot_cp,
-                          const std::string& art_path,
-                          const std::string& oat_path,
-                          const char* isa) const {
-        // This needs to be kept in sync with ART, see art/runtime/gc/space/image_space.cc.
-        std::vector<std::string> cmd;
-        cmd.push_back(kDex2oatPath);
-        cmd.push_back(StringPrintf("--image=%s", art_path.c_str()));
-        for (const std::string& boot_part : Split(boot_cp, ":")) {
-            cmd.push_back(StringPrintf("--dex-file=%s", boot_part.c_str()));
-        }
-        cmd.push_back(StringPrintf("--oat-file=%s", oat_path.c_str()));
-
-        int32_t base_offset = ChooseRelocationOffsetDelta(
-                art::imagevalues::GetImageMinBaseAddressDelta(),
-                art::imagevalues::GetImageMaxBaseAddressDelta());
-        cmd.push_back(StringPrintf("--base=0x%x",
-                art::imagevalues::GetImageBaseAddress() + base_offset));
-
-        cmd.push_back(StringPrintf("--instruction-set=%s", isa));
-
-        // These things are pushed by AndroidRuntime, see frameworks/base/core/jni/AndroidRuntime.cpp.
-        AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-Xms",
-                "-Xms",
-                true,
-                cmd);
-        AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-Xmx",
-                "-Xmx",
-                true,
-                cmd);
-        AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-filter",
-                "--compiler-filter=",
-                false,
-                cmd);
-        cmd.push_back("--profile-file=/system/etc/boot-image.prof");
-        // TODO: Compiled-classes.
-        const std::string* extra_opts =
-                system_properties_.GetProperty("dalvik.vm.image-dex2oat-flags");
-        if (extra_opts != nullptr) {
-            std::vector<std::string> extra_vals = Split(*extra_opts, " ");
-            cmd.insert(cmd.end(), extra_vals.begin(), extra_vals.end());
-        }
-        // TODO: Should we lower this? It's usually set close to max, because
-        //       normally there's not much else going on at boot.
-        AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-threads",
-                "-j",
-                false,
-                cmd);
-        AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-cpu-set",
-                "--cpu-set=",
-                false,
-                cmd);
-        AddCompilerOptionFromSystemProperty(
-                StringPrintf("dalvik.vm.isa.%s.variant", isa).c_str(),
-                "--instruction-set-variant=",
-                false,
-                cmd);
-        AddCompilerOptionFromSystemProperty(
-                StringPrintf("dalvik.vm.isa.%s.features", isa).c_str(),
-                "--instruction-set-features=",
-                false,
-                cmd);
-
-        std::string error_msg;
-        bool result = Exec(cmd, &error_msg);
-        if (!result) {
-            LOG(ERROR) << "Could not generate boot image: " << error_msg;
-        }
-        return result;
-    }
-
     static const char* ParseNull(const char* arg) {
         return (strcmp(arg, "!") == 0) ? nullptr : arg;
     }
@@ -592,22 +505,6 @@
             return 0;
         }
 
-        // If the dexopt failed, we may have a stale boot image from a previous OTA run.
-        // Then regenerate and retry.
-        if (WEXITSTATUS(dexopt_result) ==
-                static_cast<int>(::art::dex2oat::ReturnCode::kCreateRuntime)) {
-            if (!PrepareBootImage(/* force */ true)) {
-                LOG(ERROR) << "Forced boot image creating failed. Original error return was "
-                        << dexopt_result;
-                return dexopt_result;
-            }
-
-            int dexopt_result_boot_image_retry = Dexopt();
-            if (dexopt_result_boot_image_retry == 0) {
-                return 0;
-            }
-        }
-
         // If this was a profile-guided run, we may have profile version issues. Try to downgrade,
         // if possible.
         if ((parameters_.dexopt_flags & DEXOPT_PROFILE_GUIDED) == 0) {
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index abe6436..cbbea12 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -522,6 +522,11 @@
         return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
     }
 
+    if (serviceIt->second.guaranteeClient) {
+        LOG(INFO) << "Tried to unregister " << name << ", but there is about to be a client.";
+        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+    }
+
     int clients = handleServiceClientCallback(name, false);
 
     // clients < 0: feature not implemented or other error. Assume clients.
@@ -532,6 +537,8 @@
     if (clients < 0 || clients > 2) {
         // client callbacks are either disabled or there are other clients
         LOG(INFO) << "Tried to unregister " << name << ", but there are clients: " << clients;
+        // Set this flag to ensure the clients are acknowledged in the next callback
+        serviceIt->second.guaranteeClient = true;
         return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
     }
 
diff --git a/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-0.5-crop-11.png b/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-0.5-crop-11.png
new file mode 100644
index 0000000..1acc59d
--- /dev/null
+++ b/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-0.5-crop-11.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-169.png b/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-169.png
new file mode 100644
index 0000000..4ab9ca4
--- /dev/null
+++ b/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-169.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-43.png b/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-43.png
new file mode 100644
index 0000000..d74e673
--- /dev/null
+++ b/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-43.png
Binary files differ
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index c30dcfe..cbcf6ec 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -409,7 +409,7 @@
 
 #if __ANDROID_API__ >= 30
 
-/*
+/**
  * Sets the intended frame rate for |surface_control|.
  *
  * On devices that are capable of running the display at different refresh rates, the system may
@@ -421,9 +421,9 @@
  *
  * |frameRate| is the intended frame rate of this surface, in frames per second. 0 is a special
  * value that indicates the app will accept the system's choice for the display frame rate, which is
- * the default behavior if this function isn't called. The frameRate param does *not* need to be a
- * valid refresh rate for this device's display - e.g., it's fine to pass 30fps to a device that can
- * only run the display at 60fps.
+ * the default behavior if this function isn't called. The frameRate param does <em>not</em> need to
+ * be a valid refresh rate for this device's display - e.g., it's fine to pass 30fps to a device
+ * that can only run the display at 60fps.
  *
  * |compatibility| The frame rate compatibility of this surface. The compatibility value may
  * influence the system's choice of display frame rate. To specify a compatibility use the
diff --git a/include/android/thermal.h b/include/android/thermal.h
index 0f4b4d9..3247fa1 100644
--- a/include/android/thermal.h
+++ b/include/android/thermal.h
@@ -109,7 +109,7 @@
  * It's passed the updated thermal status as parameter, as well as the
  * pointer provided by the client that registered a callback.
  */
-typedef int (*AThermal_StatusCallback)(void *data, AThermalStatus status);
+typedef void (*AThermal_StatusCallback)(void *data, AThermalStatus status);
 
 /**
   * Acquire an instance of the thermal manager. This must be freed using
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 8ca178c..7ca9031 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -404,6 +404,13 @@
      */
     bool hasPendingBatch() const;
 
+    /* Returns the source of first pending batch if exist.
+     *
+     * Should be called after calling consume() with consumeBatches == false to determine
+     * whether consume() should be called again later on with consumeBatches == true.
+     */
+    int32_t getPendingBatchSource() const;
+
 private:
     // True if touch resampling is enabled.
     const bool mResampleTouch;
diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp
index 5e4c98f..4f2709d 100644
--- a/libs/binder/ActivityManager.cpp
+++ b/libs/binder/ActivityManager.cpp
@@ -98,6 +98,15 @@
     return PROCESS_STATE_UNKNOWN;
 }
 
+bool ActivityManager::isUidActiveOrForeground(const uid_t uid, const String16& callingPackage)
+{
+    sp<IActivityManager> service = getService();
+    if (service != nullptr) {
+        return service->isUidActiveOrForeground(uid, callingPackage);
+    }
+    return false;
+}
+
 status_t ActivityManager::linkToDeath(const sp<IBinder::DeathRecipient>& recipient) {
     sp<IActivityManager> service = getService();
     if (service != nullptr) {
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index bc541f4..e6cfeb4 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -158,7 +158,9 @@
 filegroup {
     name: "libbinder_aidl",
     srcs: [
+        "aidl/android/content/pm/IPackageChangeObserver.aidl",
         "aidl/android/content/pm/IPackageManagerNative.aidl",
+        "aidl/android/content/pm/PackageChangeEvent.aidl",
         "aidl/android/os/IClientCallback.aidl",
         "aidl/android/os/IServiceCallback.aidl",
         "aidl/android/os/IServiceManager.aidl",
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
index 1eb5363..9e1249b 100644
--- a/libs/binder/IActivityManager.cpp
+++ b/libs/binder/IActivityManager.cpp
@@ -104,6 +104,18 @@
         }
         return reply.readInt32();
     }
+
+    virtual bool isUidActiveOrForeground(const uid_t uid, const String16& callingPackage)
+    {
+         Parcel data, reply;
+         data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
+         data.writeInt32(uid);
+         data.writeString16(callingPackage);
+         remote()->transact(IS_UID_ACTIVE_OR_FOREGROUND_TRANSACTION, data, &reply);
+         // fail on exception
+         if (reply.readExceptionCode() != 0) return false;
+         return reply.readInt32() == 1;
+    }
 };
 
 // ------------------------------------------------------------------------------------
diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp
index 71d8130..74aece8 100644
--- a/libs/binder/LazyServiceRegistrar.cpp
+++ b/libs/binder/LazyServiceRegistrar.cpp
@@ -31,11 +31,16 @@
 
 class ClientCounterCallback : public ::android::os::BnClientCallback {
 public:
-    ClientCounterCallback() : mNumConnectedServices(0) {}
+    ClientCounterCallback() : mNumConnectedServices(0), mForcePersist(false) {}
 
     bool registerService(const sp<IBinder>& service, const std::string& name,
                          bool allowIsolated, int dumpFlags);
 
+    /**
+     * Set a flag to prevent services from automatically shutting down
+     */
+    void forcePersist(bool persist);
+
 protected:
     Status onClients(const sp<IBinder>& service, bool clients) override;
 
@@ -60,6 +65,8 @@
      * Map of registered names and services
      */
     std::map<std::string, Service> mRegisteredServices;
+
+    bool mForcePersist;
 };
 
 bool ClientCounterCallback::registerService(const sp<IBinder>& service, const std::string& name,
@@ -88,6 +95,14 @@
     return true;
 }
 
+void ClientCounterCallback::forcePersist(bool persist) {
+    mForcePersist = persist;
+    if(!mForcePersist) {
+        // Attempt a shutdown in case the number of clients hit 0 while the flag was on
+        tryShutdown();
+    }
+}
+
 /**
  * onClients is oneway, so no need to worry about multi-threading. Note that this means multiple
  * invocations could occur on different threads however.
@@ -103,14 +118,21 @@
           mNumConnectedServices, mRegisteredServices.size(),
           String8(service->getInterfaceDescriptor()).string(), clients);
 
-    if (mNumConnectedServices == 0) {
-        tryShutdown();
-    }
-
+    tryShutdown();
     return Status::ok();
 }
 
 void ClientCounterCallback::tryShutdown() {
+    if(mNumConnectedServices > 0) {
+        // Should only shut down if there are no clients
+        return;
+    }
+
+    if(mForcePersist) {
+        ALOGI("Shutdown prevented by forcePersist override flag.");
+        return;
+    }
+
     ALOGI("Trying to shut down the service. No clients in use for any service in process.");
 
     auto manager = interface_cast<AidlServiceManager>(asBinder(defaultServiceManager()));
@@ -165,5 +187,9 @@
     return OK;
 }
 
+void LazyServiceRegistrar::forcePersist(bool persist) {
+    mClientCC->forcePersist(persist);
+}
+
 }  // namespace hardware
 }  // namespace android
\ No newline at end of file
diff --git a/libs/binder/aidl/android/content/pm/IPackageChangeObserver.aidl b/libs/binder/aidl/android/content/pm/IPackageChangeObserver.aidl
new file mode 100644
index 0000000..6929a6c
--- /dev/null
+++ b/libs/binder/aidl/android/content/pm/IPackageChangeObserver.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.content.pm.PackageChangeEvent;
+
+/**
+ * This is a non-blocking notification when a package has changed.
+ *
+ * @hide
+ */
+oneway interface IPackageChangeObserver {
+  void onPackageChanged(in PackageChangeEvent event);
+}
diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
index 618f88c..dc8d74c 100644
--- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
+++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
@@ -17,6 +17,8 @@
 
 package android.content.pm;
 
+import android.content.pm.IPackageChangeObserver;
+
 /**
  * Parallel implementation of certain {@link PackageManager} APIs that need to
  * be exposed to native code.
@@ -90,4 +92,13 @@
 
     /* Returns the names of all packages. */
     @utf8InCpp String[] getAllPackages();
+
+    /** Register an extra package change observer to receive the multi-cast. */
+    void registerPackageChangeObserver(in IPackageChangeObserver observer);
+
+    /**
+     * Unregister an existing package change observer.
+     * This does nothing if this observer was not already registered.
+     */
+    void unregisterPackageChangeObserver(in IPackageChangeObserver observer);
 }
diff --git a/libs/binder/aidl/android/content/pm/PackageChangeEvent.aidl b/libs/binder/aidl/android/content/pm/PackageChangeEvent.aidl
new file mode 100644
index 0000000..e30e907
--- /dev/null
+++ b/libs/binder/aidl/android/content/pm/PackageChangeEvent.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+/**
+ * This event is designed for notification to native code listener about
+ * any changes on a package including update, deletion and etc.
+ *
+ * @hide
+ */
+parcelable PackageChangeEvent {
+  @utf8InCpp String packageName;
+  long version;
+  long lastUpdateTimeMillis;
+  boolean newInstalled;
+  boolean dataRemoved;
+  boolean isDeleted;
+}
diff --git a/libs/binder/include/binder/ActivityManager.h b/libs/binder/include/binder/ActivityManager.h
index 9108e31..0bb6d28 100644
--- a/libs/binder/include/binder/ActivityManager.h
+++ b/libs/binder/include/binder/ActivityManager.h
@@ -46,25 +46,24 @@
         PROCESS_STATE_PERSISTENT = 0,
         PROCESS_STATE_PERSISTENT_UI = 1,
         PROCESS_STATE_TOP = 2,
-        PROCESS_STATE_FOREGROUND_SERVICE_LOCATION = 3,
-        PROCESS_STATE_BOUND_TOP = 4,
-        PROCESS_STATE_FOREGROUND_SERVICE = 5,
-        PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 6,
-        PROCESS_STATE_IMPORTANT_FOREGROUND = 7,
-        PROCESS_STATE_IMPORTANT_BACKGROUND = 8,
-        PROCESS_STATE_TRANSIENT_BACKGROUND = 9,
-        PROCESS_STATE_BACKUP = 10,
-        PROCESS_STATE_SERVICE = 11,
-        PROCESS_STATE_RECEIVER = 12,
-        PROCESS_STATE_TOP_SLEEPING = 13,
-        PROCESS_STATE_HEAVY_WEIGHT = 14,
-        PROCESS_STATE_HOME = 15,
-        PROCESS_STATE_LAST_ACTIVITY = 16,
-        PROCESS_STATE_CACHED_ACTIVITY = 17,
-        PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 18,
-        PROCESS_STATE_CACHED_RECENT = 19,
-        PROCESS_STATE_CACHED_EMPTY = 20,
-        PROCESS_STATE_NONEXISTENT = 21,
+        PROCESS_STATE_BOUND_TOP = 3,
+        PROCESS_STATE_FOREGROUND_SERVICE = 4,
+        PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 5,
+        PROCESS_STATE_IMPORTANT_FOREGROUND = 6,
+        PROCESS_STATE_IMPORTANT_BACKGROUND = 7,
+        PROCESS_STATE_TRANSIENT_BACKGROUND = 8,
+        PROCESS_STATE_BACKUP = 9,
+        PROCESS_STATE_SERVICE = 10,
+        PROCESS_STATE_RECEIVER = 11,
+        PROCESS_STATE_TOP_SLEEPING = 12,
+        PROCESS_STATE_HEAVY_WEIGHT = 13,
+        PROCESS_STATE_HOME = 14,
+        PROCESS_STATE_LAST_ACTIVITY = 15,
+        PROCESS_STATE_CACHED_ACTIVITY = 16,
+        PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 17,
+        PROCESS_STATE_CACHED_RECENT = 18,
+        PROCESS_STATE_CACHED_EMPTY = 19,
+        PROCESS_STATE_NONEXISTENT = 20,
     };
 
     ActivityManager();
@@ -77,6 +76,7 @@
     void unregisterUidObserver(const sp<IUidObserver>& observer);
     bool isUidActive(const uid_t uid, const String16& callingPackage);
     int getUidProcessState(const uid_t uid, const String16& callingPackage);
+    bool isUidActiveOrForeground(const uid_t uid, const String16& callingPackage);
 
 
   status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient);
diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h
index 2ee5930..6afcd77 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -122,7 +122,16 @@
         OP_LEGACY_STORAGE = 87,
         OP_ACCESS_ACCESSIBILITY = 88,
         OP_READ_DEVICE_IDENTIFIERS = 89,
-        _NUM_OP = 90
+        OP_ACCESS_MEDIA_LOCATION = 90,
+        OP_QUERY_ALL_PACKAGES = 91,
+        OP_MANAGE_EXTERNAL_STORAGE = 92,
+        OP_INTERACT_ACROSS_PROFILES = 93,
+        OP_ACTIVATE_PLATFORM_VPN = 94,
+        OP_LOADER_USAGE_STATS = 95,
+        OP_DEPRECATED_1 = 96,
+        OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED = 97,
+        OP_AUTO_REVOKE_MANAGED_BY_INSTALLER = 98,
+        _NUM_OP = 99
     };
 
     AppOpsManager();
diff --git a/libs/binder/include/binder/IActivityManager.h b/libs/binder/include/binder/IActivityManager.h
index e0248f6..1815ecc 100644
--- a/libs/binder/include/binder/IActivityManager.h
+++ b/libs/binder/include/binder/IActivityManager.h
@@ -39,13 +39,15 @@
     virtual void unregisterUidObserver(const sp<IUidObserver>& observer) = 0;
     virtual bool isUidActive(const uid_t uid, const String16& callingPackage) = 0;
     virtual int32_t getUidProcessState(const uid_t uid, const String16& callingPackage) = 0;
+    virtual bool isUidActiveOrForeground(const uid_t uid, const String16& callingPackage) = 0;
 
     enum {
         OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
         REGISTER_UID_OBSERVER_TRANSACTION,
         UNREGISTER_UID_OBSERVER_TRANSACTION,
         IS_UID_ACTIVE_TRANSACTION,
-        GET_UID_PROCESS_STATE_TRANSACTION
+        GET_UID_PROCESS_STATE_TRANSACTION,
+        IS_UID_ACTIVE_OR_FOREGROUND_TRANSACTION,
     };
 };
 
diff --git a/libs/binder/include/binder/LazyServiceRegistrar.h b/libs/binder/include/binder/LazyServiceRegistrar.h
index efdecc4..6d711bc 100644
--- a/libs/binder/include/binder/LazyServiceRegistrar.h
+++ b/libs/binder/include/binder/LazyServiceRegistrar.h
@@ -34,6 +34,12 @@
                               const std::string& name = "default",
                               bool allowIsolated = false,
                               int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT);
+     /**
+      * Force the service to persist, even when it has 0 clients.
+      * If setting this flag from the server side, make sure to do so before calling registerService,
+      * or there may be a race with the default dynamic shutdown.
+      */
+     void forcePersist(bool persist);
 
    private:
      std::shared_ptr<internal::ClientCounterCallback> mClientCC;
diff --git a/libs/gui/tests/AndroidTest.xml b/libs/gui/tests/AndroidTest.xml
index c02e020..5e09fff 100644
--- a/libs/gui/tests/AndroidTest.xml
+++ b/libs/gui/tests/AndroidTest.xml
@@ -18,6 +18,10 @@
         <option name="cleanup" value="true" />
         <option name="push" value="libgui_test->/data/local/tmp/libgui_test" />
     </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+      <option name="force-skip-system-props" value="true" /> <!-- avoid restarting device -->
+      <option name="screen-always-on" value="on" />
+    </target_preparer>
     <option name="test-suite-tag" value="apct" />
     <test class="com.android.tradefed.testtype.GTest" >
         <option name="native-test-device-path" value="/data/local/tmp" />
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 7335b30..ef7cc7d 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -1130,6 +1130,16 @@
     return !mBatches.isEmpty();
 }
 
+int32_t InputConsumer::getPendingBatchSource() const {
+    if (mBatches.isEmpty()) {
+        return AINPUT_SOURCE_CLASS_NONE;
+    }
+
+    const Batch& batch = mBatches.itemAt(0);
+    const InputMessage& head = batch.samples.itemAt(0);
+    return head.body.motion.source;
+}
+
 ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const {
     for (size_t i = 0; i < mBatches.size(); i++) {
         const Batch& batch = mBatches.itemAt(i);
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index 59aa665..25130e2 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -233,15 +233,15 @@
 
 #if __ANDROID_API__ >= 30
 
-/* Parameter for ANativeWindow_setFrameRate */
-enum {
+/** Compatibility value for ANativeWindow_setFrameRate. */
+enum ANativeWindow_FrameRateCompatibility {
     /**
      * There are no inherent restrictions on the frame rate of this window.
      */
     ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT = 0,
     /**
      * This window is being used to display content with an inherently fixed
-     * frame rate, e.g. a video that has a specific frame rate. When the system
+     * frame rate, e.g.\ a video that has a specific frame rate. When the system
      * selects a frame rate other than what the app requested, the app will need
      * to do pull down or use some other technique to adapt to the system's
      * frame rate. The user experience is likely to be worse (e.g. more frame
@@ -272,9 +272,9 @@
  * \param frameRate The intended frame rate of this window, in frames per
  * second. 0 is a special value that indicates the app will accept the system's
  * choice for the display frame rate, which is the default behavior if this
- * function isn't called. The frameRate param does *not* need to be a valid
- * refresh rate for this device's display - e.g., it's fine to pass 30fps to a
- * device that can only run the display at 60fps.
+ * function isn't called. The frameRate param does <em>not</em> need to be a
+ * valid refresh rate for this device's display - e.g., it's fine to pass 30fps
+ * to a device that can only run the display at 60fps.
  *
  * \param compatibility The frame rate compatibility of this window. The
  * compatibility value may influence the system's choice of display refresh
diff --git a/libs/ui/include/ui/Size.h b/libs/ui/include/ui/Size.h
index c2cda17..f1e8252 100644
--- a/libs/ui/include/ui/Size.h
+++ b/libs/ui/include/ui/Size.h
@@ -109,11 +109,11 @@
     // Takes a value of type FromType, and ensures it can be represented as a value of type ToType,
     // clamping the input value to the output range if necessary.
     template <typename ToType, typename FromType>
-    static Size::remove_cv_reference_t<ToType> clamp(
-            typename std::enable_if<
-                    std::numeric_limits<Size::remove_cv_reference_t<ToType>>::is_bounded &&
-                            std::numeric_limits<Size::remove_cv_reference_t<FromType>>::is_bounded,
-                    FromType&&>::type v) {
+    static Size::remove_cv_reference_t<ToType>
+    clamp(typename std::enable_if<
+            std::numeric_limits<Size::remove_cv_reference_t<ToType>>::is_specialized &&
+                    std::numeric_limits<Size::remove_cv_reference_t<FromType>>::is_specialized,
+            FromType>::type v) {
         using BareToType = remove_cv_reference_t<ToType>;
         using BareFromType = remove_cv_reference_t<FromType>;
         static constexpr auto toHighest = std::numeric_limits<BareToType>::max();
@@ -121,21 +121,58 @@
         static constexpr auto fromHighest = std::numeric_limits<BareFromType>::max();
         static constexpr auto fromLowest = std::numeric_limits<BareFromType>::lowest();
 
-        // A clamp is needed if the range of FromType is not a subset of the range of ToType
-        static constexpr bool isClampNeeded = (toLowest > fromLowest) || (toHighest < fromHighest);
+        // Get the closest representation of [toLowest, toHighest] in type
+        // FromType to use to clamp the input value before conversion.
+
+        // std::common_type<...> is used to get a value-preserving type for the
+        // top end of the range.
+        using CommonHighestType = std::common_type_t<BareToType, BareFromType>;
+
+        // std::make_signed<std::common_type<...>> is used to get a
+        // value-preserving type for the bottom end of the range, except this is
+        // a bit trickier for non-integer types like float.
+        using CommonLowestType =
+                std::conditional_t<std::numeric_limits<CommonHighestType>::is_integer,
+                                   std::make_signed_t<std::conditional_t<
+                                           std::numeric_limits<CommonHighestType>::is_integer,
+                                           CommonHighestType, int /* not used */>>,
+                                   CommonHighestType>;
+
+        // We can then compute the clamp range in a way that can be later
+        // trivially converted to either the 'from' or 'to' types, and be
+        // representabile in either.
+        static constexpr auto commonClampHighest =
+                std::min(static_cast<CommonHighestType>(fromHighest),
+                         static_cast<CommonHighestType>(toHighest));
+        static constexpr auto commonClampLowest =
+                std::max(static_cast<CommonLowestType>(fromLowest),
+                         static_cast<CommonLowestType>(toLowest));
+
+        static constexpr auto fromClampHighest = static_cast<BareFromType>(commonClampHighest);
+        static constexpr auto fromClampLowest = static_cast<BareFromType>(commonClampLowest);
+
+        // A clamp is needed only if the range we are clamping to is not the
+        // same as the range of the input.
+        static constexpr bool isClampNeeded =
+                (fromLowest != fromClampLowest) || (fromHighest != fromClampHighest);
 
         // If a clamp is not needed, the conversion is just a trivial cast.
         if (!isClampNeeded) {
-            return static_cast<ToType>(v);
+            return static_cast<BareToType>(v);
         }
 
-        // Otherwise we need to carefully compare the limits of ToType (casted
-        // for the comparisons to be warning free to FromType) while still
-        // ensuring we return a value clamped to the range of ToType.
-        return v < static_cast<const BareFromType>(toLowest)
-                ? toLowest
-                : (v > static_cast<const BareFromType>(toHighest) ? toHighest
-                                                                  : static_cast<ToType>(v));
+        // Note: Clang complains about the value of INT32_MAX not being
+        // convertible back to int32_t from float if this is made "constexpr",
+        // when clamping a float value to an int32_t value. This is however
+        // covered by a test case to ensure the run-time cast works correctly.
+        const auto toClampHighest = static_cast<BareToType>(commonClampHighest);
+        const auto toClampLowest = static_cast<BareToType>(commonClampLowest);
+
+        // Otherwise clamping is done by using the already computed endpoints
+        // for each type.
+        return (v <= fromClampLowest)
+                ? toClampLowest
+                : ((v >= fromClampHighest) ? toClampHighest : static_cast<BareToType>(v));
     }
 };
 
diff --git a/libs/ui/tests/Size_test.cpp b/libs/ui/tests/Size_test.cpp
index 40dc702..38f37ad 100644
--- a/libs/ui/tests/Size_test.cpp
+++ b/libs/ui/tests/Size_test.cpp
@@ -186,9 +186,34 @@
 
 TEST(SizeTest, FloatRangeIsClamped) {
     ClampTest(std::numeric_limits<float>::max(), std::numeric_limits<int32_t>::max());
+    ClampTest(nexttowardf(std::numeric_limits<int32_t>::max(), std::numeric_limits<float>::max()),
+              std::numeric_limits<int32_t>::max());
+    ClampTest(static_cast<float>(std::numeric_limits<int32_t>::max()),
+              std::numeric_limits<int32_t>::max());
+    ClampTest(nexttowardf(std::numeric_limits<int32_t>::max(), 0),
+              static_cast<int32_t>(nexttowardf(std::numeric_limits<int32_t>::max(), 0)));
     ClampTest(float(0), int32_t(0));
+    ClampTest(nexttowardf(std::numeric_limits<int32_t>::lowest(), 0),
+              static_cast<int32_t>(nexttowardf(std::numeric_limits<int32_t>::lowest(), 0)));
+    ClampTest(static_cast<float>(std::numeric_limits<int32_t>::lowest()),
+              std::numeric_limits<int32_t>::lowest());
+    ClampTest(nexttowardf(std::numeric_limits<int32_t>::lowest(),
+                          std::numeric_limits<float>::lowest()),
+              std::numeric_limits<int32_t>::lowest());
     ClampTest(std::numeric_limits<float>::lowest(), std::numeric_limits<int32_t>::lowest());
 }
 
+TEST(SizeTest, Uint32RangeIsClamped) {
+    ClampTest(std::numeric_limits<uint32_t>::max(), std::numeric_limits<int32_t>::max());
+    ClampTest(std::numeric_limits<uint32_t>::max() - 1, std::numeric_limits<int32_t>::max());
+    ClampTest(static_cast<uint32_t>(std::numeric_limits<int32_t>::max()) + 1,
+              std::numeric_limits<int32_t>::max());
+    ClampTest(static_cast<uint32_t>(std::numeric_limits<int32_t>::max()),
+              std::numeric_limits<int32_t>::max());
+    ClampTest(static_cast<uint32_t>(std::numeric_limits<int32_t>::max()) - 1,
+              std::numeric_limits<int32_t>::max() - 1);
+    ClampTest(uint32_t(0), int32_t(0));
+}
+
 } // namespace ui
 } // namespace android
diff --git a/opengl/TEST_MAPPING b/opengl/TEST_MAPPING
new file mode 100644
index 0000000..d391dce
--- /dev/null
+++ b/opengl/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsGpuToolsHostTestCases"
+    }
+  ]
+}
diff --git a/services/automotive/display/Android.bp b/services/automotive/display/Android.bp
index 8ff0711..c3da216 100644
--- a/services/automotive/display/Android.bp
+++ b/services/automotive/display/Android.bp
@@ -40,4 +40,8 @@
     cflags: [
         "-DLOG_TAG=\"AutomotiveDisplayService\""
     ],
+
+    vintf_fragments: [
+        "manifest_android.frameworks.automotive.display@1.0.xml",
+    ],
 }
diff --git a/services/automotive/display/manifest_android.frameworks.automotive.display@1.0.xml b/services/automotive/display/manifest_android.frameworks.automotive.display@1.0.xml
new file mode 100644
index 0000000..464dcac
--- /dev/null
+++ b/services/automotive/display/manifest_android.frameworks.automotive.display@1.0.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="framework">
+    <hal>
+        <name>android.frameworks.automotive.display</name>
+        <transport>hwbinder</transport>
+        <version>1.0</version>
+        <interface>
+            <name>IAutomotiveDisplayProxyService</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index bbc8e53..99a572a 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -160,8 +160,8 @@
       : InputMapper(deviceContext),
         mSource(0),
         mDeviceMode(DEVICE_MODE_DISABLED),
-        mSurfaceWidth(-1),
-        mSurfaceHeight(-1),
+        mRawSurfaceWidth(-1),
+        mRawSurfaceHeight(-1),
         mSurfaceLeft(0),
         mSurfaceTop(0),
         mPhysicalWidth(-1),
@@ -680,7 +680,7 @@
                     naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft;
                     naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop;
                     naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft;
-                    naturalPhysicalLeft = mViewport.deviceHeight - naturalPhysicalWidth;
+                    naturalPhysicalLeft = mViewport.deviceHeight - mViewport.physicalBottom;
                     naturalPhysicalTop = mViewport.physicalLeft;
                     naturalDeviceWidth = mViewport.deviceHeight;
                     naturalDeviceHeight = mViewport.deviceWidth;
@@ -701,7 +701,7 @@
                     naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop;
                     naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft;
                     naturalPhysicalLeft = mViewport.physicalTop;
-                    naturalPhysicalTop = mViewport.deviceWidth - naturalPhysicalHeight;
+                    naturalPhysicalTop = mViewport.deviceWidth - mViewport.physicalRight;
                     naturalDeviceWidth = mViewport.deviceHeight;
                     naturalDeviceHeight = mViewport.deviceWidth;
                     break;
@@ -729,10 +729,12 @@
             mPhysicalLeft = naturalPhysicalLeft;
             mPhysicalTop = naturalPhysicalTop;
 
-            mSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth;
-            mSurfaceHeight = naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight;
+            mRawSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth;
+            mRawSurfaceHeight = naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight;
             mSurfaceLeft = naturalPhysicalLeft * naturalLogicalWidth / naturalPhysicalWidth;
             mSurfaceTop = naturalPhysicalTop * naturalLogicalHeight / naturalPhysicalHeight;
+            mSurfaceRight = mSurfaceLeft + naturalLogicalWidth;
+            mSurfaceBottom = mSurfaceTop + naturalLogicalHeight;
 
             mSurfaceOrientation =
                     mParameters.orientationAware ? mViewport.orientation : DISPLAY_ORIENTATION_0;
@@ -742,8 +744,8 @@
             mPhysicalLeft = 0;
             mPhysicalTop = 0;
 
-            mSurfaceWidth = rawWidth;
-            mSurfaceHeight = rawHeight;
+            mRawSurfaceWidth = rawWidth;
+            mRawSurfaceHeight = rawHeight;
             mSurfaceLeft = 0;
             mSurfaceTop = 0;
             mSurfaceOrientation = DISPLAY_ORIENTATION_0;
@@ -769,12 +771,12 @@
     if (viewportChanged || deviceModeChanged) {
         ALOGI("Device reconfigured: id=%d, name='%s', size %dx%d, orientation %d, mode %d, "
               "display id %d",
-              getDeviceId(), getDeviceName().c_str(), mSurfaceWidth, mSurfaceHeight,
+              getDeviceId(), getDeviceName().c_str(), mRawSurfaceWidth, mRawSurfaceHeight,
               mSurfaceOrientation, mDeviceMode, mViewport.displayId);
 
         // Configure X and Y factors.
-        mXScale = float(mSurfaceWidth) / rawWidth;
-        mYScale = float(mSurfaceHeight) / rawHeight;
+        mXScale = float(mRawSurfaceWidth) / rawWidth;
+        mYScale = float(mRawSurfaceHeight) / rawHeight;
         mXTranslate = -mSurfaceLeft;
         mYTranslate = -mSurfaceTop;
         mXPrecision = 1.0f / mXScale;
@@ -793,7 +795,7 @@
         mGeometricScale = avg(mXScale, mYScale);
 
         // Size of diagonal axis.
-        float diagonalSize = hypotf(mSurfaceWidth, mSurfaceHeight);
+        float diagonalSize = hypotf(mRawSurfaceWidth, mRawSurfaceHeight);
 
         // Size factors.
         if (mCalibration.sizeCalibration != Calibration::SIZE_CALIBRATION_NONE) {
@@ -956,13 +958,13 @@
                 mOrientedYPrecision = mXPrecision;
 
                 mOrientedRanges.x.min = mYTranslate;
-                mOrientedRanges.x.max = mSurfaceHeight + mYTranslate - 1;
+                mOrientedRanges.x.max = mRawSurfaceHeight + mYTranslate - 1;
                 mOrientedRanges.x.flat = 0;
                 mOrientedRanges.x.fuzz = 0;
                 mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mYScale;
 
                 mOrientedRanges.y.min = mXTranslate;
-                mOrientedRanges.y.max = mSurfaceWidth + mXTranslate - 1;
+                mOrientedRanges.y.max = mRawSurfaceWidth + mXTranslate - 1;
                 mOrientedRanges.y.flat = 0;
                 mOrientedRanges.y.fuzz = 0;
                 mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mXScale;
@@ -973,13 +975,13 @@
                 mOrientedYPrecision = mYPrecision;
 
                 mOrientedRanges.x.min = mXTranslate;
-                mOrientedRanges.x.max = mSurfaceWidth + mXTranslate - 1;
+                mOrientedRanges.x.max = mRawSurfaceWidth + mXTranslate - 1;
                 mOrientedRanges.x.flat = 0;
                 mOrientedRanges.x.fuzz = 0;
                 mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mXScale;
 
                 mOrientedRanges.y.min = mYTranslate;
-                mOrientedRanges.y.max = mSurfaceHeight + mYTranslate - 1;
+                mOrientedRanges.y.max = mRawSurfaceHeight + mYTranslate - 1;
                 mOrientedRanges.y.flat = 0;
                 mOrientedRanges.y.fuzz = 0;
                 mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mYScale;
@@ -992,7 +994,7 @@
         if (mDeviceMode == DEVICE_MODE_POINTER) {
             // Compute pointer gesture detection parameters.
             float rawDiagonal = hypotf(rawWidth, rawHeight);
-            float displayDiagonal = hypotf(mSurfaceWidth, mSurfaceHeight);
+            float displayDiagonal = hypotf(mRawSurfaceWidth, mRawSurfaceHeight);
 
             // Scale movements such that one whole swipe of the touch pad covers a
             // given area relative to the diagonal size of the display when no acceleration
@@ -1027,10 +1029,12 @@
 
 void TouchInputMapper::dumpSurface(std::string& dump) {
     dump += StringPrintf(INDENT3 "%s\n", mViewport.toString().c_str());
-    dump += StringPrintf(INDENT3 "SurfaceWidth: %dpx\n", mSurfaceWidth);
-    dump += StringPrintf(INDENT3 "SurfaceHeight: %dpx\n", mSurfaceHeight);
+    dump += StringPrintf(INDENT3 "RawSurfaceWidth: %dpx\n", mRawSurfaceWidth);
+    dump += StringPrintf(INDENT3 "RawSurfaceHeight: %dpx\n", mRawSurfaceHeight);
     dump += StringPrintf(INDENT3 "SurfaceLeft: %d\n", mSurfaceLeft);
     dump += StringPrintf(INDENT3 "SurfaceTop: %d\n", mSurfaceTop);
+    dump += StringPrintf(INDENT3 "SurfaceRight: %d\n", mSurfaceRight);
+    dump += StringPrintf(INDENT3 "SurfaceBottom: %d\n", mSurfaceBottom);
     dump += StringPrintf(INDENT3 "PhysicalWidth: %dpx\n", mPhysicalWidth);
     dump += StringPrintf(INDENT3 "PhysicalHeight: %dpx\n", mPhysicalHeight);
     dump += StringPrintf(INDENT3 "PhysicalLeft: %d\n", mPhysicalLeft);
@@ -1074,16 +1078,16 @@
         int32_t halfHeight = virtualKeyDefinition.height / 2;
 
         virtualKey.hitLeft =
-                (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth / mSurfaceWidth +
+                (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth / mRawSurfaceWidth +
                 touchScreenLeft;
         virtualKey.hitRight =
-                (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth / mSurfaceWidth +
+                (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth / mRawSurfaceWidth +
                 touchScreenLeft;
-        virtualKey.hitTop =
-                (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight / mSurfaceHeight +
+        virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight /
+                        mRawSurfaceHeight +
                 touchScreenTop;
-        virtualKey.hitBottom =
-                (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight / mSurfaceHeight +
+        virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight /
+                        mRawSurfaceHeight +
                 touchScreenTop;
         mVirtualKeys.push_back(virtualKey);
     }
@@ -2188,13 +2192,10 @@
         rotateAndScale(xTransformed, yTransformed);
 
         // Adjust X, Y, and coverage coords for surface orientation.
-        float x, y;
         float left, top, right, bottom;
 
         switch (mSurfaceOrientation) {
             case DISPLAY_ORIENTATION_90:
-                x = yTransformed + mYTranslate;
-                y = xTransformed + mXTranslate;
                 left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
                 right = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
                 bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate;
@@ -2207,8 +2208,6 @@
                 }
                 break;
             case DISPLAY_ORIENTATION_180:
-                x = xTransformed + mXTranslate;
-                y = yTransformed + mYTranslate;
                 left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale;
                 right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale;
                 bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate;
@@ -2221,8 +2220,6 @@
                 }
                 break;
             case DISPLAY_ORIENTATION_270:
-                x = yTransformed + mYTranslate;
-                y = xTransformed + mXTranslate;
                 left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale;
                 right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale;
                 bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
@@ -2235,8 +2232,6 @@
                 }
                 break;
             default:
-                x = xTransformed + mXTranslate;
-                y = yTransformed + mYTranslate;
                 left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
                 right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
                 bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
@@ -2247,8 +2242,8 @@
         // Write output coords.
         PointerCoords& out = mCurrentCookedState.cookedPointerData.pointerCoords[i];
         out.clear();
-        out.setAxisValue(AMOTION_EVENT_AXIS_X, x);
-        out.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+        out.setAxisValue(AMOTION_EVENT_AXIS_X, xTransformed);
+        out.setAxisValue(AMOTION_EVENT_AXIS_Y, yTransformed);
         out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
         out.setAxisValue(AMOTION_EVENT_AXIS_SIZE, size);
         out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, touchMajor);
@@ -3624,34 +3619,47 @@
     abortTouches(when, 0 /* policyFlags*/);
 }
 
+// Transform raw coordinate to surface coordinate
 void TouchInputMapper::rotateAndScale(float& x, float& y) {
+    // Scale to surface coordinate.
+    const float xScaled = float(x - mRawPointerAxes.x.minValue) * mXScale;
+    const float yScaled = float(y - mRawPointerAxes.y.minValue) * mYScale;
+
+    // Rotate to surface coordinate.
+    // 0 - no swap and reverse.
+    // 90 - swap x/y and reverse y.
+    // 180 - reverse x, y.
+    // 270 - swap x/y and reverse x.
     switch (mSurfaceOrientation) {
+        case DISPLAY_ORIENTATION_0:
+            x = xScaled + mXTranslate;
+            y = yScaled + mYTranslate;
+            break;
         case DISPLAY_ORIENTATION_90:
-            x = float(mRawPointerAxes.x.maxValue - x) * mXScale;
-            y = float(y - mRawPointerAxes.y.minValue) * mYScale;
+            y = mSurfaceRight - xScaled;
+            x = yScaled + mYTranslate;
             break;
         case DISPLAY_ORIENTATION_180:
-            x = float(mRawPointerAxes.x.maxValue - x) * mXScale;
-            y = float(mRawPointerAxes.y.maxValue - y) * mYScale;
+            x = mSurfaceRight - xScaled;
+            y = mSurfaceBottom - yScaled;
             break;
         case DISPLAY_ORIENTATION_270:
-            x = float(x - mRawPointerAxes.x.minValue) * mXScale;
-            y = float(mRawPointerAxes.y.maxValue - y) * mYScale;
+            y = xScaled + mXTranslate;
+            x = mSurfaceBottom - yScaled;
             break;
         default:
-            x = float(x - mRawPointerAxes.x.minValue) * mXScale;
-            y = float(y - mRawPointerAxes.y.minValue) * mYScale;
-            break;
+            assert(false);
     }
 }
 
 bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) {
-    float xTransformed = x, yTransformed = y;
-    rotateAndScale(xTransformed, yTransformed);
+    const float xScaled = (x - mRawPointerAxes.x.minValue) * mXScale;
+    const float yScaled = (y - mRawPointerAxes.y.minValue) * mYScale;
+
     return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue &&
-            xTransformed >= mSurfaceLeft && xTransformed <= mSurfaceLeft + mSurfaceWidth &&
+            xScaled >= mSurfaceLeft && xScaled <= mSurfaceRight &&
             y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue &&
-            yTransformed >= mSurfaceTop && yTransformed <= mSurfaceTop + mSurfaceHeight;
+            yScaled >= mSurfaceTop && yScaled <= mSurfaceBottom;
 }
 
 const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(int32_t x, int32_t y) {
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index e21a33a..58bfc5c 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -407,12 +407,16 @@
     // The surface orientation, width and height set by configureSurface().
     // The width and height are derived from the viewport but are specified
     // in the natural orientation.
+    // They could be used for calculating diagonal, scaling factors, and virtual keys.
+    int32_t mRawSurfaceWidth;
+    int32_t mRawSurfaceHeight;
+
     // The surface origin specifies how the surface coordinates should be translated
     // to align with the logical display coordinate space.
-    int32_t mSurfaceWidth;
-    int32_t mSurfaceHeight;
     int32_t mSurfaceLeft;
     int32_t mSurfaceTop;
+    int32_t mSurfaceRight;
+    int32_t mSurfaceBottom;
 
     // Similar to the surface coordinates, but in the raw display coordinate space rather than in
     // the logical coordinate space.
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 618aefc..96d86b6 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -6990,51 +6990,7 @@
     ASSERT_EQ(SECONDARY_DISPLAY_ID, args.displayId);
 }
 
-/**
- * Test touch should not work if outside of surface.
- */
-TEST_F(MultiTouchInputMapperTest, Viewports_SurfaceRange) {
-    addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
-    prepareAxes(POSITION);
-    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
-    // Touch on left-top area should work.
-    int32_t rawX = DISPLAY_WIDTH / 2 - 1;
-    int32_t rawY = DISPLAY_HEIGHT / 2 - 1;
-    processPosition(mapper, rawX, rawY);
-    processSync(mapper);
-
-    NotifyMotionArgs args;
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
-
-    // Reset.
-    mapper.reset(ARBITRARY_TIME);
-
-    // Let logical display be different to physical display and rotate 90-degrees.
-    std::optional<DisplayViewport> internalViewport =
-            mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
-    internalViewport->orientation = DISPLAY_ORIENTATION_90;
-    internalViewport->logicalLeft = 0;
-    internalViewport->logicalTop = 0;
-    internalViewport->logicalRight = DISPLAY_HEIGHT;
-    internalViewport->logicalBottom = DISPLAY_WIDTH / 2;
-
-    internalViewport->physicalLeft = DISPLAY_HEIGHT;
-    internalViewport->physicalTop = DISPLAY_WIDTH / 2;
-    internalViewport->physicalRight = DISPLAY_HEIGHT;
-    internalViewport->physicalBottom = DISPLAY_WIDTH;
-
-    internalViewport->deviceWidth = DISPLAY_HEIGHT;
-    internalViewport->deviceHeight = DISPLAY_WIDTH;
-    mFakePolicy->updateViewport(internalViewport.value());
-    configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
-
-    // Display align to right-top after rotate 90-degrees, touch on left-top area should not work.
-    processPosition(mapper, rawX, rawY);
-    processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
-}
 
 TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleSingleTouch) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
@@ -7161,4 +7117,130 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId);
 }
+
+/**
+ * Test touch should not work if outside of surface.
+ */
+class MultiTouchInputMapperTest_SurfaceRange : public MultiTouchInputMapperTest {
+protected:
+    void halfDisplayToCenterHorizontal(int32_t orientation) {
+        std::optional<DisplayViewport> internalViewport =
+                mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+
+        // Half display to (width/4, 0, width * 3/4, height) to make display has offset.
+        internalViewport->orientation = orientation;
+        if (orientation == DISPLAY_ORIENTATION_90 || orientation == DISPLAY_ORIENTATION_270) {
+            internalViewport->logicalLeft = 0;
+            internalViewport->logicalTop = 0;
+            internalViewport->logicalRight = DISPLAY_HEIGHT;
+            internalViewport->logicalBottom = DISPLAY_WIDTH / 2;
+
+            internalViewport->physicalLeft = 0;
+            internalViewport->physicalTop = DISPLAY_WIDTH / 4;
+            internalViewport->physicalRight = DISPLAY_HEIGHT;
+            internalViewport->physicalBottom = DISPLAY_WIDTH * 3 / 4;
+
+            internalViewport->deviceWidth = DISPLAY_HEIGHT;
+            internalViewport->deviceHeight = DISPLAY_WIDTH;
+        } else {
+            internalViewport->logicalLeft = 0;
+            internalViewport->logicalTop = 0;
+            internalViewport->logicalRight = DISPLAY_WIDTH / 2;
+            internalViewport->logicalBottom = DISPLAY_HEIGHT;
+
+            internalViewport->physicalLeft = DISPLAY_WIDTH / 4;
+            internalViewport->physicalTop = 0;
+            internalViewport->physicalRight = DISPLAY_WIDTH * 3 / 4;
+            internalViewport->physicalBottom = DISPLAY_HEIGHT;
+
+            internalViewport->deviceWidth = DISPLAY_WIDTH;
+            internalViewport->deviceHeight = DISPLAY_HEIGHT;
+        }
+
+        mFakePolicy->updateViewport(internalViewport.value());
+        configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    }
+
+    void processPositionAndVerify(MultiTouchInputMapper& mapper, int32_t xInside, int32_t yInside,
+                                  int32_t xOutside, int32_t yOutside, int32_t xExpected,
+                                  int32_t yExpected) {
+        // touch on outside area should not work.
+        processPosition(mapper, toRawX(xOutside), toRawY(yOutside));
+        processSync(mapper);
+        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+        // touch on inside area should receive the event.
+        NotifyMotionArgs args;
+        processPosition(mapper, toRawX(xInside), toRawY(yInside));
+        processSync(mapper);
+        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+        ASSERT_NEAR(xExpected, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+        ASSERT_NEAR(yExpected, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+        // Reset.
+        mapper.reset(ARBITRARY_TIME);
+    }
+};
+
+TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    // Touch on center of normal display should work.
+    const int32_t x = DISPLAY_WIDTH / 4;
+    const int32_t y = DISPLAY_HEIGHT / 2;
+    processPosition(mapper, toRawX(x), toRawY(y));
+    processSync(mapper);
+    NotifyMotionArgs args;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], x, y, 1.0f, 0.0f, 0.0f, 0.0f,
+                                                0.0f, 0.0f, 0.0f, 0.0f));
+    // Reset.
+    mapper.reset(ARBITRARY_TIME);
+
+    // Let physical display be different to device, and make surface and physical could be 1:1.
+    halfDisplayToCenterHorizontal(DISPLAY_ORIENTATION_0);
+
+    const int32_t xExpected = (x + 1) - (DISPLAY_WIDTH / 4);
+    const int32_t yExpected = y;
+    processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
+}
+
+TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_90) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    // Half display to (width/4, 0, width * 3/4, height) and rotate 90-degrees.
+    halfDisplayToCenterHorizontal(DISPLAY_ORIENTATION_90);
+
+    const int32_t x = DISPLAY_WIDTH / 4;
+    const int32_t y = DISPLAY_HEIGHT / 2;
+
+    // expect x/y = swap x/y then reverse y.
+    const int32_t xExpected = y;
+    const int32_t yExpected = (DISPLAY_WIDTH * 3 / 4) - (x + 1);
+    processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
+}
+
+TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_270) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    // Half display to (width/4, 0, width * 3/4, height) and rotate 270-degrees.
+    halfDisplayToCenterHorizontal(DISPLAY_ORIENTATION_270);
+
+    const int32_t x = DISPLAY_WIDTH / 4;
+    const int32_t y = DISPLAY_HEIGHT / 2;
+
+    // expect x/y = swap x/y then reverse x.
+    constexpr int32_t xExpected = DISPLAY_HEIGHT - y;
+    constexpr int32_t yExpected = (x + 1) - DISPLAY_WIDTH / 4;
+    processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
+}
 } // namespace android
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index e4d754c..a32bc2b 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -272,7 +272,7 @@
     // pixel format is HDR Y410 masquerading as RGBA_1010102
     return (mBufferInfo.mDataspace == ui::Dataspace::BT2020_ITU_PQ &&
             mBufferInfo.mApi == NATIVE_WINDOW_API_MEDIA &&
-            mBufferInfo.mBuffer->getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102);
+            mBufferInfo.mPixelFormat == HAL_PIXEL_FORMAT_RGBA_1010102);
 }
 
 sp<compositionengine::LayerFE> BufferLayer::getCompositionEngineLayerFE() const {
@@ -374,6 +374,12 @@
     return true;
 }
 
+void BufferLayer::gatherBufferInfo() {
+    mBufferInfo.mPixelFormat =
+            !mBufferInfo.mBuffer ? PIXEL_FORMAT_NONE : mBufferInfo.mBuffer->format;
+    mBufferInfo.mFrameLatencyNeeded = true;
+}
+
 bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime,
                               nsecs_t expectedPresentTime) {
     ATRACE_CALL();
@@ -434,7 +440,6 @@
     gatherBufferInfo();
 
     mRefreshPending = true;
-    mBufferInfo.mFrameLatencyNeeded = true;
     if (oldBufferInfo.mBuffer == nullptr) {
         // the first time we receive a buffer, we need to trigger a
         // geometry invalidation.
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index f678910..fbec6ee 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -161,7 +161,7 @@
         Region mSurfaceDamage;
         HdrMetadata mHdrMetadata;
         int mApi;
-        PixelFormat mPixelFormat;
+        PixelFormat mPixelFormat{PIXEL_FORMAT_NONE};
         bool mTransformToDisplayInverse{false};
 
         sp<GraphicBuffer> mBuffer;
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 4e5c593..c84b15d 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -525,8 +525,6 @@
         return BAD_VALUE;
     }
 
-    mFormat = format;
-
     setDefaultBufferSize(w, h);
     mConsumer->setDefaultBufferFormat(format);
     mConsumer->setConsumerUsageBits(getEffectiveUsage(0));
@@ -550,6 +548,8 @@
 }
 
 void BufferQueueLayer::gatherBufferInfo() {
+    BufferLayer::gatherBufferInfo();
+
     mBufferInfo.mDesiredPresentTime = mConsumer->getTimestamp();
     mBufferInfo.mFenceTime = mConsumer->getCurrentFenceTime();
     mBufferInfo.mFence = mConsumer->getCurrentFence();
@@ -560,7 +560,6 @@
     mBufferInfo.mSurfaceDamage = mConsumer->getSurfaceDamage();
     mBufferInfo.mHdrMetadata = mConsumer->getCurrentHdrMetadata();
     mBufferInfo.mApi = mConsumer->getCurrentApi();
-    mBufferInfo.mPixelFormat = mFormat;
     mBufferInfo.mTransformToDisplayInverse = mConsumer->getTransformToDisplayInverse();
 }
 
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 5f7587c..ea7f203 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -132,8 +132,6 @@
     sp<BufferLayerConsumer> mConsumer;
     sp<IGraphicBufferProducer> mProducer;
 
-    PixelFormat mFormat{PIXEL_FORMAT_NONE};
-
     bool mUpdateTexImageFailed{false};
 
     uint64_t mPreviousBufferId = 0;
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 3ed6889..3e65171 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -718,8 +718,9 @@
 }
 
 void BufferStateLayer::gatherBufferInfo() {
-    const State& s(getDrawingState());
+    BufferLayer::gatherBufferInfo();
 
+    const State& s(getDrawingState());
     mBufferInfo.mDesiredPresentTime = s.desiredPresentTime;
     mBufferInfo.mFenceTime = std::make_shared<FenceTime>(s.acquireFence);
     mBufferInfo.mFence = s.acquireFence;
@@ -730,8 +731,6 @@
     mBufferInfo.mSurfaceDamage = s.surfaceDamageRegion;
     mBufferInfo.mHdrMetadata = s.hdrMetadata;
     mBufferInfo.mApi = s.api;
-    mBufferInfo.mPixelFormat =
-            !mBufferInfo.mBuffer ? PIXEL_FORMAT_NONE : mBufferInfo.mBuffer->format;
     mBufferInfo.mTransformToDisplayInverse = s.transformToDisplayInverse;
     mBufferInfo.mBufferSlot = mHwcSlotGenerator->getHwcCacheSlot(s.clientCacheId);
 }
diff --git a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
index acaaf4e..2d9f01b 100644
--- a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
@@ -35,7 +35,8 @@
     return lhs.geometry == rhs.geometry && lhs.alpha == rhs.alpha &&
             lhs.sourceDataspace == rhs.sourceDataspace &&
             lhs.colorTransform == rhs.colorTransform &&
-            lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow;
+            lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow &&
+            lhs.backgroundBlurRadius == rhs.backgroundBlurRadius;
 }
 
 inline bool equalIgnoringBuffer(const renderengine::Buffer& lhs, const renderengine::Buffer& rhs) {
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 02226ab..88f6649 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -45,6 +45,7 @@
 
     MOCK_METHOD3(allocateVirtualDisplay,
                  std::optional<DisplayId>(uint32_t, uint32_t, ui::PixelFormat*));
+    MOCK_METHOD2(allocatePhysicalDisplay, void(hwc2_display_t, DisplayId));
     MOCK_METHOD1(createLayer, HWC2::Layer*(DisplayId));
     MOCK_METHOD2(destroyLayer, void(DisplayId, HWC2::Layer*));
     MOCK_METHOD3(getDeviceCompositionChanges,
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
index e740b13..b738096 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
@@ -29,6 +29,7 @@
     PowerAdvisor();
     ~PowerAdvisor() override;
 
+    MOCK_METHOD0(onBootFinished, void());
     MOCK_METHOD2(setExpensiveRenderingExpected, void(DisplayId displayId, bool expected));
     MOCK_METHOD0(notifyDisplayUpdateImminent, void());
 };
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index fb6c817..397fedc 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -183,9 +183,10 @@
     struct Physical {
         DisplayId id;
         DisplayConnectionType type;
+        hwc2_display_t hwcDisplayId;
 
         bool operator==(const Physical& other) const {
-            return id == other.id && type == other.type;
+            return id == other.id && type == other.type && hwcDisplayId == other.hwcDisplayId;
         }
     };
 
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index aeffb0e..4c3b3e5 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -183,10 +183,7 @@
 }
 
 ui::Size FramebufferSurface::limitFramebufferSize(uint32_t width, uint32_t height) {
-    // TODO(b/149495759): Use the ui::Size constructor once it no longer is broken.
-    ui::Size framebufferSize;
-    framebufferSize.width = width;
-    framebufferSize.height = height;
+    ui::Size framebufferSize(width, height);
     bool wasLimited = true;
     if (width > mMaxWidth && mMaxWidth != 0) {
         float aspectRatio = float(width) / float(height);
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 1c1e113..560299a 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -205,56 +205,14 @@
 
 std::optional<DisplayIdentificationInfo> HWComposer::onHotplug(hwc2_display_t hwcDisplayId,
                                                                HWC2::Connection connection) {
-    std::optional<DisplayIdentificationInfo> info;
-
-    if (const auto displayId = toPhysicalDisplayId(hwcDisplayId)) {
-        info = DisplayIdentificationInfo{.id = *displayId,
-                                         .name = std::string(),
-                                         .deviceProductInfo = std::nullopt};
-    } else {
-        if (connection == HWC2::Connection::Disconnected) {
-            ALOGE("Ignoring disconnection of invalid HWC display %" PRIu64, hwcDisplayId);
+    switch (connection) {
+        case HWC2::Connection::Connected:
+            return onHotplugConnect(hwcDisplayId);
+        case HWC2::Connection::Disconnected:
+            return onHotplugDisconnect(hwcDisplayId);
+        case HWC2::Connection::Invalid:
             return {};
-        }
-
-        info = onHotplugConnect(hwcDisplayId);
-        if (!info) return {};
     }
-
-    ALOGV("%s: %s %s display %s with HWC ID %" PRIu64, __FUNCTION__, to_string(connection).c_str(),
-          hwcDisplayId == mInternalHwcDisplayId ? "internal" : "external",
-          to_string(info->id).c_str(), hwcDisplayId);
-
-    if (connection == HWC2::Connection::Connected) {
-        auto& displayData = mDisplayData[info->id];
-        // If we get a hotplug connected event for a display we already have,
-        // destroy the display and recreate it. This will force us to requery
-        // the display params and recreate all layers on that display.
-        if (displayData.hwcDisplay != nullptr && displayData.hwcDisplay->isConnected()) {
-            ALOGI("Hotplug connecting an already connected display."
-                  " Clearing old display state.");
-        }
-        displayData.hwcDisplay.reset();
-        auto newDisplay =
-                std::make_unique<HWC2::impl::Display>(*mComposer.get(), mCapabilities, hwcDisplayId,
-                                                      HWC2::DisplayType::Physical);
-        newDisplay->setConnected(true);
-        displayData.hwcDisplay = std::move(newDisplay);
-        mPhysicalDisplayIdMap[hwcDisplayId] = info->id;
-    } else if (connection == HWC2::Connection::Disconnected) {
-        // The display will later be destroyed by a call to
-        // destroyDisplay(). For now we just mark it disconnected.
-        auto& displayData = mDisplayData[info->id];
-        if (displayData.hwcDisplay) {
-            displayData.hwcDisplay->setConnected(false);
-        } else {
-            ALOGW("Attempted to disconnect unknown display %" PRIu64, hwcDisplayId);
-        }
-        // The cleanup of Disconnect is handled through HWComposer::disconnectDisplay
-        // via SurfaceFlinger's onHotplugReceived callback handling
-    }
-
-    return info;
 }
 
 bool HWComposer::onVsync(hwc2_display_t hwcDisplayId, int64_t timestamp) {
@@ -337,6 +295,22 @@
     return displayId;
 }
 
+void HWComposer::allocatePhysicalDisplay(hwc2_display_t hwcDisplayId, DisplayId displayId) {
+    if (!mInternalHwcDisplayId) {
+        mInternalHwcDisplayId = hwcDisplayId;
+    } else if (mInternalHwcDisplayId != hwcDisplayId && !mExternalHwcDisplayId) {
+        mExternalHwcDisplayId = hwcDisplayId;
+    }
+
+    auto& displayData = mDisplayData[displayId];
+    auto newDisplay =
+            std::make_unique<HWC2::impl::Display>(*mComposer.get(), mCapabilities, hwcDisplayId,
+                                                  HWC2::DisplayType::Physical);
+    newDisplay->setConnected(true);
+    displayData.hwcDisplay = std::move(newDisplay);
+    mPhysicalDisplayIdMap[hwcDisplayId] = displayId;
+}
+
 HWC2::Layer* HWComposer::createLayer(DisplayId displayId) {
     RETURN_IF_INVALID_DISPLAY(displayId, nullptr);
 
@@ -911,52 +885,93 @@
     return {};
 }
 
-std::optional<DisplayIdentificationInfo> HWComposer::onHotplugConnect(hwc2_display_t hwcDisplayId) {
+bool HWComposer::shouldIgnoreHotplugConnect(hwc2_display_t hwcDisplayId,
+                                            bool hasDisplayIdentificationData) const {
     if (isUsingVrComposer() && mInternalHwcDisplayId) {
         ALOGE("Ignoring connection of external display %" PRIu64 " in VR mode", hwcDisplayId);
-        return {};
+        return true;
     }
 
-    uint8_t port;
-    DisplayIdentificationData data;
-    const bool hasMultiDisplaySupport = getDisplayIdentificationData(hwcDisplayId, &port, &data);
-
-    if (mPhysicalDisplayIdMap.empty()) {
-        mHasMultiDisplaySupport = hasMultiDisplaySupport;
-        ALOGI("Switching to %s multi-display mode",
-              hasMultiDisplaySupport ? "generalized" : "legacy");
-    } else if (mHasMultiDisplaySupport && !hasMultiDisplaySupport) {
+    if (mHasMultiDisplaySupport && !hasDisplayIdentificationData) {
         ALOGE("Ignoring connection of display %" PRIu64 " without identification data",
               hwcDisplayId);
-        return {};
+        return true;
     }
 
-    std::optional<DisplayIdentificationInfo> info;
-
-    if (mHasMultiDisplaySupport) {
-        info = parseDisplayIdentificationData(port, data);
-        ALOGE_IF(!info, "Failed to parse identification data for display %" PRIu64, hwcDisplayId);
-    } else if (mInternalHwcDisplayId && mExternalHwcDisplayId) {
+    if (!mHasMultiDisplaySupport && mInternalHwcDisplayId && mExternalHwcDisplayId) {
         ALOGE("Ignoring connection of tertiary display %" PRIu64, hwcDisplayId);
-        return {};
+        return true;
+    }
+
+    return false;
+}
+
+std::optional<DisplayIdentificationInfo> HWComposer::onHotplugConnect(hwc2_display_t hwcDisplayId) {
+    std::optional<DisplayIdentificationInfo> info;
+    if (const auto displayId = toPhysicalDisplayId(hwcDisplayId)) {
+        info = DisplayIdentificationInfo{.id = *displayId,
+                                         .name = std::string(),
+                                         .deviceProductInfo = std::nullopt};
     } else {
-        ALOGW_IF(hasMultiDisplaySupport, "Ignoring identification data for display %" PRIu64,
-                 hwcDisplayId);
-        port = mInternalHwcDisplayId ? HWC_DISPLAY_EXTERNAL : HWC_DISPLAY_PRIMARY;
+        uint8_t port;
+        DisplayIdentificationData data;
+        const bool hasDisplayIdentificationData =
+                getDisplayIdentificationData(hwcDisplayId, &port, &data);
+        if (mPhysicalDisplayIdMap.empty()) {
+            mHasMultiDisplaySupport = hasDisplayIdentificationData;
+            ALOGI("Switching to %s multi-display mode",
+                  mHasMultiDisplaySupport ? "generalized" : "legacy");
+        }
+
+        if (shouldIgnoreHotplugConnect(hwcDisplayId, hasDisplayIdentificationData)) {
+            return {};
+        }
+
+        info = [this, hwcDisplayId, &port, &data, hasDisplayIdentificationData] {
+            const bool isPrimary = !mInternalHwcDisplayId;
+            if (mHasMultiDisplaySupport) {
+                if (const auto info = parseDisplayIdentificationData(port, data)) {
+                    return *info;
+                }
+                ALOGE("Failed to parse identification data for display %" PRIu64, hwcDisplayId);
+            } else {
+                ALOGW_IF(hasDisplayIdentificationData,
+                         "Ignoring identification data for display %" PRIu64, hwcDisplayId);
+                port = isPrimary ? HWC_DISPLAY_PRIMARY : HWC_DISPLAY_EXTERNAL;
+            }
+
+            return DisplayIdentificationInfo{.id = getFallbackDisplayId(port),
+                                             .name = isPrimary ? "Internal display"
+                                                               : "External display",
+                                             .deviceProductInfo = std::nullopt};
+        }();
     }
 
-    if (!mInternalHwcDisplayId) {
-        mInternalHwcDisplayId = hwcDisplayId;
-    } else if (!mExternalHwcDisplayId) {
-        mExternalHwcDisplayId = hwcDisplayId;
+    if (!isConnected(info->id)) {
+        allocatePhysicalDisplay(hwcDisplayId, info->id);
+    }
+    return info;
+}
+
+std::optional<DisplayIdentificationInfo> HWComposer::onHotplugDisconnect(
+        hwc2_display_t hwcDisplayId) {
+    const auto displayId = toPhysicalDisplayId(hwcDisplayId);
+    if (!displayId) {
+        ALOGE("Ignoring disconnection of invalid HWC display %" PRIu64, hwcDisplayId);
+        return {};
     }
 
-    if (info) return info;
-
-    return DisplayIdentificationInfo{.id = getFallbackDisplayId(port),
-                                     .name = hwcDisplayId == mInternalHwcDisplayId
-                                             ? "Internal display"
-                                             : "External display",
+    // The display will later be destroyed by a call to
+    // destroyDisplay(). For now we just mark it disconnected.
+    if (isConnected(*displayId)) {
+        mDisplayData[*displayId].hwcDisplay->setConnected(false);
+    } else {
+        ALOGW("Attempted to disconnect unknown display %" PRIu64, hwcDisplayId);
+    }
+    // The cleanup of Disconnect is handled through HWComposer::disconnectDisplay
+    // via SurfaceFlinger's onHotplugReceived callback handling
+    return DisplayIdentificationInfo{.id = *displayId,
+                                     .name = std::string(),
                                      .deviceProductInfo = std::nullopt};
 }
 
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 41db501..3cb40b1 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -77,6 +77,8 @@
     virtual std::optional<DisplayId> allocateVirtualDisplay(uint32_t width, uint32_t height,
                                                             ui::PixelFormat* format) = 0;
 
+    virtual void allocatePhysicalDisplay(hwc2_display_t hwcDisplayId, DisplayId displayId) = 0;
+
     // Attempts to create a new layer on this display
     virtual HWC2::Layer* createLayer(DisplayId displayId) = 0;
     // Destroy a previously created layer
@@ -164,6 +166,7 @@
 
     // Returns stable display ID (and display name on connection of new or previously disconnected
     // display), or std::nullopt if hotplug event was ignored.
+    // This function is called from SurfaceFlinger.
     virtual std::optional<DisplayIdentificationInfo> onHotplug(hwc2_display_t hwcDisplayId,
                                                                HWC2::Connection connection) = 0;
 
@@ -238,6 +241,9 @@
     std::optional<DisplayId> allocateVirtualDisplay(uint32_t width, uint32_t height,
                                                     ui::PixelFormat* format) override;
 
+    // Called from SurfaceFlinger, when the state for a new physical display needs to be recreated.
+    void allocatePhysicalDisplay(hwc2_display_t hwcDisplayId, DisplayId displayId) override;
+
     // Attempts to create a new layer on this display
     HWC2::Layer* createLayer(DisplayId displayId) override;
     // Destroy a previously created layer
@@ -361,6 +367,10 @@
     friend TestableSurfaceFlinger;
 
     std::optional<DisplayIdentificationInfo> onHotplugConnect(hwc2_display_t hwcDisplayId);
+    std::optional<DisplayIdentificationInfo> onHotplugDisconnect(hwc2_display_t hwcDisplayId);
+    bool shouldIgnoreHotplugConnect(hwc2_display_t hwcDisplayId,
+                                    bool hasDisplayIdentificationData) const;
+
     void loadCapabilities();
     void loadLayerMetadataSupport();
     uint32_t getMaxVirtualDisplayCount() const;
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 06e0cbb..1d8179c 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -72,6 +72,10 @@
     }
 }
 
+void PowerAdvisor::onBootFinished() {
+    mBootFinished.store(true);
+}
+
 void PowerAdvisor::setExpensiveRenderingExpected(DisplayId displayId, bool expected) {
     if (expected) {
         mExpensiveDisplays.insert(displayId);
@@ -97,6 +101,12 @@
 }
 
 void PowerAdvisor::notifyDisplayUpdateImminent() {
+    // Only start sending this notification once the system has booted so we don't introduce an
+    // early-boot dependency on Power HAL
+    if (!mBootFinished.load()) {
+        return;
+    }
+
     if (mSendUpdateImminent.load()) {
         HalWrapper* const halWrapper = getPowerHal();
         if (halWrapper == nullptr) {
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 957d289..4a90acb 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -35,6 +35,7 @@
 public:
     virtual ~PowerAdvisor();
 
+    virtual void onBootFinished() = 0;
     virtual void setExpensiveRenderingExpected(DisplayId displayId, bool expected) = 0;
     virtual void notifyDisplayUpdateImminent() = 0;
 };
@@ -56,12 +57,14 @@
     PowerAdvisor();
     ~PowerAdvisor() override;
 
+    void onBootFinished() override;
     void setExpensiveRenderingExpected(DisplayId displayId, bool expected) override;
     void notifyDisplayUpdateImminent() override;
 
 private:
     HalWrapper* getPowerHal();
 
+    std::atomic_bool mBootFinished = false;
     bool mReconnectPowerHal = false;
 
     std::unordered_set<DisplayId> mExpensiveDisplays;
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
index 776e984..4e3f85f 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
@@ -30,6 +30,7 @@
 #include "EventThread.h"
 
 namespace android {
+using base::StringAppendF;
 
 DispSyncSource::DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync,
                                const char* name)
@@ -107,6 +108,12 @@
     }
 }
 
+void DispSyncSource::dump(std::string& result) const {
+    std::lock_guard lock(mVsyncMutex);
+    StringAppendF(&result, "DispSyncSource: %s(%s)\n", mName, mEnabled ? "enabled" : "disabled");
+    mDispSync->dump(result);
+}
+
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.h b/services/surfaceflinger/Scheduler/DispSyncSource.h
index 328b8c1..f278712 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.h
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.h
@@ -36,6 +36,8 @@
     void setCallback(VSyncSource::Callback* callback) override;
     void setPhaseOffset(nsecs_t phaseOffset) override;
 
+    void dump(std::string&) const override;
+
 private:
     // The following method is the implementation of the DispSync::Callback.
     virtual void onDispSyncEvent(nsecs_t when);
@@ -52,7 +54,7 @@
     std::mutex mCallbackMutex;
     VSyncSource::Callback* mCallback GUARDED_BY(mCallbackMutex) = nullptr;
 
-    std::mutex mVsyncMutex;
+    mutable std::mutex mVsyncMutex;
     TracedOrdinal<nsecs_t> mPhaseOffset GUARDED_BY(mVsyncMutex);
     bool mEnabled GUARDED_BY(mVsyncMutex) = false;
 };
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index acab5a6..0d6a92e 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -425,7 +425,12 @@
             // display is off, keep feeding clients at 60 Hz.
             const auto timeout = mState == State::SyntheticVSync ? 16ms : 1000ms;
             if (mCondition.wait_for(lock, timeout) == std::cv_status::timeout) {
-                ALOGW_IF(mState == State::VSync, "Faking VSYNC due to driver stall");
+                if (mState == State::VSync) {
+                    ALOGW("Faking VSYNC due to driver stall for thread %s", mThreadName);
+                    std::string debugInfo = "VsyncSource debug info:\n";
+                    mVSyncSource->dump(debugInfo);
+                    ALOGW("%s", debugInfo.c_str());
+                }
 
                 LOG_FATAL_IF(!mVSyncState);
                 mPendingEvents.push_back(makeVSync(mVSyncState->displayId,
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 466234d..98b1876 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -66,6 +66,8 @@
     virtual void setVSyncEnabled(bool enable) = 0;
     virtual void setCallback(Callback* callback) = 0;
     virtual void setPhaseOffset(nsecs_t phaseOffset) = 0;
+
+    virtual void dump(std::string& result) const = 0;
 };
 
 class EventThreadConnection : public BnDisplayEventConnection {
diff --git a/services/surfaceflinger/Scheduler/InjectVSyncSource.h b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
index fa46e6f..31da588 100644
--- a/services/surfaceflinger/Scheduler/InjectVSyncSource.h
+++ b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
@@ -45,6 +45,7 @@
     const char* getName() const override { return "inject"; }
     void setVSyncEnabled(bool) override {}
     void setPhaseOffset(nsecs_t) override {}
+    void dump(std::string&) const override {}
 
 private:
     std::mutex mCallbackMutex;
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
index c04447d..43883fb 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -36,6 +36,19 @@
     return std::abs(fpsA - fpsB) <= MARGIN;
 }
 
+std::vector<float> getRefreshRatesFromConfigs(
+        const android::scheduler::RefreshRateConfigs& refreshRateConfigs) {
+    const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates();
+    std::vector<float> refreshRates;
+    refreshRates.reserve(allRefreshRates.size());
+
+    for (const auto& [ignored, refreshRate] : allRefreshRates) {
+        refreshRates.emplace_back(refreshRate->fps);
+    }
+
+    return refreshRates;
+}
+
 } // namespace
 
 namespace android::scheduler {
@@ -45,14 +58,21 @@
 namespace impl {
 
 PhaseOffsets::PhaseOffsets(const scheduler::RefreshRateConfigs& refreshRateConfigs)
-      : // Below defines the threshold when an offset is considered to be negative, i.e. targeting
-        // for the N+2 vsync instead of N+1. This means that:
-        // For offset < threshold, SF wake up (vsync_duration - offset) before HW vsync.
-        // For offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW vsync.
-        mThresholdForNextVsync(getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
-                                       .value_or(std::numeric_limits<nsecs_t>::max())),
-        mOffsets(initializeOffsets(refreshRateConfigs)),
-        mRefreshRateFps(refreshRateConfigs.getCurrentRefreshRate().fps) {}
+      : PhaseOffsets(getRefreshRatesFromConfigs(refreshRateConfigs),
+                     refreshRateConfigs.getCurrentRefreshRate().fps,
+                     // Below defines the threshold when an offset is considered to be negative,
+                     // i.e. targeting for the N+2 vsync instead of N+1. This means that: For offset
+                     // < threshold, SF wake up (vsync_duration - offset) before HW vsync. For
+                     // offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW
+                     // vsync.
+                     getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
+                             .value_or(std::numeric_limits<nsecs_t>::max())) {}
+
+PhaseOffsets::PhaseOffsets(const std::vector<float>& refreshRates, float currentFps,
+                           nsecs_t thresholdForNextVsync)
+      : mThresholdForNextVsync(thresholdForNextVsync),
+        mOffsets(initializeOffsets(refreshRates)),
+        mRefreshRateFps(currentFps) {}
 
 void PhaseOffsets::dump(std::string& result) const {
     const auto [early, earlyGl, late] = getCurrentOffsets();
@@ -67,19 +87,24 @@
 }
 
 std::unordered_map<float, PhaseOffsets::Offsets> PhaseOffsets::initializeOffsets(
-        const scheduler::RefreshRateConfigs& refreshRateConfigs) const {
+        const std::vector<float>& refreshRates) const {
     std::unordered_map<float, Offsets> offsets;
 
-    for (const auto& [ignored, refreshRate] : refreshRateConfigs.getAllRefreshRates()) {
-        if (refreshRate->fps > 65.0f) {
-            offsets.emplace(refreshRate->fps, getHighFpsOffsets(refreshRate->vsyncPeriod));
-        } else {
-            offsets.emplace(refreshRate->fps, getDefaultOffsets(refreshRate->vsyncPeriod));
-        }
+    for (const auto& refreshRate : refreshRates) {
+        offsets.emplace(refreshRate,
+                        getPhaseOffsets(refreshRate, static_cast<nsecs_t>(1e9f / refreshRate)));
     }
     return offsets;
 }
 
+PhaseOffsets::Offsets PhaseOffsets::getPhaseOffsets(float fps, nsecs_t vsyncPeriod) const {
+    if (fps > 65.0f) {
+        return getHighFpsOffsets(vsyncPeriod);
+    } else {
+        return getDefaultOffsets(vsyncPeriod);
+    }
+}
+
 PhaseOffsets::Offsets PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const {
     const int64_t vsyncPhaseOffsetNs = sysprop::vsync_event_phase_offset_ns(1000000);
     const int64_t sfVsyncPhaseOffsetNs = sysprop::vsync_sf_event_phase_offset_ns(1000000);
@@ -159,8 +184,15 @@
                                    [&fps](const std::pair<float, Offsets>& candidateFps) {
                                        return fpsEqualsWithMargin(fps, candidateFps.first);
                                    });
-    LOG_ALWAYS_FATAL_IF(iter == mOffsets.end());
-    return iter->second;
+
+    if (iter != mOffsets.end()) {
+        return iter->second;
+    }
+
+    // Unknown refresh rate. This might happen if we get a hotplug event for an external display.
+    // In this case just construct the offset.
+    ALOGW("Can't find offset for %.2f fps", fps);
+    return getPhaseOffsets(fps, static_cast<nsecs_t>(1e9f / fps));
 }
 
 static void validateSysprops() {
@@ -231,19 +263,6 @@
     };
 }
 
-static std::vector<float> getRefreshRatesFromConfigs(
-        const android::scheduler::RefreshRateConfigs& refreshRateConfigs) {
-    const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates();
-    std::vector<float> refreshRates;
-    refreshRates.reserve(allRefreshRates.size());
-
-    for (const auto& [ignored, refreshRate] : allRefreshRates) {
-        refreshRates.emplace_back(refreshRate->fps);
-    }
-
-    return refreshRates;
-}
-
 std::unordered_map<float, PhaseDurations::Offsets> PhaseDurations::initializeOffsets(
         const std::vector<float>& refreshRates) const {
     std::unordered_map<float, Offsets> offsets;
@@ -288,8 +307,7 @@
         return iter->second;
     }
 
-    // Unknown refresh rate. This might happen if we get a hotplug event for the default display.
-    // This happens only during tests and not during regular device operation.
+    // Unknown refresh rate. This might happen if we get a hotplug event for an external display.
     // In this case just construct the offset.
     ALOGW("Can't find offset for %.2f fps", fps);
     return constructOffsets(static_cast<nsecs_t>(1e9f / fps));
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h
index b7d4eae..fa8011d 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.h
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.h
@@ -66,11 +66,15 @@
     // Returns current offsets in human friendly format.
     void dump(std::string& result) const override;
 
-private:
+protected:
+    // Used for unit tests
+    PhaseOffsets(const std::vector<float>& refreshRates, float currentFps,
+                 nsecs_t thresholdForNextVsync);
     std::unordered_map<float, Offsets> initializeOffsets(
-            const scheduler::RefreshRateConfigs&) const;
-    Offsets getDefaultOffsets(nsecs_t vsyncDuration) const;
-    Offsets getHighFpsOffsets(nsecs_t vsyncDuration) const;
+            const std::vector<float>& refreshRates) const;
+    Offsets getDefaultOffsets(nsecs_t vsyncPeriod) const;
+    Offsets getHighFpsOffsets(nsecs_t vsyncPeriod) const;
+    Offsets getPhaseOffsets(float fps, nsecs_t vsyncPeriod) const;
 
     const nsecs_t mThresholdForNextVsync;
     const std::unordered_map<float, Offsets> mOffsets;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 920f0ec..cd6075f 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -419,20 +419,6 @@
                                          mRefreshRateConfigs.getMaxRefreshRate().fps,
                                          scheduler::LayerHistory::LayerVoteType::Heuristic);
         }
-
-        // TODO(146935143): Simulate youtube app vote. This should be removed once youtube calls the
-        // API to set desired rate
-        {
-            const auto vote = property_get_int32("experimental.sf.force_youtube_vote", 0);
-            if (vote != 0 &&
-                layer->getName() ==
-                        "SurfaceView - "
-                        "com.google.android.youtube/"
-                        "com.google.android.apps.youtube.app.WatchWhileActivity#0") {
-                layer->setFrameRate(
-                        Layer::FrameRate(vote, Layer::FrameRateCompatibility::ExactOrMultiple));
-            }
-        }
     }
 }
 
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index a6fb3a4..2a2d7c5 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -108,6 +108,8 @@
      */
     virtual CancelResult cancel(CallbackToken token) = 0;
 
+    virtual void dump(std::string& result) const = 0;
+
 protected:
     VSyncDispatch() = default;
     VSyncDispatch(VSyncDispatch const&) = delete;
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index d0f18ab..460d4a5 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -15,6 +15,7 @@
  */
 
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <android-base/stringprintf.h>
 #include <utils/Trace.h>
 #include <vector>
 
@@ -23,6 +24,7 @@
 #include "VSyncTracker.h"
 
 namespace android::scheduler {
+using base::StringAppendF;
 
 VSyncDispatch::~VSyncDispatch() = default;
 VSyncTracker::~VSyncTracker() = default;
@@ -122,6 +124,28 @@
     mCv.wait(lk, [this]() REQUIRES(mRunningMutex) { return !mRunning; });
 }
 
+void VSyncDispatchTimerQueueEntry::dump(std::string& result) const {
+    std::lock_guard<std::mutex> lk(mRunningMutex);
+    std::string armedInfo;
+    if (mArmedInfo) {
+        StringAppendF(&armedInfo, "[wake up in %.2fms for vsync %.2fms from now]",
+                      (mArmedInfo->mActualWakeupTime - systemTime()) / 1e6f,
+                      (mArmedInfo->mActualVsyncTime - systemTime()) / 1e6f);
+    }
+
+    StringAppendF(&result, "\t\t%s: %s %s\n", mName.c_str(),
+                  mRunning ? "(in callback function)" : "", armedInfo.c_str());
+    StringAppendF(&result, "\t\t\tmWorkDuration: %.2fms mEarliestVsync: %.2fms relative to now\n",
+                  mWorkDuration / 1e6f, (mEarliestVsync - systemTime()) / 1e6f);
+
+    if (mLastDispatchTime) {
+        StringAppendF(&result, "\t\t\tmLastDispatchTime: %.2fms ago\n",
+                      (systemTime() - *mLastDispatchTime) / 1e6f);
+    } else {
+        StringAppendF(&result, "\t\t\tmLastDispatchTime unknown\n");
+    }
+}
+
 VSyncDispatchTimerQueue::VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk,
                                                  VSyncTracker& tracker, nsecs_t timerSlack,
                                                  nsecs_t minVsyncDistance)
@@ -296,6 +320,18 @@
     return CancelResult::TooLate;
 }
 
+void VSyncDispatchTimerQueue::dump(std::string& result) const {
+    std::lock_guard<decltype(mMutex)> lk(mMutex);
+    StringAppendF(&result, "\tmTimerSlack: %.2fms mMinVsyncDistance: %.2fms\n", mTimerSlack / 1e6f,
+                  mMinVsyncDistance / 1e6f);
+    StringAppendF(&result, "\tmIntendedWakeupTime: %.2fms from now\n",
+                  (mIntendedWakeupTime - systemTime()) / 1e6f);
+    StringAppendF(&result, "\tCallbacks:\n");
+    for (const auto& [token, entry] : mCallbacks) {
+        entry->dump(result);
+    }
+}
+
 VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncDispatch& dispatch,
                                                      VSyncDispatch::Callback const& callbackFn,
                                                      std::string const& callbackName)
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index fd0a034..390e0c4 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -71,6 +71,8 @@
     // Block calling thread while the callback is executing.
     void ensureNotRunning();
 
+    void dump(std::string& result) const;
+
 private:
     std::string const mName;
     VSyncDispatch::Callback const mCallback;
@@ -86,7 +88,7 @@
     std::optional<ArmingInfo> mArmedInfo;
     std::optional<nsecs_t> mLastDispatchTime;
 
-    std::mutex mRunningMutex;
+    mutable std::mutex mRunningMutex;
     std::condition_variable mCv;
     bool mRunning GUARDED_BY(mRunningMutex) = false;
 };
@@ -112,6 +114,7 @@
     void unregisterCallback(CallbackToken token) final;
     ScheduleResult schedule(CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync) final;
     CancelResult cancel(CallbackToken token) final;
+    void dump(std::string& result) const final;
 
 private:
     VSyncDispatchTimerQueue(VSyncDispatchTimerQueue const&) = delete;
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 257b8b1..a3cb772 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -22,6 +22,7 @@
 //#define LOG_NDEBUG 0
 #include "VSyncPredictor.h"
 #include <android-base/logging.h>
+#include <android-base/stringprintf.h>
 #include <cutils/compiler.h>
 #include <cutils/properties.h>
 #include <utils/Log.h>
@@ -31,6 +32,7 @@
 #include <sstream>
 
 namespace android::scheduler {
+using base::StringAppendF;
 
 static auto constexpr kMaxPercent = 100u;
 
@@ -263,6 +265,18 @@
     clearTimestamps();
 }
 
+void VSyncPredictor::dump(std::string& result) const {
+    std::lock_guard<std::mutex> lk(mMutex);
+    StringAppendF(&result, "\tmIdealPeriod=%.2f\n", mIdealPeriod / 1e6f);
+    StringAppendF(&result, "\tRefresh Rate Map:\n");
+    for (const auto& [idealPeriod, periodInterceptTuple] : mRateMap) {
+        StringAppendF(&result,
+                      "\t\tFor ideal period %.2fms: period = %.2fms, intercept = %" PRId64 "\n",
+                      idealPeriod / 1e6f, std::get<0>(periodInterceptTuple) / 1e6f,
+                      std::get<1>(periodInterceptTuple));
+    }
+}
+
 } // namespace android::scheduler
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index ef1d88a..3ca878d 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -60,6 +60,8 @@
 
     std::tuple<nsecs_t /* slope */, nsecs_t /* intercept */> getVSyncPredictionModel() const;
 
+    void dump(std::string& result) const final;
+
 private:
     VSyncPredictor(VSyncPredictor const&) = delete;
     VSyncPredictor& operator=(VSyncPredictor const&) = delete;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 8987dcd..892ae62 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -28,6 +28,7 @@
 #include "VSyncTracker.h"
 
 namespace android::scheduler {
+using base::StringAppendF;
 
 Clock::~Clock() = default;
 nsecs_t SystemClock::now() const {
@@ -73,11 +74,12 @@
 public:
     CallbackRepeater(VSyncDispatch& dispatch, DispSync::Callback* cb, const char* name,
                      nsecs_t period, nsecs_t offset, nsecs_t notBefore)
-          : mCallback(cb),
+          : mName(name),
+            mCallback(cb),
             mRegistration(dispatch,
                           std::bind(&CallbackRepeater::callback, this, std::placeholders::_1,
                                     std::placeholders::_2),
-                          std::string(name)),
+                          mName),
             mPeriod(period),
             mOffset(offset),
             mLastCallTime(notBefore) {}
@@ -112,6 +114,13 @@
         mRegistration.cancel();
     }
 
+    void dump(std::string& result) const {
+        std::lock_guard<std::mutex> lk(mMutex);
+        StringAppendF(&result, "\t%s: mPeriod=%.2f last vsync time %.2fms relative to now (%s)\n",
+                      mName.c_str(), mPeriod / 1e6f, (mLastCallTime - systemTime()) / 1e6f,
+                      mStopped ? "stopped" : "running");
+    }
+
 private:
     void callback(nsecs_t vsynctime, nsecs_t wakeupTime) {
         {
@@ -137,6 +146,7 @@
     // Note change in sign between the two defnitions.
     nsecs_t calculateWorkload() REQUIRES(mMutex) { return mPeriod - mOffset; }
 
+    const std::string mName;
     DispSync::Callback* const mCallback;
 
     std::mutex mutable mMutex;
@@ -349,7 +359,35 @@
 }
 
 void VSyncReactor::dump(std::string& result) const {
-    result += "VsyncReactor in use\n"; // TODO (b/144927823): add more information!
+    std::lock_guard<std::mutex> lk(mMutex);
+    StringAppendF(&result, "VsyncReactor in use\n");
+    StringAppendF(&result, "Has %zu unfired fences\n", mUnfiredFences.size());
+    StringAppendF(&result, "mInternalIgnoreFences=%d mExternalIgnoreFences=%d\n",
+                  mInternalIgnoreFences, mExternalIgnoreFences);
+    StringAppendF(&result, "mMoreSamplesNeeded=%d mPeriodConfirmationInProgress=%d\n",
+                  mMoreSamplesNeeded, mPeriodConfirmationInProgress);
+    if (mPeriodTransitioningTo) {
+        StringAppendF(&result, "mPeriodTransitioningTo=%" PRId64 "\n", *mPeriodTransitioningTo);
+    } else {
+        StringAppendF(&result, "mPeriodTransitioningTo=nullptr\n");
+    }
+
+    if (mLastHwVsync) {
+        StringAppendF(&result, "Last HW vsync was %.2fms ago\n",
+                      (mClock->now() - *mLastHwVsync) / 1e6f);
+    } else {
+        StringAppendF(&result, "No Last HW vsync\n");
+    }
+
+    StringAppendF(&result, "CallbackRepeaters:\n");
+    for (const auto& [callback, repeater] : mCallbacks) {
+        repeater->dump(result);
+    }
+
+    StringAppendF(&result, "VSyncTracker:\n");
+    mTracker->dump(result);
+    StringAppendF(&result, "VSyncDispatch:\n");
+    mDispatch->dump(result);
 }
 
 void VSyncReactor::reset() {}
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index 7d8a8e3..5ee29f8 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -75,7 +75,7 @@
     std::unique_ptr<VSyncDispatch> const mDispatch;
     size_t const mPendingLimit;
 
-    std::mutex mMutex;
+    mutable std::mutex mMutex;
     bool mInternalIgnoreFences GUARDED_BY(mMutex) = false;
     bool mExternalIgnoreFences GUARDED_BY(mMutex) = false;
     std::vector<std::shared_ptr<FenceTime>> mUnfiredFences GUARDED_BY(mMutex);
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index a25b8a9..05a6fc3 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -66,6 +66,8 @@
     /* Inform the tracker that the samples it has are not accurate for prediction. */
     virtual void resetModel() = 0;
 
+    virtual void dump(std::string& result) const = 0;
+
 protected:
     VSyncTracker(VSyncTracker const&) = delete;
     VSyncTracker& operator=(VSyncTracker const&) = delete;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index cfaabfc..880be28 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -567,6 +567,7 @@
 
     postMessageAsync(new LambdaMessage([this]() NO_THREAD_SAFETY_ANALYSIS {
         readPersistentProperties();
+        mPowerAdvisor.onBootFinished();
         mBootStage = BootStage::FINISHED;
 
         if (property_get_bool("sf.debug.show_refresh_rate_overlay", false)) {
@@ -1028,7 +1029,7 @@
     ATRACE_CALL();
     ALOGV("performSetActiveConfig");
     if (mCheckPendingFence) {
-        if (previousFrameMissed()) {
+        if (previousFramePending()) {
             // fence has not signaled yet. wait for the next invalidate
             mEventQueue->invalidate();
             return true;
@@ -1775,13 +1776,17 @@
     setTransactionFlags(eDisplayTransactionNeeded);
 }
 
-bool SurfaceFlinger::previousFrameMissed(int graceTimeMs) NO_THREAD_SAFETY_ANALYSIS {
-    ATRACE_CALL();
+sp<Fence> SurfaceFlinger::previousFrameFence() NO_THREAD_SAFETY_ANALYSIS {
     // We are storing the last 2 present fences. If sf's phase offset is to be
     // woken up before the actual vsync but targeting the next vsync, we need to check
     // fence N-2
-    const sp<Fence>& fence = mVSyncModulator->getOffsets().sf > 0 ? mPreviousPresentFences[0]
-                                                                  : mPreviousPresentFences[1];
+    return mVSyncModulator->getOffsets().sf > 0 ? mPreviousPresentFences[0]
+                                                : mPreviousPresentFences[1];
+}
+
+bool SurfaceFlinger::previousFramePending(int graceTimeMs) NO_THREAD_SAFETY_ANALYSIS {
+    ATRACE_CALL();
+    const sp<Fence>& fence = previousFrameFence();
 
     if (fence == Fence::NO_FENCE) {
         return false;
@@ -1794,6 +1799,16 @@
     return (fence->getStatus() == Fence::Status::Unsignaled);
 }
 
+nsecs_t SurfaceFlinger::previousFramePresentTime() NO_THREAD_SAFETY_ANALYSIS {
+    const sp<Fence>& fence = previousFrameFence();
+
+    if (fence == Fence::NO_FENCE) {
+        return Fence::SIGNAL_TIME_INVALID;
+    }
+
+    return fence->getSignalTime();
+}
+
 void SurfaceFlinger::populateExpectedPresentTime() {
     DisplayStatInfo stats;
     mScheduler->getDisplayStatInfo(&stats);
@@ -1811,6 +1826,7 @@
             // calculate the expected present time once and use the cached
             // value throughout this frame to make sure all layers are
             // seeing this same value.
+            const nsecs_t lastExpectedPresentTime = mExpectedPresentTime.load();
             populateExpectedPresentTime();
 
             // When Backpressure propagation is enabled we want to give a small grace period
@@ -1821,12 +1837,32 @@
                      (mPropagateBackpressureClientComposition || !mHadClientComposition))
                     ? 1
                     : 0;
-            const TracedOrdinal<bool> frameMissed = {"FrameMissed",
-                                                     previousFrameMissed(
-                                                             graceTimeForPresentFenceMs)};
-            const TracedOrdinal<bool> hwcFrameMissed = {"HwcFrameMissed",
+
+            // Pending frames may trigger backpressure propagation.
+            const TracedOrdinal<bool> framePending = {"PrevFramePending",
+                                                      previousFramePending(
+                                                              graceTimeForPresentFenceMs)};
+
+            // Frame missed counts for metrics tracking.
+            // A frame is missed if the prior frame is still pending. If no longer pending,
+            // then we still count the frame as missed if the predicted present time
+            // was further in the past than when the fence actually fired.
+
+            // Add some slop to correct for drift. This should generally be
+            // smaller than a typical frame duration, but should not be so small
+            // that it reports reasonable drift as a missed frame.
+            DisplayStatInfo stats;
+            mScheduler->getDisplayStatInfo(&stats);
+            const nsecs_t frameMissedSlop = stats.vsyncPeriod / 2;
+            const nsecs_t previousPresentTime = previousFramePresentTime();
+            const TracedOrdinal<bool> frameMissed =
+                    {"PrevFrameMissed",
+                     framePending ||
+                             (previousPresentTime >= 0 &&
+                              (lastExpectedPresentTime < previousPresentTime - frameMissedSlop))};
+            const TracedOrdinal<bool> hwcFrameMissed = {"PrevHwcFrameMissed",
                                                         mHadDeviceComposition && frameMissed};
-            const TracedOrdinal<bool> gpuFrameMissed = {"GpuFrameMissed",
+            const TracedOrdinal<bool> gpuFrameMissed = {"PrevGpuFrameMissed",
                                                         mHadClientComposition && frameMissed};
 
             if (frameMissed) {
@@ -1846,7 +1882,7 @@
                 mGpuFrameMissedCount++;
             }
 
-            if (frameMissed && mPropagateBackpressure) {
+            if (framePending && mPropagateBackpressure) {
                 if ((hwcFrameMissed && !gpuFrameMissed) ||
                     mPropagateBackpressureClientComposition) {
                     signalLayerUpdate();
@@ -2349,7 +2385,8 @@
                 }
 
                 DisplayDeviceState state;
-                state.physical = {displayId, getHwComposer().getDisplayConnectionType(displayId)};
+                state.physical = {displayId, getHwComposer().getDisplayConnectionType(displayId),
+                                  event.hwcDisplayId};
                 state.isSecure = true; // All physical displays are currently considered secure.
                 state.displayName = info->name;
 
@@ -2358,6 +2395,12 @@
                 mPhysicalDisplayTokens.emplace(displayId, std::move(token));
 
                 mInterceptor->saveDisplayCreation(state);
+            } else {
+                ALOGV("Recreating display %s", to_string(displayId).c_str());
+
+                const auto token = it->second;
+                auto& state = mCurrentState.displays.editValueFor(token);
+                state.sequenceId = DisplayDeviceState{}.sequenceId;
             }
         } else {
             ALOGV("Removing display %s", to_string(displayId).c_str());
@@ -2535,20 +2578,17 @@
         producer = bqProducer;
     }
 
-    if (displaySurface != nullptr) {
-        mDisplays.emplace(displayToken,
-                          setupNewDisplayDeviceInternal(displayToken, compositionDisplay, state,
-                                                        displaySurface, producer));
-        if (!state.isVirtual()) {
-            LOG_ALWAYS_FATAL_IF(!displayId);
-            dispatchDisplayHotplugEvent(displayId->value, true);
-        }
+    LOG_FATAL_IF(!displaySurface);
+    const auto display = setupNewDisplayDeviceInternal(displayToken, compositionDisplay, state,
+                                                       displaySurface, producer);
+    mDisplays.emplace(displayToken, display);
+    if (!state.isVirtual()) {
+        LOG_FATAL_IF(!displayId);
+        dispatchDisplayHotplugEvent(displayId->value, true);
+    }
 
-        const auto displayDevice = mDisplays[displayToken];
-        if (displayDevice->isPrimary()) {
-            mScheduler->onPrimaryDisplayAreaChanged(displayDevice->getWidth() *
-                                                    displayDevice->getHeight());
-        }
+    if (display->isPrimary()) {
+        mScheduler->onPrimaryDisplayAreaChanged(display->getWidth() * display->getHeight());
     }
 }
 
@@ -2559,7 +2599,7 @@
         display->disconnect();
 
         if (!display->isVirtual()) {
-            LOG_ALWAYS_FATAL_IF(!displayId);
+            LOG_FATAL_IF(!displayId);
             dispatchDisplayHotplugEvent(displayId->value, false);
         }
     }
@@ -2572,13 +2612,19 @@
                                            const DisplayDeviceState& drawingState) {
     const sp<IBinder> currentBinder = IInterface::asBinder(currentState.surface);
     const sp<IBinder> drawingBinder = IInterface::asBinder(drawingState.surface);
-    if (currentBinder != drawingBinder) {
+    if (currentBinder != drawingBinder || currentState.sequenceId != drawingState.sequenceId) {
         // changing the surface is like destroying and recreating the DisplayDevice
         if (const auto display = getDisplayDeviceLocked(displayToken)) {
             display->disconnect();
         }
         mDisplays.erase(displayToken);
+        if (const auto& physical = currentState.physical) {
+            getHwComposer().allocatePhysicalDisplay(physical->hwcDisplayId, physical->id);
+        }
         processDisplayAdded(displayToken, currentState);
+        if (currentState.physical) {
+            initializeDisplays();
+        }
         return;
     }
 
@@ -4096,6 +4142,9 @@
     }
 
     if (currentMode == HWC_POWER_MODE_OFF) {
+        if (SurfaceFlinger::setSchedFifo(true) != NO_ERROR) {
+            ALOGW("Couldn't set SCHED_FIFO on display on: %s\n", strerror(errno));
+        }
         getHwComposer().setPowerMode(*displayId, mode);
         if (display->isPrimary() && mode != HWC_POWER_MODE_DOZE_SUSPEND) {
             setVsyncEnabledInHWC(*displayId, mHWCVsyncPendingState);
@@ -4106,19 +4155,11 @@
         mVisibleRegionsDirty = true;
         mHasPoweredOff = true;
         repaintEverything();
-
-        struct sched_param param = {0};
-        param.sched_priority = 1;
-        if (sched_setscheduler(0, SCHED_FIFO, &param) != 0) {
-            ALOGW("Couldn't set SCHED_FIFO on display on");
-        }
     } else if (mode == HWC_POWER_MODE_OFF) {
         // Turn off the display
-        struct sched_param param = {0};
-        if (sched_setscheduler(0, SCHED_OTHER, &param) != 0) {
-            ALOGW("Couldn't set SCHED_OTHER on display off");
+        if (SurfaceFlinger::setSchedFifo(false) != NO_ERROR) {
+            ALOGW("Couldn't set SCHED_OTHER on display off: %s\n", strerror(errno));
         }
-
         if (display->isPrimary() && currentMode != HWC_POWER_MODE_DOZE_SUSPEND) {
             mScheduler->disableHardwareVsync(true);
             mScheduler->onScreenReleased(mAppConnectionHandle);
@@ -5305,6 +5346,26 @@
     }
 }
 
+status_t SurfaceFlinger::setSchedFifo(bool enabled) {
+    static constexpr int kFifoPriority = 2;
+    static constexpr int kOtherPriority = 0;
+
+    struct sched_param param = {0};
+    int sched_policy;
+    if (enabled) {
+        sched_policy = SCHED_FIFO;
+        param.sched_priority = kFifoPriority;
+    } else {
+        sched_policy = SCHED_OTHER;
+        param.sched_priority = kOtherPriority;
+    }
+
+    if (sched_setscheduler(0, sched_policy, &param) != 0) {
+        return -errno;
+    }
+    return NO_ERROR;
+}
+
 const sp<DisplayDevice> SurfaceFlinger::getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) {
     const sp<IBinder> displayToken = getPhysicalDisplayTokenLocked(DisplayId{displayOrLayerStack});
     if (displayToken) {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 44e18a7..60904f6 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -259,6 +259,9 @@
     // overhead that is caused by reading from sysprop.
     static bool useFrameRateApi;
 
+    // set main thread scheduling policy
+    static status_t setSchedFifo(bool enabled) ANDROID_API;
+
     static char const* getServiceName() ANDROID_API {
         return "SurfaceFlinger";
     }
@@ -844,7 +847,21 @@
 
     bool isDisplayConfigAllowed(HwcConfigIndexType configId) const REQUIRES(mStateLock);
 
-    bool previousFrameMissed(int graceTimeMs = 0);
+    // Gets the fence for the previous frame.
+    // Must be called on the main thread.
+    sp<Fence> previousFrameFence();
+
+    // Whether the previous frame has not yet been presented to the display.
+    // If graceTimeMs is positive, this method waits for at most the provided
+    // grace period before reporting if the frame missed.
+    // Must be called on the main thread.
+    bool previousFramePending(int graceTimeMs = 0);
+
+    // Returns the previous time that the frame was presented. If the frame has
+    // not been presented yet, then returns Fence::SIGNAL_TIME_PENDING. If there
+    // is no pending frame, then returns Fence::SIGNAL_TIME_INVALID.
+    // Must be called on the main thread.
+    nsecs_t previousFramePresentTime();
 
     // Populates the expected present time for this frame. For negative offsets, performs a
     // correction using the predicted vsync for the next frame instead.
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 5b3cd69..80102bd 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -194,7 +194,7 @@
 
 Increment* SurfaceInterceptor::createTraceIncrementLocked() {
     Increment* increment(mTrace.add_increment());
-    increment->set_time_stamp(systemTime());
+    increment->set_time_stamp(elapsedRealtimeNano());
     return increment;
 }
 
diff --git a/services/surfaceflinger/SurfaceTracing.cpp b/services/surfaceflinger/SurfaceTracing.cpp
index 20c8d7a..a9c3332 100644
--- a/services/surfaceflinger/SurfaceTracing.cpp
+++ b/services/surfaceflinger/SurfaceTracing.cpp
@@ -129,7 +129,12 @@
 }
 
 status_t SurfaceTracing::writeToFile() {
-    mThread.join();
+    std::thread thread;
+    {
+        std::scoped_lock lock(mTraceLock);
+        thread = std::move(mThread);
+    }
+    thread.join();
     return mLastErr;
 }
 
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index d7ad9de..2b8424c 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -31,6 +31,7 @@
 #include <binder/ProcessState.h>
 #include <configstore/Utils.h>
 #include <displayservice/DisplayService.h>
+#include <errno.h>
 #include <hidl/LegacySupport.h>
 #include <processgroup/sched_policy.h>
 #include "SurfaceFlinger.h"
@@ -114,10 +115,8 @@
 
     startDisplayService(); // dependency on SF getting registered above
 
-    struct sched_param param = {0};
-    param.sched_priority = 2;
-    if (sched_setscheduler(0, SCHED_FIFO, &param) != 0) {
-        ALOGE("Couldn't set SCHED_FIFO");
+    if (SurfaceFlinger::setSchedFifo(true) != NO_ERROR) {
+        ALOGW("Couldn't set to SCHED_FIFO: %s", strerror(errno));
     }
 
     // run surface flinger in this thread
diff --git a/services/surfaceflinger/tests/AndroidTest.xml b/services/surfaceflinger/tests/AndroidTest.xml
index 8315037..000628f 100644
--- a/services/surfaceflinger/tests/AndroidTest.xml
+++ b/services/surfaceflinger/tests/AndroidTest.xml
@@ -18,6 +18,7 @@
         <option name="cleanup" value="true" />
         <option name="push" value="SurfaceFlinger_test->/data/local/tmp/SurfaceFlinger_test" />
     </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
     <option name="test-suite-tag" value="apct" />
     <test class="com.android.tradefed.testtype.GTest" >
         <option name="native-test-device-path" value="/data/local/tmp" />
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index 932c7c8..40ec502 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -52,9 +52,10 @@
     virtual sp<SurfaceControl> createLayer(const sp<SurfaceComposerClient>& client,
                                            const char* name, uint32_t width, uint32_t height,
                                            uint32_t flags = 0, SurfaceControl* parent = nullptr,
-                                           uint32_t* outTransformHint = nullptr) {
-        auto layer = createSurface(client, name, width, height, PIXEL_FORMAT_RGBA_8888, flags,
-                                   parent, outTransformHint);
+                                           uint32_t* outTransformHint = nullptr,
+                                           PixelFormat format = PIXEL_FORMAT_RGBA_8888) {
+        auto layer =
+                createSurface(client, name, width, height, format, flags, parent, outTransformHint);
 
         Transaction t;
         t.setLayerStack(layer, mDisplayLayerStack).setLayer(layer, mLayerZBase);
@@ -81,8 +82,9 @@
 
     virtual sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height,
                                            uint32_t flags = 0, SurfaceControl* parent = nullptr,
-                                           uint32_t* outTransformHint = nullptr) {
-        return createLayer(mClient, name, width, height, flags, parent, outTransformHint);
+                                           uint32_t* outTransformHint = nullptr,
+                                           PixelFormat format = PIXEL_FORMAT_RGBA_8888) {
+        return createLayer(mClient, name, width, height, flags, parent, outTransformHint, format);
     }
 
     sp<SurfaceControl> createColorLayer(const char* name, const Color& color,
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
index 2fd2579..d666d7e 100644
--- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
@@ -365,6 +365,67 @@
         getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
     }
 }
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBufferFormat) {
+    int32_t width = 100;
+    int32_t height = 100;
+    Rect crop = Rect(0, 0, width, height);
+
+    sp<SurfaceControl> behindLayer = createColorLayer("Behind layer", Color::RED);
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height, 0, nullptr, nullptr,
+                                                PIXEL_FORMAT_RGBX_8888));
+
+    Transaction()
+            .setLayer(layer, INT32_MAX - 1)
+            .show(layer)
+            .setLayerStack(behindLayer, mDisplayLayerStack)
+            .setCrop_legacy(behindLayer, crop)
+            .setLayer(behindLayer, INT32_MAX - 2)
+            .show(behindLayer)
+            .apply();
+
+    sp<Surface> surface = layer->getSurface();
+
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(width, height, PIXEL_FORMAT_RGBX_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+    ASSERT_NO_FATAL_FAILURE(
+            TransactionUtils::fillGraphicBufferColor(buffer, crop, Color::TRANSPARENT));
+
+    if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
+        Surface::attachAndQueueBufferWithDataspace(surface.get(), buffer, ui::Dataspace::V0_SRGB);
+    } else {
+        Transaction().setBuffer(layer, buffer).apply();
+    }
+
+    {
+        SCOPED_TRACE("Buffer Opaque Format");
+        auto shot = screenshot();
+        shot->expectColor(crop, Color::BLACK);
+    }
+
+    buffer = new GraphicBuffer(width, height, PIXEL_FORMAT_RGBA_8888, 1,
+                               BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                       BufferUsage::COMPOSER_OVERLAY,
+                               "test");
+    ASSERT_NO_FATAL_FAILURE(
+            TransactionUtils::fillGraphicBufferColor(buffer, crop, Color::TRANSPARENT));
+
+    if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
+        Surface::attachAndQueueBufferWithDataspace(surface.get(), buffer, ui::Dataspace::V0_SRGB);
+    } else {
+        Transaction().setBuffer(layer, buffer).apply();
+    }
+
+    {
+        SCOPED_TRACE("Buffer Transparent Format");
+        auto shot = screenshot();
+        shot->expectColor(crop, Color::RED);
+    }
+}
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
index 040852f..f0af363 100644
--- a/services/surfaceflinger/tests/TransactionTestHarnesses.h
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -88,13 +88,14 @@
 
     sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height,
                                    uint32_t flags = 0, SurfaceControl* parent = nullptr,
-                                   uint32_t* outTransformHint = nullptr) {
+                                   uint32_t* outTransformHint = nullptr,
+                                   PixelFormat format = PIXEL_FORMAT_RGBA_8888) {
         // if the flags already have a layer type specified, return an error
         if (flags & ISurfaceComposerClient::eFXSurfaceMask) {
             return nullptr;
         }
         return LayerTransactionTest::createLayer(name, width, height, flags | mLayerType, parent,
-                                                 outTransformHint);
+                                                 outTransformHint, format);
     }
 
     void fillLayerColor(const sp<SurfaceControl>& layer, const Color& color, int32_t bufferWidth,
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 680b0a0..2dcaf63 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -305,14 +305,14 @@
                 compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
                                                        ceDisplayArgs);
 
-        test->mDisplay =
-                FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay,
-                                          DisplayConnectionType::Internal, true /* isPrimary */)
-                        .setDisplaySurface(test->mDisplaySurface)
-                        .setNativeWindow(test->mNativeWindow)
-                        .setSecure(Derived::IS_SECURE)
-                        .setPowerMode(Derived::INIT_POWER_MODE)
-                        .inject();
+        test->mDisplay = FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay,
+                                                   DisplayConnectionType::Internal, HWC_DISPLAY,
+                                                   true /* isPrimary */)
+                                 .setDisplaySurface(test->mDisplaySurface)
+                                 .setNativeWindow(test->mNativeWindow)
+                                 .setSecure(Derived::IS_SECURE)
+                                 .setPowerMode(Derived::INIT_POWER_MODE)
+                                 .inject();
         Mock::VerifyAndClear(test->mNativeWindow);
         test->mDisplay->setLayerStack(DEFAULT_LAYER_STACK);
     }
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 6d00ccc..24eeac7 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -246,6 +246,7 @@
     constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{777};
     constexpr int DEFAULT_DISPLAY_WIDTH = 1080;
     constexpr int DEFAULT_DISPLAY_HEIGHT = 1920;
+    constexpr hwc2_display_t DEFAULT_DISPLAY_HWC_DISPLAY_ID = 0;
 
     // The DisplayDevice is required to have a framebuffer (behind the
     // ANativeWindow interface) which uses the actual hardware display
@@ -270,7 +271,7 @@
 
     auto injector =
             FakeDisplayDeviceInjector(mFlinger, compositionDisplay, DisplayConnectionType::Internal,
-                                      true /* isPrimary */);
+                                      DEFAULT_DISPLAY_HWC_DISPLAY_ID, true /* isPrimary */);
 
     injector.setNativeWindow(mNativeWindow);
     if (injectExtra) {
@@ -373,6 +374,23 @@
     static constexpr std::optional<DisplayConnectionType> value = PhysicalDisplay::CONNECTION_TYPE;
 };
 
+template <typename>
+struct HwcDisplayIdGetter {
+    static constexpr std::optional<hwc2_display_t> value;
+};
+
+constexpr hwc2_display_t HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID = 1010;
+
+template <DisplayId::Type displayId>
+struct HwcDisplayIdGetter<VirtualDisplayId<displayId>> {
+    static constexpr std::optional<hwc2_display_t> value = HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID;
+};
+
+template <typename PhysicalDisplay>
+struct HwcDisplayIdGetter<PhysicalDisplayId<PhysicalDisplay>> {
+    static constexpr std::optional<hwc2_display_t> value = PhysicalDisplay::HWC_DISPLAY_ID;
+};
+
 // DisplayIdType can be:
 //     1) PhysicalDisplayId<...> for generated ID of physical display backed by HWC.
 //     2) VirtualDisplayId<...> for hard-coded ID of virtual display backed by HWC.
@@ -382,6 +400,7 @@
 struct DisplayVariant {
     using DISPLAY_ID = DisplayIdGetter<DisplayIdType>;
     using CONNECTION_TYPE = DisplayConnectionTypeGetter<DisplayIdType>;
+    using HWC_DISPLAY_ID_OPT = HwcDisplayIdGetter<DisplayIdType>;
 
     // The display width and height
     static constexpr int WIDTH = width;
@@ -418,9 +437,9 @@
                 compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
                                                        ceDisplayArgs.build());
 
-        auto injector =
-                FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay,
-                                          CONNECTION_TYPE::value, static_cast<bool>(PRIMARY));
+        auto injector = FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay,
+                                                  CONNECTION_TYPE::value, HWC_DISPLAY_ID_OPT::value,
+                                                  static_cast<bool>(PRIMARY));
 
         injector.setSecure(static_cast<bool>(SECURE));
         injector.setNativeWindow(test->mNativeWindow);
@@ -603,12 +622,11 @@
 constexpr uint32_t GRALLOC_USAGE_PHYSICAL_DISPLAY =
         GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_FB;
 
-template <hwc2_display_t hwcDisplayId, typename PhysicalDisplay, int width, int height,
-          Critical critical>
+template <typename PhysicalDisplay, int width, int height, Critical critical>
 struct PhysicalDisplayVariant
       : DisplayVariant<PhysicalDisplayId<PhysicalDisplay>, width, height, critical, Async::FALSE,
                        Secure::TRUE, PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY>,
-        HwcDisplayVariant<hwcDisplayId, HWC2::DisplayType::Physical,
+        HwcDisplayVariant<PhysicalDisplay::HWC_DISPLAY_ID, HWC2::DisplayType::Physical,
                           DisplayVariant<PhysicalDisplayId<PhysicalDisplay>, width, height,
                                          critical, Async::FALSE, Secure::TRUE,
                                          PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY>,
@@ -619,6 +637,7 @@
     static constexpr auto CONNECTION_TYPE = DisplayConnectionType::Internal;
     static constexpr Primary PRIMARY = Primary::TRUE;
     static constexpr uint8_t PORT = 255;
+    static constexpr hwc2_display_t HWC_DISPLAY_ID = 1001;
     static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
     static constexpr auto GET_IDENTIFICATION_DATA = getInternalEdid;
 };
@@ -628,6 +647,7 @@
     static constexpr auto CONNECTION_TYPE = DisplayConnectionType::External;
     static constexpr Primary PRIMARY = Primary::FALSE;
     static constexpr uint8_t PORT = 254;
+    static constexpr hwc2_display_t HWC_DISPLAY_ID = 1002;
     static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
     static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid;
 };
@@ -635,19 +655,19 @@
 struct TertiaryDisplay {
     static constexpr Primary PRIMARY = Primary::FALSE;
     static constexpr uint8_t PORT = 253;
+    static constexpr hwc2_display_t HWC_DISPLAY_ID = 1003;
     static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid;
 };
 
 // A primary display is a physical display that is critical
 using PrimaryDisplayVariant =
-        PhysicalDisplayVariant<1001, PrimaryDisplay<false>, 3840, 2160, Critical::TRUE>;
+        PhysicalDisplayVariant<PrimaryDisplay<false>, 3840, 2160, Critical::TRUE>;
 
 // An external display is physical display that is not critical.
 using ExternalDisplayVariant =
-        PhysicalDisplayVariant<1002, ExternalDisplay<false>, 1920, 1280, Critical::FALSE>;
+        PhysicalDisplayVariant<ExternalDisplay<false>, 1920, 1280, Critical::FALSE>;
 
-using TertiaryDisplayVariant =
-        PhysicalDisplayVariant<1003, TertiaryDisplay, 1600, 1200, Critical::FALSE>;
+using TertiaryDisplayVariant = PhysicalDisplayVariant<TertiaryDisplay, 1600, 1200, Critical::FALSE>;
 
 // A virtual display not supported by the HWC.
 constexpr uint32_t GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY = 0;
@@ -696,7 +716,7 @@
       : DisplayVariant<VirtualDisplayId<42>, width, height, Critical::FALSE, Async::TRUE, secure,
                        Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>,
         HwcDisplayVariant<
-                1010, HWC2::DisplayType::Virtual,
+                HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID, HWC2::DisplayType::Virtual,
                 DisplayVariant<VirtualDisplayId<42>, width, height, Critical::FALSE, Async::TRUE,
                                secure, Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>> {
     using Base = DisplayVariant<VirtualDisplayId<42>, width, height, Critical::FALSE, Async::TRUE,
@@ -1767,7 +1787,9 @@
     if (const auto connectionType = Case::Display::CONNECTION_TYPE::value) {
         const auto displayId = Case::Display::DISPLAY_ID::get();
         ASSERT_TRUE(displayId);
-        state.physical = {*displayId, *connectionType};
+        const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
+        ASSERT_TRUE(hwcDisplayId);
+        state.physical = {*displayId, *connectionType, *hwcDisplayId};
     }
 
     state.isSecure = static_cast<bool>(Case::Display::SECURE);
@@ -1941,7 +1963,9 @@
     if (const auto connectionType = Case::Display::CONNECTION_TYPE::value) {
         const auto displayId = Case::Display::DISPLAY_ID::get();
         ASSERT_TRUE(displayId);
-        expectedPhysical = {*displayId, *connectionType};
+        const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
+        ASSERT_TRUE(hwcDisplayId);
+        expectedPhysical = {*displayId, *connectionType, *hwcDisplayId};
     }
 
     // The display should have been set up in the current display state
@@ -2124,11 +2148,11 @@
     ignoresHotplugConnectCommon<SimpleExternalDisplayCase>();
 }
 
-TEST_F(HandleTransactionLockedTest, processHotplugDisconnectPrimaryDisplay) {
+TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectPrimaryDisplay) {
     processesHotplugDisconnectCommon<SimplePrimaryDisplayCase>();
 }
 
-TEST_F(HandleTransactionLockedTest, processHotplugDisconnectExternalDisplay) {
+TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectExternalDisplay) {
     processesHotplugDisconnectCommon<SimpleExternalDisplayCase>();
 }
 
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 65b3e35..ba5c0c2 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -48,6 +48,7 @@
     MOCK_METHOD1(setCallback, void(VSyncSource::Callback*));
     MOCK_METHOD1(setPhaseOffset, void(nsecs_t));
     MOCK_METHOD1(pauseVsyncCallback, void(bool));
+    MOCK_CONST_METHOD1(dump, void(std::string&));
 };
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
index 910e73b..8d49201 100644
--- a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
@@ -42,25 +42,25 @@
                                  appEarlyGlDuration) {}
 };
 
-class PhaseOffsetsTest : public testing::Test {
+class PhaseDurationTest : public testing::Test {
 protected:
-    PhaseOffsetsTest()
-          : mPhaseOffsets(60.0f, 10'500'000, 20'500'000, 16'000'000, 33'500'000, 13'500'000,
-                          38'000'000) {}
+    PhaseDurationTest()
+          : mPhaseDurations(60.0f, 10'500'000, 20'500'000, 16'000'000, 33'500'000, 13'500'000,
+                            38'000'000) {}
 
-    ~PhaseOffsetsTest() = default;
+    ~PhaseDurationTest() = default;
 
-    TestablePhaseOffsetsAsDurations mPhaseOffsets;
+    TestablePhaseOffsetsAsDurations mPhaseDurations;
 };
 
 namespace {
 /* ------------------------------------------------------------------------
  * Test cases
  */
-TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_60Hz) {
-    mPhaseOffsets.setRefreshRateFps(60.0f);
-    auto currentOffsets = mPhaseOffsets.getCurrentOffsets();
-    auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(60.0f);
+TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_60Hz) {
+    mPhaseDurations.setRefreshRateFps(60.0f);
+    auto currentOffsets = mPhaseDurations.getCurrentOffsets();
+    auto offsets = mPhaseDurations.getOffsetsForRefreshRate(60.0f);
 
     EXPECT_EQ(currentOffsets, offsets);
     EXPECT_EQ(offsets.late.sf, 6'166'667);
@@ -76,10 +76,10 @@
     EXPECT_EQ(offsets.earlyGl.app, 15'166'668);
 }
 
-TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_90Hz) {
-    mPhaseOffsets.setRefreshRateFps(90.0f);
-    auto currentOffsets = mPhaseOffsets.getCurrentOffsets();
-    auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(90.0f);
+TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_90Hz) {
+    mPhaseDurations.setRefreshRateFps(90.0f);
+    auto currentOffsets = mPhaseDurations.getCurrentOffsets();
+    auto offsets = mPhaseDurations.getOffsetsForRefreshRate(90.0f);
 
     EXPECT_EQ(currentOffsets, offsets);
     EXPECT_EQ(offsets.late.sf, 611'111);
@@ -95,7 +95,7 @@
     EXPECT_EQ(offsets.earlyGl.app, 4'055'555);
 }
 
-TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_DefaultOffsets) {
+TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_DefaultOffsets) {
     TestablePhaseOffsetsAsDurations phaseOffsetsWithDefaultValues(60.0f, -1, -1, -1, -1, -1, -1);
 
     auto validateOffsets = [](auto& offsets) {
@@ -125,6 +125,54 @@
     validateOffsets(offsets);
 }
 
+TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_unknownRefreshRate) {
+    auto offsets = mPhaseDurations.getOffsetsForRefreshRate(14.7f);
+
+    EXPECT_EQ(offsets.late.sf, 57'527'208);
+
+    EXPECT_EQ(offsets.late.app, 37'027'208);
+
+    EXPECT_EQ(offsets.early.sf, 52'027'208);
+
+    EXPECT_EQ(offsets.early.app, 18'527'208);
+
+    EXPECT_EQ(offsets.earlyGl.sf, 54'527'208);
+
+    EXPECT_EQ(offsets.earlyGl.app, 16'527'208);
+}
+
+} // namespace
+
+class TestablePhaseOffsets : public impl::PhaseOffsets {
+public:
+    TestablePhaseOffsets() : impl::PhaseOffsets({60.0f, 90.0f}, 60.0f, 10'000'000) {}
+};
+
+class PhaseOffsetsTest : public testing::Test {
+protected:
+    PhaseOffsetsTest() = default;
+    ~PhaseOffsetsTest() = default;
+
+    TestablePhaseOffsets mPhaseOffsets;
+};
+
+namespace {
+TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_unknownRefreshRate) {
+    auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(14.7f);
+
+    EXPECT_EQ(offsets.late.sf, 1'000'000);
+
+    EXPECT_EQ(offsets.late.app, 1'000'000);
+
+    EXPECT_EQ(offsets.early.sf, 1'000'000);
+
+    EXPECT_EQ(offsets.early.app, 1'000'000);
+
+    EXPECT_EQ(offsets.earlyGl.sf, 1'000'000);
+
+    EXPECT_EQ(offsets.earlyGl.app, 1'000'000);
+}
+
 } // namespace
 } // namespace scheduler
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 414085c..058c5cc 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -549,9 +549,10 @@
         FakeDisplayDeviceInjector(TestableSurfaceFlinger& flinger,
                                   std::shared_ptr<compositionengine::Display> compositionDisplay,
                                   std::optional<DisplayConnectionType> connectionType,
-                                  bool isPrimary)
+                                  std::optional<hwc2_display_t> hwcDisplayId, bool isPrimary)
               : mFlinger(flinger),
-                mCreationArgs(flinger.mFlinger.get(), mDisplayToken, compositionDisplay) {
+                mCreationArgs(flinger.mFlinger.get(), mDisplayToken, compositionDisplay),
+                mHwcDisplayId(hwcDisplayId) {
             mCreationArgs.connectionType = connectionType;
             mCreationArgs.isPrimary = isPrimary;
         }
@@ -619,7 +620,8 @@
             DisplayDeviceState state;
             if (const auto type = mCreationArgs.connectionType) {
                 LOG_ALWAYS_FATAL_IF(!displayId);
-                state.physical = {*displayId, *type};
+                LOG_ALWAYS_FATAL_IF(!mHwcDisplayId);
+                state.physical = {*displayId, *type, *mHwcDisplayId};
             }
 
             state.isSecure = mCreationArgs.isSecure;
@@ -640,6 +642,7 @@
         TestableSurfaceFlinger& mFlinger;
         sp<BBinder> mDisplayToken = new BBinder();
         DisplayDeviceCreationArgs mCreationArgs;
+        const std::optional<hwc2_display_t> mHwcDisplayId;
     };
 
     surfaceflinger::test::Factory mFactory;
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index caac61d..be49ef3 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -51,6 +51,7 @@
 
     void setPeriod(nsecs_t) final {}
     void resetModel() final {}
+    void dump(std::string&) const final {}
 
 private:
     nsecs_t const mPeriod;
@@ -85,6 +86,7 @@
 
     void setPeriod(nsecs_t) final {}
     void resetModel() final {}
+    void dump(std::string&) const final {}
 
 private:
     std::mutex mutable mMutex;
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index 3ab38e4..3543361 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -47,6 +47,7 @@
     MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
     MOCK_METHOD1(setPeriod, void(nsecs_t));
     MOCK_METHOD0(resetModel, void());
+    MOCK_CONST_METHOD1(dump, void(std::string&));
 
     nsecs_t nextVSyncTime(nsecs_t timePoint) const {
         if (timePoint % mPeriod == 0) {
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index ac95938..4f150ef 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -41,6 +41,7 @@
     MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
     MOCK_METHOD1(setPeriod, void(nsecs_t));
     MOCK_METHOD0(resetModel, void());
+    MOCK_CONST_METHOD1(dump, void(std::string&));
 };
 
 class VSyncTrackerWrapper : public VSyncTracker {
@@ -56,6 +57,7 @@
     nsecs_t currentPeriod() const final { return mTracker->currentPeriod(); }
     void setPeriod(nsecs_t period) final { mTracker->setPeriod(period); }
     void resetModel() final { mTracker->resetModel(); }
+    void dump(std::string& result) const final { mTracker->dump(result); }
 
 private:
     std::shared_ptr<VSyncTracker> const mTracker;
@@ -83,6 +85,7 @@
     MOCK_METHOD1(unregisterCallback, void(CallbackToken));
     MOCK_METHOD3(schedule, ScheduleResult(CallbackToken, nsecs_t, nsecs_t));
     MOCK_METHOD1(cancel, CancelResult(CallbackToken token));
+    MOCK_CONST_METHOD1(dump, void(std::string&));
 };
 
 class VSyncDispatchWrapper : public VSyncDispatch {
@@ -102,6 +105,8 @@
 
     CancelResult cancel(CallbackToken token) final { return mDispatch->cancel(token); }
 
+    void dump(std::string& result) const final { return mDispatch->dump(result); }
+
 private:
     std::shared_ptr<VSyncDispatch> const mDispatch;
 };
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
index fe57c98..e22d0cf 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
@@ -29,6 +29,7 @@
     PowerAdvisor();
     ~PowerAdvisor() override;
 
+    MOCK_METHOD0(onBootFinished, void());
     MOCK_METHOD2(setExpensiveRenderingExpected, void(DisplayId displayId, bool expected));
     MOCK_METHOD0(notifyDisplayUpdateImminent, void());
 };
diff --git a/vulkan/TEST_MAPPING b/vulkan/TEST_MAPPING
new file mode 100644
index 0000000..d391dce
--- /dev/null
+++ b/vulkan/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsGpuToolsHostTestCases"
+    }
+  ]
+}
diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp
index e607b05..5b9affd 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -1196,6 +1196,23 @@
     return initialized;
 }
 
+template <typename Functor>
+void ForEachLayerFromSettings(Functor functor) {
+    const std::string layersSetting =
+        android::GraphicsEnv::getInstance().getDebugLayers();
+    if (!layersSetting.empty()) {
+        std::vector<std::string> layers =
+            android::base::Split(layersSetting, ":");
+        for (uint32_t i = 0; i < layers.size(); i++) {
+            const Layer* layer = FindLayer(layers[i].c_str());
+            if (!layer) {
+                continue;
+            }
+            functor(layer);
+        }
+    }
+}
+
 }  // anonymous namespace
 
 VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo,
@@ -1291,28 +1308,18 @@
     std::unordered_set<std::string> extensionNames;
 
     // Expose extensions from implicitly enabled layers.
-    const std::string layersSetting =
-        android::GraphicsEnv::getInstance().getDebugLayers();
-    if (!layersSetting.empty()) {
-        std::vector<std::string> layers =
-            android::base::Split(layersSetting, ":");
-        for (uint32_t i = 0; i < layers.size(); i++) {
-            const Layer* layer = FindLayer(layers[i].c_str());
-            if (!layer) {
-                continue;
-            }
-            uint32_t count = 0;
-            const VkExtensionProperties* props =
-                GetLayerInstanceExtensions(*layer, count);
-            if (count > 0) {
-                for (uint32_t i = 0; i < count; ++i) {
-                    if (extensionNames.emplace(props[i].extensionName).second) {
-                        properties.push_back(props[i]);
-                    }
+    ForEachLayerFromSettings([&](const Layer* layer) {
+        uint32_t count = 0;
+        const VkExtensionProperties* props =
+            GetLayerInstanceExtensions(*layer, count);
+        if (count > 0) {
+            for (uint32_t i = 0; i < count; ++i) {
+                if (extensionNames.emplace(props[i].extensionName).second) {
+                    properties.push_back(props[i]);
                 }
             }
         }
-    }
+    });
 
     // TODO(b/143293104): Parse debug.vulkan.layers properties
 
@@ -1393,10 +1400,57 @@
         return *pPropertyCount < count ? VK_INCOMPLETE : VK_SUCCESS;
     }
 
-    // TODO(b/143293104): expose extensions from implicitly enabled layers
-    const InstanceData& data = GetData(physicalDevice);
-    return data.dispatch.EnumerateDeviceExtensionProperties(
-        physicalDevice, nullptr, pPropertyCount, pProperties);
+    // If the pLayerName is nullptr, we must advertise all device extensions
+    // from all implicitly enabled layers and the driver implementation. If
+    // there are duplicates among layers and the driver implementation, always
+    // only preserve the top layer closest to the application regardless of the
+    // spec version.
+    std::vector<VkExtensionProperties> properties;
+    std::unordered_set<std::string> extensionNames;
+
+    // Expose extensions from implicitly enabled layers.
+    ForEachLayerFromSettings([&](const Layer* layer) {
+        uint32_t count = 0;
+        const VkExtensionProperties* props =
+            GetLayerDeviceExtensions(*layer, count);
+        if (count > 0) {
+            for (uint32_t i = 0; i < count; ++i) {
+                if (extensionNames.emplace(props[i].extensionName).second) {
+                    properties.push_back(props[i]);
+                }
+            }
+        }
+    });
+
+    // TODO(b/143293104): Parse debug.vulkan.layers properties
+
+    // Expose extensions from driver implementation.
+    {
+        const InstanceData& data = GetData(physicalDevice);
+        uint32_t count = 0;
+        VkResult result = data.dispatch.EnumerateDeviceExtensionProperties(
+            physicalDevice, nullptr, &count, nullptr);
+        if (result == VK_SUCCESS && count > 0) {
+            std::vector<VkExtensionProperties> props(count);
+            result = data.dispatch.EnumerateDeviceExtensionProperties(
+                physicalDevice, nullptr, &count, props.data());
+            for (auto prop : props) {
+                if (extensionNames.emplace(prop.extensionName).second) {
+                    properties.push_back(prop);
+                }
+            }
+        }
+    }
+
+    uint32_t totalCount = properties.size();
+    if (!pProperties || *pPropertyCount > totalCount) {
+        *pPropertyCount = totalCount;
+    }
+    if (pProperties) {
+        std::copy(properties.data(), properties.data() + *pPropertyCount,
+                  pProperties);
+    }
+    return *pPropertyCount < totalCount ? VK_INCOMPLETE : VK_SUCCESS;
 }
 
 VkResult EnumerateInstanceVersion(uint32_t* pApiVersion) {