Merge "Initialize PointerCoords in input tests"
diff --git a/cmds/atrace/TEST_MAPPING b/cmds/atrace/TEST_MAPPING
new file mode 100644
index 0000000..f43db22
--- /dev/null
+++ b/cmds/atrace/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsAtraceHostTestCases"
+    }
+  ]
+}
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index bd3d2d8..5836f11 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -193,10 +193,12 @@
         { REQ,      "events/cpufreq_interactive/enable" },
     } },
     { "sync",       "Synchronization",  0, {
-        // before linux kernel 4.9
+        // linux kernel < 4.9
         { OPT,      "events/sync/enable" },
-        // starting in linux kernel 4.9
+        // linux kernel == 4.9.x
         { OPT,      "events/fence/enable" },
+        // linux kernel > 4.9
+        { OPT,      "events/dma_fence/enable" },
     } },
     { "workq",      "Kernel Workqueues", 0, {
         { REQ,      "events/workqueue/enable" },
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index a282424..66c9cc6 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -91,6 +91,8 @@
     chmod 0666 /sys/kernel/tracing/events/sync/enable
     chmod 0666 /sys/kernel/debug/tracing/events/fence/enable
     chmod 0666 /sys/kernel/tracing/events/fence/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/dma_fence/enable
+    chmod 0666 /sys/kernel/tracing/events/dma_fence/enable
     chmod 0666 /sys/kernel/debug/tracing/events/kmem/rss_stat/enable
     chmod 0666 /sys/kernel/tracing/events/kmem/rss_stat/enable
     chmod 0666 /sys/kernel/debug/tracing/events/kmem/ion_heap_grow/enable
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 03a15bb..21de4db 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1752,7 +1752,9 @@
     }
     // TODO: Should truncate the existing file.
     // ... and re-open it for further logging.
-    redirect_to_existing_file(stderr, const_cast<char*>(ds.log_path_.c_str()));
+    if (!redirect_to_existing_file(stderr, const_cast<char*>(ds.log_path_.c_str()))) {
+        return false;
+    }
     fprintf(stderr, "\n");
 
     int32_t err = zip_writer_->Finish();
@@ -2372,12 +2374,17 @@
     // If we are going to use a socket, do it as early as possible
     // to avoid timeouts from bugreport.
     if (options_->use_socket) {
-        redirect_to_socket(stdout, "dumpstate");
+        if (!redirect_to_socket(stdout, "dumpstate")) {
+            return ERROR;
+        }
     }
 
     if (options_->use_control_socket) {
         MYLOGD("Opening control socket\n");
         control_socket_fd_ = open_socket("dumpstate");
+        if (control_socket_fd_ == -1) {
+            return ERROR;
+        }
         options_->do_progress_updates = 1;
     }
 
@@ -2436,7 +2443,9 @@
     if (is_redirecting) {
         // Redirect stderr to log_path_ for debugging.
         TEMP_FAILURE_RETRY(dup_stderr_fd = dup(fileno(stderr)));
-        redirect_to_file(stderr, const_cast<char*>(log_path_.c_str()));
+        if (!redirect_to_file(stderr, const_cast<char*>(log_path_.c_str()))) {
+            return ERROR;
+        }
         if (chown(log_path_.c_str(), AID_SHELL, AID_SHELL)) {
             MYLOGE("Unable to change ownership of dumpstate log file %s: %s\n", log_path_.c_str(),
                    strerror(errno));
@@ -2449,7 +2458,9 @@
         /* TODO: rather than generating a text file now and zipping it later,
            it would be more efficient to redirect stdout to the zip entry
            directly, but the libziparchive doesn't support that option yet. */
-        redirect_to_file(stdout, const_cast<char*>(tmp_path_.c_str()));
+        if (!redirect_to_file(stdout, const_cast<char*>(tmp_path_.c_str()))) {
+            return ERROR;
+        }
         if (chown(tmp_path_.c_str(), AID_SHELL, AID_SHELL)) {
             MYLOGE("Unable to change ownership of temporary bugreport file %s: %s\n",
                    tmp_path_.c_str(), strerror(errno));
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 9803f00..c326bb6 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -519,14 +519,26 @@
 /** opens a socket and returns its file descriptor */
 int open_socket(const char *service);
 
-/* redirect output to a service control socket */
-void redirect_to_socket(FILE *redirect, const char *service);
+/*
+ * Redirects 'redirect' to a service control socket.
+ *
+ * Returns true if redirect succeeds.
+ */
+bool redirect_to_socket(FILE* redirect, const char* service);
 
-/* redirect output to a new file */
-void redirect_to_file(FILE *redirect, char *path);
+/*
+ * Redirects 'redirect' to a file indicated by 'path', truncating it.
+ *
+ * Returns true if redirect succeeds.
+ */
+bool redirect_to_file(FILE* redirect, char* path);
 
-/* redirect output to an existing file */
-void redirect_to_existing_file(FILE *redirect, char *path);
+/*
+ * Redirects 'redirect' to an existing file indicated by 'path', appending it.
+ *
+ * Returns true if redirect succeeds.
+ */
+bool redirect_to_existing_file(FILE* redirect, char* path);
 
 /* create leading directories, if necessary */
 void create_parent_dirs(const char *path);
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp
index 4bc0e1d..2a5516d 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -712,12 +712,12 @@
     int s = android_get_control_socket(service);
     if (s < 0) {
         MYLOGE("android_get_control_socket(%s): %s\n", service, strerror(errno));
-        exit(1);
+        return -1;
     }
     fcntl(s, F_SETFD, FD_CLOEXEC);
     if (listen(s, 4) < 0) {
         MYLOGE("listen(control socket): %s\n", strerror(errno));
-        exit(1);
+        return -1;
     }
 
     struct sockaddr addr;
@@ -725,18 +725,23 @@
     int fd = accept(s, &addr, &alen);
     if (fd < 0) {
         MYLOGE("accept(control socket): %s\n", strerror(errno));
-        exit(1);
+        return -1;
     }
 
     return fd;
 }
 
 /* redirect output to a service control socket */
-void redirect_to_socket(FILE *redirect, const char *service) {
+bool redirect_to_socket(FILE* redirect, const char* service) {
     int fd = open_socket(service);
+    if (fd == -1) {
+        return false;
+    }
     fflush(redirect);
-    dup2(fd, fileno(redirect));
+    // TODO: handle dup2 failure
+    TEMP_FAILURE_RETRY(dup2(fd, fileno(redirect)));
     close(fd);
+    return true;
 }
 
 // TODO: should call is_valid_output_file and/or be merged into it.
@@ -766,7 +771,7 @@
     }
 }
 
-void _redirect_to_file(FILE *redirect, char *path, int truncate_flag) {
+bool _redirect_to_file(FILE* redirect, char* path, int truncate_flag) {
     create_parent_dirs(path);
 
     int fd = TEMP_FAILURE_RETRY(open(path,
@@ -774,19 +779,20 @@
                                      S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
     if (fd < 0) {
         MYLOGE("%s: %s\n", path, strerror(errno));
-        exit(1);
+        return false;
     }
 
     TEMP_FAILURE_RETRY(dup2(fd, fileno(redirect)));
     close(fd);
+    return true;
 }
 
-void redirect_to_file(FILE *redirect, char *path) {
-    _redirect_to_file(redirect, path, O_TRUNC);
+bool redirect_to_file(FILE* redirect, char* path) {
+    return _redirect_to_file(redirect, path, O_TRUNC);
 }
 
-void redirect_to_existing_file(FILE *redirect, char *path) {
-    _redirect_to_file(redirect, path, O_APPEND);
+bool redirect_to_existing_file(FILE* redirect, char* path) {
+    return _redirect_to_file(redirect, path, O_APPEND);
 }
 
 // Dump Dalvik and native stack traces, return the trace file location (nullptr if none).
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index f523725..ffd1191 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -261,6 +261,40 @@
   return "";
 }
 
+// Determines which binary we should use for execution (the debug or non-debug version).
+// e.g. dex2oatd vs dex2oat
+static const char* select_execution_binary(const char* binary, const char* debug_binary,
+        bool background_job_compile) {
+    return select_execution_binary(
+        binary,
+        debug_binary,
+        background_job_compile,
+        is_debug_runtime(),
+        (android::base::GetProperty("ro.build.version.codename", "") == "REL"),
+        is_debuggable_build());
+}
+
+// Determines which binary we should use for execution (the debug or non-debug version).
+// e.g. dex2oatd vs dex2oat
+// This is convenient method which is much easier to test because it doesn't read
+// system properties.
+const char* select_execution_binary(
+        const char* binary,
+        const char* debug_binary,
+        bool background_job_compile,
+        bool is_debug_runtime,
+        bool is_release,
+        bool is_debuggable_build) {
+    // Do not use debug binaries for release candidates (to give more soak time).
+    bool is_debug_bg_job = background_job_compile && is_debuggable_build && !is_release;
+
+    // If the runtime was requested to use libartd.so, we'll run the debug version - assuming
+    // the file is present (it may not be on images with very little space available).
+    bool useDebug = (is_debug_runtime || is_debug_bg_job) && (access(debug_binary, X_OK) == 0);
+
+    return useDebug ? debug_binary : binary;
+}
+
 // Namespace for Android Runtime flags applied during boot time.
 static const char* RUNTIME_NATIVE_BOOT_NAMESPACE = "runtime_native_boot";
 // Feature flag name for running the JIT in Zygote experiment, b/119800099.
@@ -354,16 +388,9 @@
         std::string dex2oat_large_app_threshold_arg =
             MapPropertyToArg("dalvik.vm.dex2oat-very-large", "--very-large-app-threshold=%s");
 
-        // If the runtime was requested to use libartd.so, we'll run dex2oatd, otherwise dex2oat.
-        const char* dex2oat_bin = kDex2oatPath;
-        // Do not use dex2oatd for release candidates (give dex2oat more soak time).
-        bool is_release = android::base::GetProperty("ro.build.version.codename", "") == "REL";
-        if (is_debug_runtime() ||
-                (background_job_compile && is_debuggable_build() && !is_release)) {
-            if (access(kDex2oatDebugPath, X_OK) == 0) {
-                dex2oat_bin = kDex2oatDebugPath;
-            }
-        }
+
+        const char* dex2oat_bin = select_execution_binary(
+            kDex2oatPath, kDex2oatDebugPath, background_job_compile);
 
         bool generate_minidebug_info = kEnableMinidebugInfo &&
                 GetBoolProperty(kMinidebugInfoSystemProperty, kMinidebugInfoSystemPropertyDefault);
@@ -676,7 +703,12 @@
                   const std::vector<std::string>& dex_locations,
                   bool copy_and_update,
                   bool store_aggregation_counters) {
-        const char* profman_bin = is_debug_runtime() ? kProfmanDebugPath: kProfmanPath;
+
+        // TODO(calin): Assume for now we run in the bg compile job (which is in
+        // most of the invocation). With the current data flow, is not very easy or
+        // clean to discover this in RunProfman (it will require quite a messy refactoring).
+        const char* profman_bin = select_execution_binary(
+            kProfmanPath, kProfmanDebugPath, /*background_job_compile=*/ true);
 
         if (copy_and_update) {
             CHECK_EQ(1u, profile_fds.size());
@@ -1491,8 +1523,10 @@
                     bool downgrade,
                     const char* class_loader_context) {
         CHECK_GE(zip_fd, 0);
-        const char* dexoptanalyzer_bin =
-            is_debug_runtime() ? kDexoptanalyzerDebugPath : kDexoptanalyzerPath;
+
+        // We always run the analyzer in the background job.
+        const char* dexoptanalyzer_bin = select_execution_binary(
+             kDexoptanalyzerPath, kDexoptanalyzerDebugPath, /*background_job_compile=*/ true);
 
         std::string dex_file_arg = "--dex-file=" + dex_file;
         std::string oat_fd_arg = "--oat-fd=" + std::to_string(oat_fd);
diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h
index 5902659..a8c48c5 100644
--- a/cmds/installd/dexopt.h
+++ b/cmds/installd/dexopt.h
@@ -36,7 +36,7 @@
 // Location of binaries in the Android Runtime APEX.
 static constexpr const char* kDex2oatPath = ANDROID_RUNTIME_APEX_BIN "/dex2oat";
 static constexpr const char* kDex2oatDebugPath = ANDROID_RUNTIME_APEX_BIN "/dex2oatd";
-static constexpr const char* kProfmanPath = ANDROID_RUNTIME_APEX_BIN "/profmand";
+static constexpr const char* kProfmanPath = ANDROID_RUNTIME_APEX_BIN "/profman";
 static constexpr const char* kProfmanDebugPath = ANDROID_RUNTIME_APEX_BIN "/profmand";
 static constexpr const char* kDexoptanalyzerPath = ANDROID_RUNTIME_APEX_BIN "/dexoptanalyzer";
 static constexpr const char* kDexoptanalyzerDebugPath = ANDROID_RUNTIME_APEX_BIN "/dexoptanalyzerd";
@@ -128,6 +128,14 @@
 
 bool move_ab(const char* apk_path, const char* instruction_set, const char* output_path);
 
+const char* select_execution_binary(
+        const char* binary,
+        const char* debug_binary,
+        bool background_job_compile,
+        bool is_debug_runtime,
+        bool is_release,
+        bool is_debuggable_build);
+
 }  // namespace installd
 }  // namespace android
 
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index 78edce0..71b710b 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -1175,5 +1175,64 @@
     ASSERT_TRUE(std::find(profiles.begin(), profiles.end(), ref_prof) != profiles.end());
 }
 
+TEST_F(DexoptTest, select_execution_binary) {
+    LOG(INFO) << "DexoptTestselect_execution_binary";
+
+    std::string release_str = app_private_dir_ce_  + "/release";
+    std::string debug_str = app_private_dir_ce_  + "/debug";
+
+    // Setup the binaries. Note that we only need executable files to actually
+    // test the execution binary selection
+    run_cmd("touch " + release_str);
+    run_cmd("touch " + debug_str);
+    run_cmd("chmod 777 " + release_str);
+    run_cmd("chmod 777 " + debug_str);
+
+    const char* release = release_str.c_str();
+    const char* debug = debug_str.c_str();
+
+    ASSERT_STREQ(release, select_execution_binary(
+        release,
+        debug,
+        /*background_job_compile=*/ false,
+        /*is_debug_runtime=*/ false,
+        /*is_release=*/ false,
+        /*is_debuggable_build=*/ false));
+
+    ASSERT_STREQ(release, select_execution_binary(
+        release,
+        debug,
+        /*background_job_compile=*/ true,
+        /*is_debug_runtime=*/ false,
+        /*is_release=*/ true,
+        /*is_debuggable_build=*/ true));
+
+    ASSERT_STREQ(debug, select_execution_binary(
+        release,
+        debug,
+        /*background_job_compile=*/ false,
+        /*is_debug_runtime=*/ true,
+        /*is_release=*/ false,
+        /*is_debuggable_build=*/ false));
+
+    ASSERT_STREQ(debug, select_execution_binary(
+        release,
+        debug,
+        /*background_job_compile=*/ true,
+        /*is_debug_runtime=*/ false,
+        /*is_release=*/ false,
+        /*is_debuggable_build=*/ true));
+
+
+    // Select the release when the debug file is not there.
+    ASSERT_STREQ(release, select_execution_binary(
+        release,
+        "does_not_exist",
+        /*background_job_compile=*/ false,
+        /*is_debug_runtime=*/ true,
+        /*is_release=*/ false,
+        /*is_debuggable_build=*/ false));
+}
+
 }  // namespace installd
 }  // namespace android
diff --git a/cmds/ip-up-vpn/ip-up-vpn.c b/cmds/ip-up-vpn/ip-up-vpn.c
index 3b8955b..71f0837 100644
--- a/cmds/ip-up-vpn/ip-up-vpn.c
+++ b/cmds/ip-up-vpn/ip-up-vpn.c
@@ -95,6 +95,7 @@
         strncpy(ifr.ifr_name, interface, IFNAMSIZ);
         if (ioctl(s, SIOCSIFFLAGS, &ifr)) {
             ALOGE("Cannot bring up %s: %s", interface, strerror(errno));
+            fclose(state);
             return 1;
         }
 
@@ -102,6 +103,7 @@
         if (!set_address(&ifr.ifr_addr, address) ||
                 ioctl(s, SIOCSIFADDR, &ifr)) {
             ALOGE("Cannot set address: %s", strerror(errno));
+            fclose(state);
             return 1;
         }
 
@@ -109,6 +111,7 @@
         if (set_address(&ifr.ifr_netmask, env("INTERNAL_NETMASK4"))) {
             if (ioctl(s, SIOCSIFNETMASK, &ifr)) {
                 ALOGE("Cannot set netmask: %s", strerror(errno));
+                fclose(state);
                 return 1;
             }
         }
@@ -123,6 +126,7 @@
         fprintf(state, "%s\n", env("REMOTE_ADDR"));
     } else {
         ALOGE("Cannot parse parameters");
+        fclose(state);
         return 1;
     }
 
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index f9c8c8a..bdbc0d3 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -31,6 +31,7 @@
 using ::android::sp;
 using ::android::status_t;
 using ::android::String16;
+using ::android::String8;
 using ::android::wp;
 
 namespace ABBinderTag {
@@ -67,8 +68,6 @@
 AIBinder::~AIBinder() {}
 
 bool AIBinder::associateClass(const AIBinder_Class* clazz) {
-    using ::android::String8;
-
     if (clazz == nullptr) return false;
     if (mClazz == clazz) return true;
 
@@ -119,6 +118,33 @@
     return getClass()->getInterfaceDescriptor();
 }
 
+status_t ABBinder::dump(int fd, const ::android::Vector<String16>& args) {
+    AIBinder_onDump onDump = getClass()->onDump;
+
+    if (onDump == nullptr) {
+        return STATUS_OK;
+    }
+
+    // technically UINT32_MAX would be okay here, but INT32_MAX is expected since this may be
+    // null in Java
+    if (args.size() > INT32_MAX) {
+        LOG(ERROR) << "ABBinder::dump received too many arguments: " << args.size();
+        return STATUS_BAD_VALUE;
+    }
+
+    std::vector<String8> utf8Args;  // owns memory of utf8s
+    utf8Args.reserve(args.size());
+    std::vector<const char*> utf8Pointers;  // what can be passed over NDK API
+    utf8Pointers.reserve(args.size());
+
+    for (size_t i = 0; i < args.size(); i++) {
+        utf8Args.push_back(String8(args[i]));
+        utf8Pointers.push_back(utf8Args[i].c_str());
+    }
+
+    return onDump(this, fd, utf8Pointers.data(), utf8Pointers.size());
+}
+
 status_t ABBinder::onTransact(transaction_code_t code, const Parcel& data, Parcel* reply,
                               binder_flags_t flags) {
     if (isUserCommand(code)) {
@@ -232,6 +258,13 @@
     return new AIBinder_Class(interfaceDescriptor, onCreate, onDestroy, onTransact);
 }
 
+void AIBinder_Class_setOnDump(AIBinder_Class* clazz, AIBinder_onDump onDump) {
+    CHECK(clazz != nullptr) << "setOnDump requires non-null clazz";
+
+    // this is required to be called before instances are instantiated
+    clazz->onDump = onDump;
+}
+
 void AIBinder_DeathRecipient::TransferDeathRecipient::binderDied(const wp<IBinder>& who) {
     CHECK(who == mWho);
 
@@ -325,6 +358,30 @@
     return PruneStatusT(binder->getBinder()->pingBinder());
 }
 
+binder_status_t AIBinder_dump(AIBinder* binder, int fd, const char** args, uint32_t numArgs) {
+    if (binder == nullptr) {
+        return STATUS_UNEXPECTED_NULL;
+    }
+
+    ABBinder* bBinder = binder->asABBinder();
+    if (bBinder != nullptr) {
+        AIBinder_onDump onDump = binder->getClass()->onDump;
+        if (onDump == nullptr) {
+            return STATUS_OK;
+        }
+        return PruneStatusT(onDump(bBinder, fd, args, numArgs));
+    }
+
+    ::android::Vector<String16> utf16Args;
+    utf16Args.setCapacity(numArgs);
+    for (uint32_t i = 0; i < numArgs; i++) {
+        utf16Args.push(String16(String8(args[i])));
+    }
+
+    status_t status = binder->getBinder()->dump(fd, utf16Args);
+    return PruneStatusT(status);
+}
+
 binder_status_t AIBinder_linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient,
                                      void* cookie) {
     if (binder == nullptr || recipient == nullptr) {
diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h
index 7852298..0dd795a 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -25,6 +25,7 @@
 
 #include <binder/Binder.h>
 #include <binder/IBinder.h>
+#include <utils/Vector.h>
 
 inline bool isUserCommand(transaction_code_t code) {
     return code >= FIRST_CALL_TRANSACTION && code <= LAST_CALL_TRANSACTION;
@@ -66,6 +67,7 @@
     ABBinder* asABBinder() override { return this; }
 
     const ::android::String16& getInterfaceDescriptor() const override;
+    ::android::status_t dump(int fd, const ::android::Vector<::android::String16>& args) override;
     ::android::status_t onTransact(uint32_t code, const ::android::Parcel& data,
                                    ::android::Parcel* reply, binder_flags_t flags) override;
 
@@ -106,10 +108,14 @@
 
     const ::android::String16& getInterfaceDescriptor() const { return mInterfaceDescriptor; }
 
+    // required to be non-null, implemented for every class
     const AIBinder_Class_onCreate onCreate;
     const AIBinder_Class_onDestroy onDestroy;
     const AIBinder_Class_onTransact onTransact;
 
+    // optional methods for a class
+    AIBinder_onDump onDump;
+
    private:
     // This must be a String16 since BBinder virtual getInterfaceDescriptor returns a reference to
     // one.
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 9c6c55e..bddc10d 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -179,6 +179,31 @@
         __INTRODUCED_IN(29);
 
 /**
+ * Dump information about an AIBinder (usually for debugging).
+ *
+ * When no arguments are provided, a brief overview of the interview should be given.
+ *
+ * \param binder interface being dumped
+ * \param fd file descriptor to be dumped to, should be flushed, ownership is not passed.
+ * \param args array of null-terminated strings for dump (may be null if numArgs is 0)
+ * \param numArgs number of args to be sent
+ *
+ * \return binder_status_t result of transaction (if remote, for instance)
+ */
+typedef binder_status_t (*AIBinder_onDump)(AIBinder* binder, int fd, const char** args,
+                                           uint32_t numArgs);
+
+/**
+ * This sets the implementation of the dump method for a class.
+ *
+ * If this isn't set, nothing will be dumped when dump is called (for instance with
+ * android.os.Binder#dump). Must be called before any instance of the class is created.
+ *
+ * \param dump function to call when an instance of this binder class is being dumped.
+ */
+void AIBinder_Class_setOnDump(AIBinder_Class* clazz, AIBinder_onDump onDump) __INTRODUCED_IN(29);
+
+/**
  * Creates a new binder object of the appropriate class.
  *
  * Ownership of args is passed to this object. The lifecycle is implemented with AIBinder_incStrong
@@ -237,6 +262,21 @@
 binder_status_t AIBinder_ping(AIBinder* binder) __INTRODUCED_IN(29);
 
 /**
+ * Built-in transaction for all binder objects. This dumps information about a given binder.
+ *
+ * See also AIBinder_Class_setOnDump, AIBinder_onDump
+ *
+ * \param binder the binder to dump information about
+ * \param fd where information should be dumped to
+ * \param args null-terminated arguments to pass (may be null if numArgs is 0)
+ * \param numArgs number of args to send
+ *
+ * \return STATUS_OK if dump succeeds (or if there is nothing to dump)
+ */
+binder_status_t AIBinder_dump(AIBinder* binder, int fd, const char** args, uint32_t numArgs)
+        __INTRODUCED_IN(29);
+
+/**
  * Registers for notifications that the associated binder is dead. The same death recipient may be
  * associated with multiple different binders. If the binder is local, then no death recipient will
  * be given (since if the local process dies, then no recipient will exist to recieve a
diff --git a/libs/binder/ndk/include_ndk/android/binder_interface_utils.h b/libs/binder/ndk/include_ndk/android/binder_interface_utils.h
index a42c60b..b82141c 100644
--- a/libs/binder/ndk/include_ndk/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_ndk/android/binder_interface_utils.h
@@ -104,6 +104,30 @@
      * this will be checked using AIBinder_isRemote.
      */
     virtual bool isRemote() = 0;
+
+    /**
+     * Dumps information about the interface.
+     */
+    virtual binder_status_t dump(int /*fd*/, const char** /*args*/, uint32_t /*numArgs*/) {
+        return STATUS_OK;
+    }
+
+    /**
+     * Helper method to create a class
+     */
+    static AIBinder_Class* defineClass(const char* interfaceDescriptor,
+                                       AIBinder_Class_onCreate onCreate,
+                                       AIBinder_Class_onDestroy onDestroy,
+                                       AIBinder_Class_onTransact onTransact,
+                                       AIBinder_onDump onDump = nullptr) {
+        AIBinder_Class* clazz =
+                AIBinder_Class_define(interfaceDescriptor, onCreate, onDestroy, onTransact);
+        if (clazz == nullptr) {
+            return nullptr;
+        }
+        AIBinder_Class_setOnDump(clazz, onDump);
+        return clazz;
+    }
 };
 
 /**
@@ -144,6 +168,10 @@
 
     bool isRemote() override { return AIBinder_isRemote(mBinder.get()); }
 
+    binder_status_t dump(int fd, const char** args, uint32_t numArgs) override {
+        return AIBinder_dump(asBinder().get(), fd, args, numArgs);
+    }
+
    private:
     SpAIBinder mBinder;
 };
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 655f4d5..7e65817 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -2,10 +2,12 @@
   global:
     AIBinder_associateClass;
     AIBinder_Class_define;
+    AIBinder_Class_setOnDump;
     AIBinder_DeathRecipient_delete;
     AIBinder_DeathRecipient_new;
     AIBinder_debugGetRefCount;
     AIBinder_decStrong;
+    AIBinder_dump;
     AIBinder_fromJavaBinder;
     AIBinder_getCallingPid;
     AIBinder_getCallingUid;
diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp
index 4cddf94..5fd4a95 100644
--- a/libs/cputimeinstate/cputimeinstate.cpp
+++ b/libs/cputimeinstate/cputimeinstate.cpp
@@ -45,14 +45,14 @@
 namespace android {
 namespace bpf {
 
-typedef struct {
+struct time_key_t {
     uint32_t uid;
     uint32_t freq;
-} time_key_t;
+};
 
-typedef struct {
+struct val_t {
     uint64_t ar[100];
-} val_t;
+};
 
 static std::mutex gInitializedMutex;
 static bool gInitialized = false;
@@ -65,19 +65,13 @@
 static bool readNumbersFromFile(const std::string &path, std::vector<uint32_t> *out) {
     std::string data;
 
-    if (!android::base::ReadFileToString(path, &data)) {
-        ALOGD("Failed to read file %s", path.c_str());
-        return false;
-    }
+    if (!android::base::ReadFileToString(path, &data)) return false;
 
     auto strings = android::base::Split(data, " \n");
     for (const auto &s : strings) {
         if (s.empty()) continue;
         uint32_t n;
-        if (!android::base::ParseUint(s, &n)) {
-            ALOGD("Failed to parse file %s", path.c_str());
-            return false;
-        }
+        if (!android::base::ParseUint(s, &n)) return false;
         out->emplace_back(n);
     }
     return true;
@@ -141,16 +135,8 @@
     std::string path = StringPrintf(BPF_FS_PATH "prog_time_in_state_tracepoint_%s_%s",
                                     eventType.c_str(), eventName.c_str());
     int prog_fd = bpf_obj_get(path.c_str());
-    if (prog_fd < 0) {
-        ALOGD("bpf_obj_get() failed for program %s", path.c_str());
-        return false;
-    }
-    if (bpf_attach_tracepoint(prog_fd, eventType.c_str(), eventName.c_str()) < 0) {
-        ALOGD("Failed to attach bpf program to tracepoint %s/%s", eventType.c_str(),
-              eventName.c_str());
-        return false;
-    }
-    return true;
+    if (prog_fd < 0) return false;
+    return bpf_attach_tracepoint(prog_fd, eventType.c_str(), eventName.c_str()) >= 0;
 }
 
 // Start tracking and aggregating data to be reported by getUidCpuFreqTimes and getUidsCpuFreqTimes.
@@ -224,20 +210,16 @@
     auto fn = [freqTimeMap, &policyFreqIdxs](const time_key_t &key, const val_t &val,
                                              const BpfMap<time_key_t, val_t> &) {
         if (freqTimeMap->find(key.uid) == freqTimeMap->end()) {
-            std::vector<std::vector<uint64_t>> v;
+            (*freqTimeMap)[key.uid].resize(gNPolicies);
             for (uint32_t i = 0; i < gNPolicies; ++i) {
-                std::vector<uint64_t> v2(gPolicyFreqs[i].size(), 0);
-                v.emplace_back(v2);
+                (*freqTimeMap)[key.uid][i].resize(gPolicyFreqs[i].size(), 0);
             }
-            (*freqTimeMap)[key.uid] = v;
         }
 
         for (size_t policy = 0; policy < gNPolicies; ++policy) {
             for (const auto &cpu : gPolicyCpus[policy]) {
-                uint32_t cpuTime = val.ar[cpu];
-                if (cpuTime == 0) continue;
                 auto freqIdx = policyFreqIdxs[policy][key.freq];
-                (*freqTimeMap)[key.uid][policy][freqIdx] += cpuTime;
+                (*freqTimeMap)[key.uid][policy][freqIdx] += val.ar[cpu];
             }
         }
         return android::netdutils::status::ok;
diff --git a/libs/cputimeinstate/cputimeinstate.h b/libs/cputimeinstate/cputimeinstate.h
index 0205452..9f6103e 100644
--- a/libs/cputimeinstate/cputimeinstate.h
+++ b/libs/cputimeinstate/cputimeinstate.h
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#pragma once
+
 #include <unordered_map>
 #include <vector>
 
diff --git a/libs/graphicsenv/IGpuService.cpp b/libs/graphicsenv/IGpuService.cpp
index f755e00..0b0bf44 100644
--- a/libs/graphicsenv/IGpuService.cpp
+++ b/libs/graphicsenv/IGpuService.cpp
@@ -46,6 +46,27 @@
 
         remote()->transact(BnGpuService::SET_GPU_STATS, data, &reply, IBinder::FLAG_ONEWAY);
     }
+
+    virtual status_t getGpuStatsGlobalInfo(std::vector<GpuStatsGlobalInfo>* outStats) const {
+        if (!outStats) return UNEXPECTED_NULL;
+
+        Parcel data, reply;
+        status_t status;
+
+        if ((status = data.writeInterfaceToken(IGpuService::getInterfaceDescriptor())) != OK)
+            return status;
+
+        if ((status = remote()->transact(BnGpuService::GET_GPU_STATS_GLOBAL_INFO, data, &reply)) !=
+            OK)
+            return status;
+
+        int32_t result = 0;
+        if ((status = reply.readInt32(&result)) != OK) return status;
+        if (result != OK) return result;
+
+        outStats->clear();
+        return reply.readParcelableVector(outStats);
+    }
 };
 
 IMPLEMENT_META_INTERFACE(GpuService, "android.graphicsenv.IGpuService");
@@ -89,6 +110,19 @@
 
             return OK;
         }
+        case GET_GPU_STATS_GLOBAL_INFO: {
+            CHECK_INTERFACE(IGpuService, data, reply);
+
+            std::vector<GpuStatsGlobalInfo> stats;
+            const status_t result = getGpuStatsGlobalInfo(&stats);
+
+            if ((status = reply->writeInt32(result)) != OK) return status;
+            if (result != OK) return result;
+
+            if ((status = reply->writeParcelableVector(stats)) != OK) return status;
+
+            return OK;
+        }
         case SHELL_COMMAND_TRANSACTION: {
             int in = data.readFileDescriptor();
             int out = data.readFileDescriptor();
diff --git a/libs/graphicsenv/include/graphicsenv/IGpuService.h b/libs/graphicsenv/include/graphicsenv/IGpuService.h
index 5f9340d..e7cdb38 100644
--- a/libs/graphicsenv/include/graphicsenv/IGpuService.h
+++ b/libs/graphicsenv/include/graphicsenv/IGpuService.h
@@ -16,12 +16,13 @@
 
 #pragma once
 
+#include <vector>
+
 #include <binder/IInterface.h>
 #include <cutils/compiler.h>
+#include <graphicsenv/GpuStatsInfo.h>
 #include <graphicsenv/GraphicsEnv.h>
 
-#include <vector>
-
 namespace android {
 
 /*
@@ -38,12 +39,16 @@
                              int64_t driverBuildTime, const std::string& appPackageName,
                              GraphicsEnv::Driver driver, bool isDriverLoaded,
                              int64_t driverLoadingTime) = 0;
+
+    // get GPU global stats from GpuStats module.
+    virtual status_t getGpuStatsGlobalInfo(std::vector<GpuStatsGlobalInfo>* outStats) const = 0;
 };
 
 class BnGpuService : public BnInterface<IGpuService> {
 public:
     enum IGpuServiceTag {
         SET_GPU_STATS = IBinder::FIRST_CALL_TRANSACTION,
+        GET_GPU_STATS_GLOBAL_INFO,
         // Always append new enum to the end.
     };
 
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 962d263..84ba644 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -100,6 +100,7 @@
 
     output.writeFloat(bgColorAlpha);
     output.writeUint32(static_cast<uint32_t>(bgColorDataspace));
+    output.writeBool(colorSpaceAgnostic);
 
     return NO_ERROR;
 }
@@ -177,6 +178,7 @@
 
     bgColorAlpha = input.readFloat();
     bgColorDataspace = static_cast<ui::Dataspace>(input.readUint32());
+    colorSpaceAgnostic = input.readBool();
 
     return NO_ERROR;
 }
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 7a39222..0a4ad46 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -189,7 +189,7 @@
 }
 
 void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
-    std::lock_guard lock(mMutex);
+    std::lock_guard<std::mutex> lock(mMutex);
 
     /* This listener knows all the sp<IBinder> to sp<SurfaceControl> for all its registered
      * callbackIds, except for when Transactions are merged together. This probably cannot be
@@ -242,7 +242,7 @@
     }
 
     int32_t getId(const sp<GraphicBuffer>& buffer) {
-        std::lock_guard lock(mMutex);
+        std::lock_guard<std::mutex> lock(mMutex);
 
         auto itr = mBuffers.find(buffer);
         if (itr == mBuffers.end()) {
@@ -253,7 +253,7 @@
     }
 
     int32_t cache(const sp<GraphicBuffer>& buffer) {
-        std::lock_guard lock(mMutex);
+        std::lock_guard<std::mutex> lock(mMutex);
 
         int32_t bufferId = getNextAvailableId();
 
@@ -989,6 +989,20 @@
     return *this;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setColorSpaceAgnostic(
+        const sp<SurfaceControl>& sc, const bool agnostic) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+    s->what |= layer_state_t::eColorSpaceAgnosticChanged;
+    s->colorSpaceAgnostic = agnostic;
+
+    registerSurfaceControlForCallback(sc);
+    return *this;
+}
+
 SurfaceComposerClient::Transaction&
 SurfaceComposerClient::Transaction::addTransactionCompletedCallback(
         TransactionCompletedCallbackTakesContext callback, void* callbackContext) {
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 32d7391..35e795c 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -88,6 +88,7 @@
         eCachedBufferChanged = 0x2'00000000,
         eBackgroundColorChanged = 0x4'00000000,
         eMetadataChanged = 0x8'00000000,
+        eColorSpaceAgnosticChanged = 0x10'00000000,
     };
 
     layer_state_t()
@@ -115,7 +116,8 @@
             api(-1),
             colorTransform(mat4()),
             bgColorAlpha(0),
-            bgColorDataspace(ui::Dataspace::UNKNOWN) {
+            bgColorDataspace(ui::Dataspace::UNKNOWN),
+            colorSpaceAgnostic(false) {
         matrix.dsdx = matrix.dtdy = 1.0f;
         matrix.dsdy = matrix.dtdx = 0.0f;
         hdrMetadata.validTypes = 0;
@@ -192,6 +194,10 @@
     // the background color layer
     float bgColorAlpha;
     ui::Dataspace bgColorDataspace;
+
+    // A color space agnostic layer means the color of this layer can be
+    // interpreted in any color space.
+    bool colorSpaceAgnostic;
 };
 
 struct ComposerState {
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index e062339..2c483ee 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -360,6 +360,7 @@
         Transaction& setSidebandStream(const sp<SurfaceControl>& sc,
                                        const sp<NativeHandle>& sidebandStream);
         Transaction& setDesiredPresentTime(nsecs_t desiredPresentTime);
+        Transaction& setColorSpaceAgnostic(const sp<SurfaceControl>& sc, const bool agnostic);
 
         Transaction& addTransactionCompletedCallback(
                 TransactionCompletedCallbackTakesContext callback, void* callbackContext);
diff --git a/libs/gui/tests/RegionSampling_test.cpp b/libs/gui/tests/RegionSampling_test.cpp
index 5652c0c..d33ecfb 100644
--- a/libs/gui/tests/RegionSampling_test.cpp
+++ b/libs/gui/tests/RegionSampling_test.cpp
@@ -240,7 +240,7 @@
     float const luma_gray = 0.50;
 };
 
-TEST_F(RegionSamplingTest, CollectsLuma) {
+TEST_F(RegionSamplingTest, DISABLED_CollectsLuma) {
     fill_render(rgba_green);
 
     sp<ISurfaceComposer> composer = ComposerService::getComposerService();
@@ -254,7 +254,7 @@
     composer->removeRegionSamplingListener(listener);
 }
 
-TEST_F(RegionSamplingTest, CollectsChangingLuma) {
+TEST_F(RegionSamplingTest, DISABLED_CollectsChangingLuma) {
     fill_render(rgba_green);
 
     sp<ISurfaceComposer> composer = ComposerService::getComposerService();
@@ -275,7 +275,7 @@
     composer->removeRegionSamplingListener(listener);
 }
 
-TEST_F(RegionSamplingTest, CollectsLumaFromTwoRegions) {
+TEST_F(RegionSamplingTest, DISABLED_CollectsLumaFromTwoRegions) {
     fill_render(rgba_green);
     sp<ISurfaceComposer> composer = ComposerService::getComposerService();
     sp<Listener> greenListener = new Listener();
diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h
index 3ac1c58..2899bcf 100644
--- a/libs/nativewindow/include/android/data_space.h
+++ b/libs/nativewindow/include/android/data_space.h
@@ -75,7 +75,7 @@
      * scRGB:
      *
      * The red, green, and blue components are stored in extended sRGB space,
-     * but are linear, not gamma-encoded.
+     * and gamma-encoded using the SRGB transfer function.
      * The RGB primaries and the white point are the same as BT.709.
      *
      * The values are floating point.
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 5d0aa1e..8069a1a 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -626,22 +626,13 @@
 }
 
 status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer,
-                                                     sp<Fence> bufferFence, bool readCache) {
-    return bindExternalTextureBuffer(texName, buffer, bufferFence, readCache,
-                                     /*persistCache=*/false);
-}
-
-status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer,
-                                                     sp<Fence> bufferFence, bool readCache,
-                                                     bool persistCache) {
+                                                     sp<Fence> bufferFence) {
     ATRACE_CALL();
-    if (readCache) {
-        auto cachedImage = mImageCache.find(buffer->getId());
+    auto cachedImage = mImageCache.find(buffer->getId());
 
-        if (cachedImage != mImageCache.end()) {
-            bindExternalTextureImage(texName, *cachedImage->second);
-            return NO_ERROR;
-        }
+    if (cachedImage != mImageCache.end()) {
+        bindExternalTextureImage(texName, *cachedImage->second);
+        return NO_ERROR;
     }
 
     std::unique_ptr<Image> newImage = createImage();
@@ -678,35 +669,19 @@
             }
         }
     }
-
-    // We don't always want to persist to the cache, e.g. on older devices we
-    // might bind for synchronization purposes, but that might leak if we never
-    // call drawLayers again, so it's just better to recreate the image again
-    // if needed when we draw.
-    if (persistCache) {
-        mImageCache.insert(std::make_pair(buffer->getId(), std::move(newImage)));
-    }
+    mImageCache.insert(std::make_pair(buffer->getId(), std::move(newImage)));
 
     return NO_ERROR;
 }
 
-void GLESRenderEngine::evictImages(const std::vector<LayerSettings>& layers) {
-    ATRACE_CALL();
-    // destroy old image references that we're not going to draw with.
-    std::unordered_set<uint64_t> bufIds;
-    for (auto layer : layers) {
-        if (layer.source.buffer.buffer != nullptr) {
-            bufIds.emplace(layer.source.buffer.buffer->getId());
-        }
+void GLESRenderEngine::unbindExternalTextureBuffer(uint64_t bufferId) {
+    const auto& cachedImage = mImageCache.find(bufferId);
+    if (cachedImage != mImageCache.end()) {
+        ALOGV("Destroying image for buffer: %" PRIu64, bufferId);
+        mImageCache.erase(bufferId);
+        return;
     }
-
-    for (auto it = mImageCache.begin(); it != mImageCache.end();) {
-        if (bufIds.count(it->first) == 0) {
-            it = mImageCache.erase(it);
-        } else {
-            it++;
-        }
-    }
+    ALOGV("Failed to find image for buffer: %" PRIu64, bufferId);
 }
 
 FloatRect GLESRenderEngine::setupLayerCropping(const LayerSettings& layer, Mesh& mesh) {
@@ -844,8 +819,6 @@
         return fbo.getStatus();
     }
 
-    evictImages(layers);
-
     // clear the entire buffer, sometimes when we reuse buffers we'd persist
     // ghost images otherwise.
     // we also require a full transparent framebuffer for overlays. This is
@@ -888,10 +861,8 @@
             isOpaque = layer.source.buffer.isOpaque;
 
             sp<GraphicBuffer> gBuf = layer.source.buffer.buffer;
-
-            bool readCache = layer.source.buffer.cacheHint == Buffer::CachingHint::USE_CACHE;
             bindExternalTextureBuffer(layer.source.buffer.textureName, gBuf,
-                                      layer.source.buffer.fence, readCache, /*persistCache=*/true);
+                                      layer.source.buffer.fence);
 
             usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha;
             Texture texture(Texture::TEXTURE_EXTERNAL, layer.source.buffer.textureName);
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index e37c91d..728882a 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -74,8 +74,8 @@
     void genTextures(size_t count, uint32_t* names) override;
     void deleteTextures(size_t count, uint32_t const* names) override;
     void bindExternalTextureImage(uint32_t texName, const Image& image) override;
-    status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer, sp<Fence> fence,
-                                       bool readCache);
+    status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer, sp<Fence> fence);
+    void unbindExternalTextureBuffer(uint64_t bufferId);
     status_t bindFrameBuffer(Framebuffer* framebuffer) override;
     void unbindFrameBuffer(Framebuffer* framebuffer) override;
     void checkErrors() const override;
@@ -143,8 +143,6 @@
     // Defines the viewport, and sets the projection matrix to the projection
     // defined by the clip.
     void setViewportAndProjection(Rect viewport, Rect clip);
-    status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer, sp<Fence> fence,
-                                       bool readCache, bool persistCache);
     // Evicts stale images from the buffer cache.
     void evictImages(const std::vector<LayerSettings>& layers);
     // Computes the cropping window for the layer and sets up cropping
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index aa45ed8..b8bf801 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -32,16 +32,6 @@
 
 // Metadata describing the input buffer to render from.
 struct Buffer {
-    // Hint for whether to use the Image cache or not.
-    // If NO_CACHE is specified, then upload the contents of the GraphicBuffer
-    // to the GPU, without checking against any implementation defined cache.
-    // If USE_CACHE is specified, then check against an implementation defined
-    // cache first. If there is an Image cached for the given GraphicBuffer id,
-    // then use that instead of the provided buffer contents. If there is no
-    // cached image or the RenderEngine implementation does not support caching,
-    // then use the GraphicBuffer contents.
-    enum class CachingHint { NO_CACHE, USE_CACHE };
-
     // Buffer containing the image that we will render.
     // If buffer == nullptr, then the rest of the fields in this struct will be
     // ignored.
@@ -50,9 +40,6 @@
     // Fence that will fire when the buffer is ready to be bound.
     sp<Fence> fence = nullptr;
 
-    // Caching hint to use when uploading buffer contents.
-    CachingHint cacheHint = CachingHint::NO_CACHE;
-
     // Texture identifier to bind the external texture to.
     // TODO(alecmouri): This is GL-specific...make the type backend-agnostic.
     uint32_t textureName = 0;
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 2a2b48f..ab34274 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -110,7 +110,11 @@
     virtual void deleteTextures(size_t count, uint32_t const* names) = 0;
     virtual void bindExternalTextureImage(uint32_t texName, const Image& image) = 0;
     virtual status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer,
-                                               sp<Fence> fence, bool cleanCache) = 0;
+                                               sp<Fence> fence) = 0;
+    // Removes internal resources referenced by the bufferId. This method should be
+    // invoked when the caller will no longer hold a reference to a GraphicBuffer
+    // and needs to clean up its resources.
+    virtual void unbindExternalTextureBuffer(uint64_t bufferId) = 0;
     // When binding a native buffer, it must be done before setViewportAndProjection
     // Returns NO_ERROR when binds successfully, NO_MEMORY when there's no memory for allocation.
     virtual status_t bindFrameBuffer(Framebuffer* framebuffer) = 0;
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index 5956c46..ddf7420 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -53,7 +53,8 @@
     MOCK_METHOD2(genTextures, void(size_t, uint32_t*));
     MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*));
     MOCK_METHOD2(bindExternalTextureImage, void(uint32_t, const renderengine::Image&));
-    MOCK_METHOD4(bindExternalTextureBuffer, status_t(uint32_t, sp<GraphicBuffer>, sp<Fence>, bool));
+    MOCK_METHOD3(bindExternalTextureBuffer, status_t(uint32_t, sp<GraphicBuffer>, sp<Fence>));
+    MOCK_METHOD1(unbindExternalTextureBuffer, void(uint64_t));
     MOCK_CONST_METHOD0(checkErrors, void());
     MOCK_METHOD4(setViewportAndProjection,
                  void(size_t, size_t, Rect, ui::Transform::orientation_flags));
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index af95a05..4ca1781 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -90,8 +90,6 @@
         "android.hardware.graphics.mapper@2.0",
         "android.hardware.graphics.mapper@2.1",
         "android.hardware.graphics.mapper@3.0",
-        "android.hardware.configstore@1.2",
-        "android.hardware.configstore-utils",
         "libbase",
         "libcutils",
         "libhardware",
@@ -105,7 +103,6 @@
     ],
 
     export_shared_lib_headers: [
-        "android.hardware.configstore@1.2",
         "android.hardware.graphics.common@1.2",
     ],
 
diff --git a/libs/ui/include/ui/ConfigStoreTypes.h b/libs/ui/include/ui/ConfigStoreTypes.h
index 37a2bd5..4445ae9 100644
--- a/libs/ui/include/ui/ConfigStoreTypes.h
+++ b/libs/ui/include/ui/ConfigStoreTypes.h
@@ -16,14 +16,22 @@
 
 #pragma once
 
-#include <android/hardware/configstore/1.2/types.h>
-
 // android::ui::* in this header file will alias different types as
 // the HIDL interface is updated.
 namespace android {
 namespace ui {
 
-using android::hardware::configstore::V1_2::DisplayPrimaries;
+struct CieXyz {
+    float X;
+    float Y;
+    float Z;
+};
+struct DisplayPrimaries {
+    CieXyz red;
+    CieXyz green;
+    CieXyz blue;
+    CieXyz white;
+};
 
 }  // namespace ui
 }  // namespace android
diff --git a/libs/ui/include_vndk/ui/ConfigStoreTypes.h b/libs/ui/include_vndk/ui/ConfigStoreTypes.h
index 37a2bd5..4445ae9 100644
--- a/libs/ui/include_vndk/ui/ConfigStoreTypes.h
+++ b/libs/ui/include_vndk/ui/ConfigStoreTypes.h
@@ -16,14 +16,22 @@
 
 #pragma once
 
-#include <android/hardware/configstore/1.2/types.h>
-
 // android::ui::* in this header file will alias different types as
 // the HIDL interface is updated.
 namespace android {
 namespace ui {
 
-using android::hardware::configstore::V1_2::DisplayPrimaries;
+struct CieXyz {
+    float X;
+    float Y;
+    float Z;
+};
+struct DisplayPrimaries {
+    CieXyz red;
+    CieXyz green;
+    CieXyz blue;
+    CieXyz white;
+};
 
 }  // namespace ui
 }  // namespace android
diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp
index a73705b..5e5cf35 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -59,6 +59,14 @@
                       appPackageName, driver, isDriverLoaded, driverLoadingTime);
 }
 
+status_t GpuService::getGpuStatsGlobalInfo(std::vector<GpuStatsGlobalInfo>* outStats) const {
+    ATRACE_CALL();
+
+    mGpuStats->pullGlobalStats(outStats);
+
+    return OK;
+}
+
 status_t GpuService::shellCommand(int /*in*/, int out, int err, std::vector<String16>& args) {
     ATRACE_CALL();
 
diff --git a/services/gpuservice/GpuService.h b/services/gpuservice/GpuService.h
index 0cf48bb..b984e0f 100644
--- a/services/gpuservice/GpuService.h
+++ b/services/gpuservice/GpuService.h
@@ -19,6 +19,7 @@
 
 #include <binder/IInterface.h>
 #include <cutils/compiler.h>
+#include <graphicsenv/GpuStatsInfo.h>
 #include <graphicsenv/IGpuService.h>
 #include <serviceutils/PriorityDumper.h>
 
@@ -45,7 +46,8 @@
     void setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName,
                      uint64_t driverVersionCode, int64_t driverBuildTime,
                      const std::string& appPackageName, GraphicsEnv::Driver driver,
-                     bool isDriverLoaded, int64_t driverLoadingTime);
+                     bool isDriverLoaded, int64_t driverLoadingTime) override;
+    status_t getGpuStatsGlobalInfo(std::vector<GpuStatsGlobalInfo>* outStats) const override;
 
     /*
      * IBinder interface
diff --git a/services/gpuservice/gpustats/GpuStats.cpp b/services/gpuservice/gpustats/GpuStats.cpp
index c4256df..146e2c2 100644
--- a/services/gpuservice/gpustats/GpuStats.cpp
+++ b/services/gpuservice/gpustats/GpuStats.cpp
@@ -184,4 +184,18 @@
     }
 }
 
+void GpuStats::pullGlobalStats(std::vector<GpuStatsGlobalInfo>* outStats) {
+    ATRACE_CALL();
+
+    std::lock_guard<std::mutex> lock(mLock);
+    outStats->clear();
+    outStats->reserve(mGlobalStats.size());
+
+    for (const auto& ele : mGlobalStats) {
+        outStats->emplace_back(ele.second);
+    }
+
+    mGlobalStats.clear();
+}
+
 } // namespace android
diff --git a/services/gpuservice/gpustats/GpuStats.h b/services/gpuservice/gpustats/GpuStats.h
index da7fd33..9cdcd95 100644
--- a/services/gpuservice/gpustats/GpuStats.h
+++ b/services/gpuservice/gpustats/GpuStats.h
@@ -39,6 +39,8 @@
                 int64_t driverLoadingTime);
     // dumpsys interface
     void dump(const Vector<String16>& args, std::string* result);
+    // Pull gpu global stats
+    void pullGlobalStats(std::vector<GpuStatsGlobalInfo>* outStats);
 
 private:
     // Dump global stats
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 9bf499b..ac1d492 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -23,7 +23,6 @@
         "android.hardware.configstore-utils",
         "android.hardware.configstore@1.0",
         "android.hardware.configstore@1.1",
-        "android.hardware.configstore@1.2",
         "android.hardware.graphics.allocator@2.0",
         "android.hardware.graphics.allocator@3.0",
         "android.hardware.graphics.common@1.2",
@@ -57,7 +56,6 @@
         "libui",
         "libinput",
         "libutils",
-        "libutilscallstack",
         "libSurfaceFlingerProperties",
     ],
     static_libs: [
@@ -189,7 +187,6 @@
         "android.frameworks.displayservice@1.0",
         "android.hardware.configstore-utils",
         "android.hardware.configstore@1.0",
-        "android.hardware.configstore@1.2",
         "android.hardware.graphics.allocator@2.0",
         "android.hardware.graphics.allocator@3.0",
         "libbinder",
@@ -244,13 +241,15 @@
         "android.hardware.configstore-utils",
         "android.hardware.configstore@1.0",
         "android.hardware.configstore@1.1",
-        "android.hardware.configstore@1.2",
+        "android.hardware.graphics.common@1.2",
         "libhidlbase",
         "libhidltransport",
         "libhwbinder",
+        "libui",
         "libutils",
     ],
     export_shared_lib_headers: [
+        "android.hardware.graphics.common@1.2",
         "libhidlbase",
         "libhidltransport",
         "libhwbinder",
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 6badc73..7884362 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -163,9 +163,6 @@
         layer.source.buffer.buffer = mActiveBuffer;
         layer.source.buffer.isOpaque = isOpaque(s);
         layer.source.buffer.fence = mActiveBufferFence;
-        layer.source.buffer.cacheHint = useCachedBufferForClientComposition()
-                ? renderengine::Buffer::CachingHint::USE_CACHE
-                : renderengine::Buffer::CachingHint::NO_CACHE;
         layer.source.buffer.textureName = mTextureName;
         layer.source.buffer.usePremultipliedAlpha = getPremultipledAlpha();
         layer.source.buffer.isY410BT2020 = isHdrY410();
@@ -242,7 +239,8 @@
 
 void BufferLayer::setPerFrameData(const sp<const DisplayDevice>& displayDevice,
                                   const ui::Transform& transform, const Rect& viewport,
-                                  int32_t supportedPerFrameMetadata) {
+                                  int32_t supportedPerFrameMetadata,
+                                  const ui::Dataspace targetDataspace) {
     RETURN_IF_NO_HWC_LAYER(displayDevice);
 
     // Apply this display's projection's viewport to the visible region
@@ -294,10 +292,10 @@
         setCompositionType(displayDevice, Hwc2::IComposerClient::Composition::DEVICE);
     }
 
-    ALOGV("setPerFrameData: dataspace = %d", mCurrentDataSpace);
-    error = hwcLayer->setDataspace(mCurrentDataSpace);
+    ui::Dataspace dataspace = isColorSpaceAgnostic() ? targetDataspace : mCurrentDataSpace;
+    error = hwcLayer->setDataspace(dataspace);
     if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), mCurrentDataSpace,
+        ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), dataspace,
               to_string(error).c_str(), static_cast<int32_t>(error));
     }
 
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index c48146f..8149cba 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -81,7 +81,8 @@
     bool isHdrY410() const override;
 
     void setPerFrameData(const sp<const DisplayDevice>& display, const ui::Transform& transform,
-                         const Rect& viewport, int32_t supportedPerFrameMetadata) override;
+                         const Rect& viewport, int32_t supportedPerFrameMetadata,
+                         const ui::Dataspace targetDataspace) override;
 
     bool onPreComposition(nsecs_t refreshStartTime) override;
     bool onPostComposition(const std::optional<DisplayId>& displayId,
@@ -161,10 +162,6 @@
 
     bool mRefreshPending{false};
 
-    // Returns true if, when drawing the active buffer during gpu compositon, we
-    // should use a cached buffer or not.
-    virtual bool useCachedBufferForClientComposition() const = 0;
-
     // prepareClientLayer - constructs a RenderEngine layer for GPU composition.
     bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
                             bool useIdentityTransform, Region& clearRegion,
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 6866e5c..7d2dcba 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -178,7 +178,8 @@
         return;
     }
 
-    auto buffer = mPendingRelease.isPending ? mPendingRelease.graphicBuffer : mCurrentTextureBuffer;
+    auto buffer = mPendingRelease.isPending ? mPendingRelease.graphicBuffer
+                                            : mCurrentTextureBuffer->graphicBuffer();
     auto err = addReleaseFence(slot, buffer, fence);
     if (err != OK) {
         BLC_LOGE("setReleaseFence: failed to add the fence: %s (%d)", strerror(-err), err);
@@ -214,9 +215,9 @@
     }
 
     // If item->mGraphicBuffer is not null, this buffer has not been acquired
-    // before.
+    // before, so we need to clean up old references.
     if (item->mGraphicBuffer != nullptr) {
-        mImages[item->mSlot] = nullptr;
+        mImages[item->mSlot] = std::make_shared<Image>(item->mGraphicBuffer, mRE);
     }
 
     return NO_ERROR;
@@ -229,18 +230,21 @@
     int slot = item.mSlot;
 
     BLC_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture,
-             mCurrentTextureBuffer != nullptr ? mCurrentTextureBuffer->handle : 0, slot,
-             mSlots[slot].mGraphicBuffer->handle);
+             (mCurrentTextureBuffer != nullptr && mCurrentTextureBuffer->graphicBuffer() != nullptr)
+                     ? mCurrentTextureBuffer->graphicBuffer()->handle
+                     : 0,
+             slot, mSlots[slot].mGraphicBuffer->handle);
 
     // Hang onto the pointer so that it isn't freed in the call to
     // releaseBufferLocked() if we're in shared buffer mode and both buffers are
     // the same.
-    sp<GraphicBuffer> nextTextureBuffer = mSlots[slot].mGraphicBuffer;
+    std::shared_ptr<Image> nextTextureBuffer = mImages[slot];
 
     // release old buffer
     if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
         if (pendingRelease == nullptr) {
-            status_t status = releaseBufferLocked(mCurrentTexture, mCurrentTextureBuffer);
+            status_t status =
+                    releaseBufferLocked(mCurrentTexture, mCurrentTextureBuffer->graphicBuffer());
             if (status < NO_ERROR) {
                 BLC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status),
                          status);
@@ -249,7 +253,7 @@
             }
         } else {
             pendingRelease->currentTexture = mCurrentTexture;
-            pendingRelease->graphicBuffer = mCurrentTextureBuffer;
+            pendingRelease->graphicBuffer = mCurrentTextureBuffer->graphicBuffer();
             pendingRelease->isPending = true;
         }
     }
@@ -257,8 +261,6 @@
     // Update the BufferLayerConsumer state.
     mCurrentTexture = slot;
     mCurrentTextureBuffer = nextTextureBuffer;
-    mCurrentTextureBufferStaleForGpu = false;
-    mCurrentTextureImageFreed = nullptr;
     mCurrentCrop = item.mCrop;
     mCurrentTransform = item.mTransform;
     mCurrentScalingMode = item.mScalingMode;
@@ -280,7 +282,12 @@
 status_t BufferLayerConsumer::bindTextureImageLocked() {
     ATRACE_CALL();
 
-    return mRE.bindExternalTextureBuffer(mTexName, mCurrentTextureBuffer, mCurrentFence, false);
+    if (mCurrentTextureBuffer != nullptr && mCurrentTextureBuffer->graphicBuffer() != nullptr) {
+        return mRE.bindExternalTextureBuffer(mTexName, mCurrentTextureBuffer->graphicBuffer(),
+                                             mCurrentFence);
+    }
+
+    return NO_INIT;
 }
 
 void BufferLayerConsumer::getTransformMatrix(float mtx[16]) {
@@ -308,12 +315,15 @@
 
 void BufferLayerConsumer::computeCurrentTransformMatrixLocked() {
     BLC_LOGV("computeCurrentTransformMatrixLocked");
-    if (mCurrentTextureBuffer == nullptr) {
+    if (mCurrentTextureBuffer == nullptr || mCurrentTextureBuffer->graphicBuffer() == nullptr) {
         BLC_LOGD("computeCurrentTransformMatrixLocked: "
                  "mCurrentTextureBuffer is nullptr");
     }
-    GLConsumer::computeTransformMatrix(mCurrentTransformMatrix, mCurrentTextureBuffer, mCurrentCrop,
-                                       mCurrentTransform, mFilteringEnabled);
+    GLConsumer::computeTransformMatrix(mCurrentTransformMatrix,
+                                       mCurrentTextureBuffer == nullptr
+                                               ? nullptr
+                                               : mCurrentTextureBuffer->graphicBuffer(),
+                                       mCurrentCrop, mCurrentTransform, mFilteringEnabled);
 }
 
 nsecs_t BufferLayerConsumer::getTimestamp() {
@@ -365,16 +375,7 @@
         *outFence = mCurrentFence;
     }
 
-    return mCurrentTextureBuffer;
-}
-
-bool BufferLayerConsumer::getAndSetCurrentBufferCacheHint() {
-    Mutex::Autolock lock(mMutex);
-    bool useCache = mCurrentTextureBufferStaleForGpu;
-    // Set the staleness bit here, as this function is only called during a
-    // client composition path.
-    mCurrentTextureBufferStaleForGpu = true;
-    return useCache;
+    return mCurrentTextureBuffer == nullptr ? nullptr : mCurrentTextureBuffer->graphicBuffer();
 }
 
 Rect BufferLayerConsumer::getCurrentCrop() const {
@@ -432,10 +433,8 @@
     BLC_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
     if (slotIndex == mCurrentTexture) {
         mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
-        mCurrentTextureImageFreed = std::move(mImages[slotIndex]);
-    } else {
-        mImages[slotIndex] = nullptr;
     }
+    mImages[slotIndex] = nullptr;
     ConsumerBase::freeBufferLocked(slotIndex);
 }
 
@@ -474,7 +473,10 @@
 
 void BufferLayerConsumer::abandonLocked() {
     BLC_LOGV("abandonLocked");
-    mCurrentTextureBuffer.clear();
+    mCurrentTextureBuffer = nullptr;
+    for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+        mImages[i] = nullptr;
+    }
     ConsumerBase::abandonLocked();
 }
 
@@ -492,4 +494,11 @@
     ConsumerBase::dumpLocked(result, prefix);
 }
 
+BufferLayerConsumer::Image::~Image() {
+    if (mGraphicBuffer != nullptr) {
+        ALOGE("Destroying buffer: %" PRId64, mGraphicBuffer->getId());
+        mRE.unbindExternalTextureBuffer(mGraphicBuffer->getId());
+    }
+}
+
 }; // namespace android
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
index e2a6d2e..32ccfbb 100644
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ b/services/surfaceflinger/BufferLayerConsumer.h
@@ -149,11 +149,6 @@
     // for use with bilinear filtering.
     void setFilteringEnabled(bool enabled);
 
-    // Sets mCurrentTextureBufferStaleForGpu to true to indicate that the
-    // buffer is now "stale" for GPU composition, and returns the old staleness
-    // bit as a caching hint.
-    bool getAndSetCurrentBufferCacheHint();
-
     // getCurrentBuffer returns the buffer associated with the current image.
     // When outSlot is not nullptr, the current buffer slot index is also
     // returned. Simiarly, when outFence is not nullptr, the current output
@@ -218,6 +213,22 @@
     status_t bindTextureImageLocked();
 
 private:
+    // Utility class for managing GraphicBuffer references into renderengine
+    class Image {
+    public:
+        Image(sp<GraphicBuffer> graphicBuffer, renderengine::RenderEngine& engine)
+              : mGraphicBuffer(graphicBuffer), mRE(engine) {}
+        virtual ~Image();
+        const sp<GraphicBuffer>& graphicBuffer() { return mGraphicBuffer; }
+
+    private:
+        // mGraphicBuffer is the buffer that was used to create this image.
+        sp<GraphicBuffer> mGraphicBuffer;
+        // Back-reference into renderengine to initiate cleanup.
+        renderengine::RenderEngine& mRE;
+        DISALLOW_COPY_AND_ASSIGN(Image);
+    };
+
     // freeBufferLocked frees up the given buffer slot. If the slot has been
     // initialized this will release the reference to the GraphicBuffer in
     // that slot.  Otherwise it has no effect.
@@ -248,14 +259,10 @@
     // consume buffers as hardware textures.
     static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE;
 
-    // mCurrentTextureImage is the buffer containing the current texture. It's
+    // mCurrentTextureBuffer is the buffer containing the current texture. It's
     // possible that this buffer is not associated with any buffer slot, so we
     // must track it separately in order to support the getCurrentBuffer method.
-    sp<GraphicBuffer> mCurrentTextureBuffer;
-
-    // True if the buffer was used for the previous client composition frame,
-    // and false otherwise.
-    bool mCurrentTextureBufferStaleForGpu;
+    std::shared_ptr<Image> mCurrentTextureBuffer;
 
     // mCurrentCrop is the crop rectangle that applies to the current texture.
     // It gets set each time updateTexImage is called.
@@ -333,16 +340,8 @@
     // reset mCurrentTexture to INVALID_BUFFER_SLOT.
     int mCurrentTexture;
 
-    // Cached image used for rendering the current texture through GPU
-    // composition, which contains the cached image after freeBufferLocked is
-    // called on the current buffer. Whenever latchBuffer is called, this is
-    // expected to be cleared. Then, if bindTexImage is called before the next
-    // buffer is acquired, then this image is bound.
-    std::unique_ptr<renderengine::Image> mCurrentTextureImageFreed;
-
-    // Cached images used for rendering the current texture through GPU
-    // composition.
-    std::unique_ptr<renderengine::Image> mImages[BufferQueueDefs::NUM_BUFFER_SLOTS];
+    // Shadow buffer cache for cleaning up renderengine references.
+    std::shared_ptr<Image> mImages[BufferQueueDefs::NUM_BUFFER_SLOTS];
 
     // A release that is pending on the receipt of a new release fence from
     // presentDisplay
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 215dea1..da34083 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -342,10 +342,6 @@
     return NO_ERROR;
 }
 
-bool BufferQueueLayer::useCachedBufferForClientComposition() const {
-    return mConsumer->getAndSetCurrentBufferCacheHint();
-}
-
 status_t BufferQueueLayer::updateFrameNumber(nsecs_t latchTime) {
     mPreviousFrameNumber = mCurrentFrameNumber;
     mCurrentFrameNumber = mConsumer->getFrameNumber();
@@ -366,7 +362,7 @@
     uint32_t hwcSlot = 0;
     sp<GraphicBuffer> hwcBuffer;
     (*outputLayer->editState().hwc)
-            .hwcBufferCache.getHwcBuffer(mActiveBufferSlot, mActiveBuffer, &hwcSlot, &hwcBuffer);
+            .hwcBufferCache.getHwcBuffer(mActiveBuffer, &hwcSlot, &hwcBuffer);
 
     auto acquireFence = mConsumer->getCurrentFence();
     auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence);
@@ -396,7 +392,7 @@
 void BufferQueueLayer::onFrameAvailable(const BufferItem& item) {
     // Add this buffer from our internal queue tracker
     { // Autolock scope
-        if (mFlinger->mUse90Hz && mFlinger->mUseSmart90ForVideo) {
+        if (mFlinger->mUseSmart90ForVideo) {
             // Report the requested present time to the Scheduler, if the feature is turned on.
             mFlinger->mScheduler->addFramePresentTimeForLayer(item.mTimestamp,
                                                               item.mIsAutoTimestamp, mName.c_str());
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index fbb8c14..a2aad17 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -62,9 +62,6 @@
 public:
     bool fenceHasSignaled() const override;
 
-protected:
-    bool useCachedBufferForClientComposition() const override;
-
 private:
     nsecs_t getDesiredPresentTime() override;
     std::shared_ptr<FenceTime> getCurrentFenceTime() const override;
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index f6b69eb..b2383ad 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -48,7 +48,12 @@
     mOverrideScalingMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
     mCurrentState.dataspace = ui::Dataspace::V0_SRGB;
 }
-BufferStateLayer::~BufferStateLayer() = default;
+BufferStateLayer::~BufferStateLayer() {
+    if (mActiveBuffer != nullptr) {
+        auto& engine(mFlinger->getRenderEngine());
+        engine.unbindExternalTextureBuffer(mActiveBuffer->getId());
+    }
+}
 
 // -----------------------------------------------------------------------
 // Interface implementation for Layer
@@ -468,7 +473,7 @@
     const State& s(getDrawingState());
     auto& engine(mFlinger->getRenderEngine());
 
-    return engine.bindExternalTextureBuffer(mTextureName, s.buffer, s.acquireFence, false);
+    return engine.bindExternalTextureBuffer(mTextureName, s.buffer, s.acquireFence);
 }
 
 status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime) {
@@ -542,6 +547,11 @@
         return BAD_VALUE;
     }
 
+    if (mActiveBuffer != nullptr) {
+        // todo: get this to work with BufferStateLayerCache
+        auto& engine(mFlinger->getRenderEngine());
+        engine.unbindExternalTextureBuffer(mActiveBuffer->getId());
+    }
     mActiveBuffer = s.buffer;
     mActiveBufferFence = s.acquireFence;
     auto& layerCompositionState = getCompositionLayer()->editState().frontEnd;
@@ -551,11 +561,6 @@
     return NO_ERROR;
 }
 
-bool BufferStateLayer::useCachedBufferForClientComposition() const {
-    // TODO: Store a proper staleness bit to support EGLImage caching.
-    return false;
-}
-
 status_t BufferStateLayer::updateFrameNumber(nsecs_t /*latchTime*/) {
     // TODO(marissaw): support frame history events
     mCurrentFrameNumber = mFrameNumber;
@@ -565,14 +570,17 @@
 void BufferStateLayer::setHwcLayerBuffer(const sp<const DisplayDevice>& display) {
     const auto outputLayer = findOutputLayerForDisplay(display);
     LOG_FATAL_IF(!outputLayer || !outputLayer->getState().hwc);
-    auto& hwcLayer = (*outputLayer->getState().hwc).hwcLayer;
+    auto& hwcInfo = *outputLayer->editState().hwc;
+    auto& hwcLayer = hwcInfo.hwcLayer;
 
     const State& s(getDrawingState());
 
-    // TODO(marissaw): support more than one slot
+    // obtain slot
     uint32_t hwcSlot = 0;
+    sp<GraphicBuffer> buffer;
+    hwcInfo.hwcBufferCache.getHwcBuffer(s.buffer, &hwcSlot, &buffer);
 
-    auto error = hwcLayer->setBuffer(hwcSlot, s.buffer, s.acquireFence);
+    auto error = hwcLayer->setBuffer(hwcSlot, buffer, s.acquireFence);
     if (error != HWC2::Error::None) {
         ALOGE("[%s] Failed to set buffer %p: %s (%d)", mName.string(),
               s.buffer->handle, to_string(error).c_str(), static_cast<int32_t>(error));
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 0b03f49..97662e8 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -96,9 +96,6 @@
     // -----------------------------------------------------------------------
     bool fenceHasSignaled() const override;
 
-protected:
-    bool useCachedBufferForClientComposition() const override;
-
 private:
     nsecs_t getDesiredPresentTime() override;
     std::shared_ptr<FenceTime> getCurrentFenceTime() const override;
diff --git a/services/surfaceflinger/ColorLayer.cpp b/services/surfaceflinger/ColorLayer.cpp
index 2aeece7..a2692bc 100644
--- a/services/surfaceflinger/ColorLayer.cpp
+++ b/services/surfaceflinger/ColorLayer.cpp
@@ -81,7 +81,8 @@
 
 void ColorLayer::setPerFrameData(const sp<const DisplayDevice>& display,
                                  const ui::Transform& transform, const Rect& viewport,
-                                 int32_t /* supportedPerFrameMetadata */) {
+                                 int32_t /* supportedPerFrameMetadata */,
+                                 const ui::Dataspace targetDataspace) {
     RETURN_IF_NO_HWC_LAYER(display);
 
     Region visible = transform.transform(visibleRegion.intersect(viewport));
@@ -101,9 +102,10 @@
 
     setCompositionType(display, Hwc2::IComposerClient::Composition::SOLID_COLOR);
 
-    error = hwcLayer->setDataspace(mCurrentDataSpace);
+    const ui::Dataspace dataspace = isColorSpaceAgnostic() ? targetDataspace : mCurrentDataSpace;
+    error = hwcLayer->setDataspace(dataspace);
     if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), mCurrentDataSpace,
+        ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), dataspace,
               to_string(error).c_str(), static_cast<int32_t>(error));
     }
 
diff --git a/services/surfaceflinger/ColorLayer.h b/services/surfaceflinger/ColorLayer.h
index 9786419..9a72b40 100644
--- a/services/surfaceflinger/ColorLayer.h
+++ b/services/surfaceflinger/ColorLayer.h
@@ -36,7 +36,8 @@
     bool setColor(const half3& color) override;
 
     void setPerFrameData(const sp<const DisplayDevice>& display, const ui::Transform& transform,
-                         const Rect& viewport, int32_t supportedPerFrameMetadata) override;
+                         const Rect& viewport, int32_t supportedPerFrameMetadata,
+                         const ui::Dataspace targetDataspace) override;
 
     bool onPreComposition(nsecs_t /*refreshStartTime*/) override { return false; }
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
index b45de5a..02d7890 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
@@ -19,6 +19,7 @@
 #include <cstdint>
 #include <vector>
 
+#include <gui/BufferQueue.h>
 #include <utils/StrongPointer.h>
 
 namespace android {
@@ -39,19 +40,28 @@
 class HwcBufferCache {
 public:
     HwcBufferCache();
-
-    // Given a buffer queue slot and buffer, return the HWC cache slot and
+    // Given a buffer, return the HWC cache slot and
     // buffer to be sent to HWC.
     //
     // outBuffer is set to buffer when buffer is not in the HWC cache;
     // otherwise, outBuffer is set to nullptr.
-    void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
+    void getHwcBuffer(const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
                       sp<GraphicBuffer>* outBuffer);
 
+protected:
+    bool getSlot(const sp<GraphicBuffer>& buffer, uint32_t* outSlot);
+    uint32_t getLeastRecentlyUsedSlot();
+    uint64_t getCounter();
+
 private:
-    // a vector as we expect "slot" to be in the range of [0, 63] (that is,
-    // less than BufferQueue::NUM_BUFFER_SLOTS).
-    std::vector<sp<GraphicBuffer>> mBuffers;
+    // an array where the index corresponds to a slot and the value corresponds to a (counter,
+    // buffer) pair. "counter" is a unique value that indicates the last time this slot was updated
+    // or used and allows us to keep track of the least-recently used buffer.
+    std::pair<uint64_t, wp<GraphicBuffer>> mBuffers[BufferQueue::NUM_BUFFER_SLOTS];
+
+    // The cache increments this counter value when a slot is updated or used.
+    // Used to track the least recently-used buffer
+    uint64_t mCounter = 1;
 };
 
 } // namespace compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
index 6f340b9..8613210 100644
--- a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
@@ -21,31 +21,52 @@
 namespace android::compositionengine::impl {
 
 HwcBufferCache::HwcBufferCache() {
-    mBuffers.reserve(BufferQueue::NUM_BUFFER_SLOTS);
+    std::fill(std::begin(mBuffers), std::end(mBuffers),
+              std::pair<uint64_t, wp<GraphicBuffer>>(0, nullptr));
+}
+bool HwcBufferCache::getSlot(const sp<GraphicBuffer>& buffer, uint32_t* outSlot) {
+    // search for cached buffer first
+    for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+        // Weak pointers in the cache may have had their object destroyed.
+        // Comparisons between weak pointers will accurately reflect this case,
+        // but comparisons between weak and strong may not.  Thus, we create a weak
+        // pointer from strong pointer buffer
+        wp<GraphicBuffer> weakCopy(buffer);
+        if (mBuffers[i].second == weakCopy) {
+            *outSlot = i;
+            return true;
+        }
+    }
+
+    // use the least-recently used slot
+    *outSlot = getLeastRecentlyUsedSlot();
+    return false;
 }
 
-void HwcBufferCache::getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
+uint32_t HwcBufferCache::getLeastRecentlyUsedSlot() {
+    auto iter = std::min_element(std::begin(mBuffers), std::end(mBuffers));
+    return std::distance(std::begin(mBuffers), iter);
+}
+
+void HwcBufferCache::getHwcBuffer(const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
                                   sp<GraphicBuffer>* outBuffer) {
-    if (slot == BufferQueue::INVALID_BUFFER_SLOT || slot < 0) {
-        // default to slot 0
-        slot = 0;
-    }
+    bool cached = getSlot(buffer, outSlot);
 
-    if (static_cast<size_t>(slot) >= mBuffers.size()) {
-        mBuffers.resize(slot + 1);
-    }
-
-    *outSlot = slot;
-
-    if (mBuffers[slot] == buffer) {
+    auto& [currentCounter, currentBuffer] = mBuffers[*outSlot];
+    if (cached) {
         // already cached in HWC, skip sending the buffer
         *outBuffer = nullptr;
+        currentCounter = getCounter();
     } else {
         *outBuffer = buffer;
 
         // update cache
-        mBuffers[slot] = buffer;
+        currentBuffer = buffer;
+        currentCounter = getCounter();
     }
 }
 
+uint64_t HwcBufferCache::getCounter() {
+    return mCounter++;
+}
 } // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
index f2a1aad..ac04cb3 100644
--- a/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
@@ -22,60 +22,80 @@
 namespace android::compositionengine {
 namespace {
 
+class TestableHwcBufferCache : public impl::HwcBufferCache {
+public:
+    void getHwcBuffer(const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
+                      sp<GraphicBuffer>* outBuffer) {
+        HwcBufferCache::getHwcBuffer(buffer, outSlot, outBuffer);
+    }
+    bool getSlot(const sp<GraphicBuffer>& buffer, uint32_t* outSlot) {
+        return HwcBufferCache::getSlot(buffer, outSlot);
+    }
+    uint32_t getLeastRecentlyUsedSlot() { return HwcBufferCache::getLeastRecentlyUsedSlot(); }
+};
+
 class HwcBufferCacheTest : public testing::Test {
 public:
     ~HwcBufferCacheTest() override = default;
 
-    void testSlot(const int inSlot, const uint32_t expectedSlot) {
-        uint32_t outSlot;
-        sp<GraphicBuffer> outBuffer;
-
-        // The first time, the output  is the same as the input
-        mCache.getHwcBuffer(inSlot, mBuffer1, &outSlot, &outBuffer);
-        EXPECT_EQ(expectedSlot, outSlot);
-        EXPECT_EQ(mBuffer1, outBuffer);
-
-        // The second time with the same buffer, the outBuffer is nullptr.
-        mCache.getHwcBuffer(inSlot, mBuffer1, &outSlot, &outBuffer);
-        EXPECT_EQ(expectedSlot, outSlot);
-        EXPECT_EQ(nullptr, outBuffer.get());
-
-        // With a new buffer, the outBuffer is the input.
-        mCache.getHwcBuffer(inSlot, mBuffer2, &outSlot, &outBuffer);
-        EXPECT_EQ(expectedSlot, outSlot);
-        EXPECT_EQ(mBuffer2, outBuffer);
-
-        // Again, the second request with the same buffer sets outBuffer to nullptr.
-        mCache.getHwcBuffer(inSlot, mBuffer2, &outSlot, &outBuffer);
-        EXPECT_EQ(expectedSlot, outSlot);
-        EXPECT_EQ(nullptr, outBuffer.get());
-
-        // Setting a slot to use nullptr lookslike works, but note that
-        // the output values make it look like no new buffer is being set....
-        mCache.getHwcBuffer(inSlot, sp<GraphicBuffer>(), &outSlot, &outBuffer);
-        EXPECT_EQ(expectedSlot, outSlot);
-        EXPECT_EQ(nullptr, outBuffer.get());
-    }
-
-    impl::HwcBufferCache mCache;
+    TestableHwcBufferCache mCache;
     sp<GraphicBuffer> mBuffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
     sp<GraphicBuffer> mBuffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
 };
 
-TEST_F(HwcBufferCacheTest, cacheWorksForSlotZero) {
-    testSlot(0, 0);
+TEST_F(HwcBufferCacheTest, testSlot) {
+    uint32_t outSlot;
+    sp<GraphicBuffer> outBuffer;
+
+    // The first time, the output  is the same as the input
+    mCache.getHwcBuffer(mBuffer1, &outSlot, &outBuffer);
+    EXPECT_EQ(0, outSlot);
+    EXPECT_EQ(mBuffer1, outBuffer);
+
+    // The second time with the same buffer, the outBuffer is nullptr.
+    mCache.getHwcBuffer(mBuffer1, &outSlot, &outBuffer);
+    EXPECT_EQ(0, outSlot);
+    EXPECT_EQ(nullptr, outBuffer.get());
+
+    // With a new buffer, the outBuffer is the input.
+    mCache.getHwcBuffer(mBuffer2, &outSlot, &outBuffer);
+    EXPECT_EQ(1, outSlot);
+    EXPECT_EQ(mBuffer2, outBuffer);
+
+    // Again, the second request with the same buffer sets outBuffer to nullptr.
+    mCache.getHwcBuffer(mBuffer2, &outSlot, &outBuffer);
+    EXPECT_EQ(1, outSlot);
+    EXPECT_EQ(nullptr, outBuffer.get());
+
+    // Setting a slot to use nullptr lookslike works, but note that
+    // the output values make it look like no new buffer is being set....
+    mCache.getHwcBuffer(sp<GraphicBuffer>(), &outSlot, &outBuffer);
+    EXPECT_EQ(2, outSlot);
+    EXPECT_EQ(nullptr, outBuffer.get());
 }
 
-TEST_F(HwcBufferCacheTest, cacheWorksForMaxSlot) {
-    testSlot(BufferQueue::NUM_BUFFER_SLOTS - 1, BufferQueue::NUM_BUFFER_SLOTS - 1);
-}
+TEST_F(HwcBufferCacheTest, testGetLeastRecentlyUsedSlot) {
+    int slot;
+    uint32_t outSlot;
+    sp<GraphicBuffer> outBuffer;
 
-TEST_F(HwcBufferCacheTest, cacheMapsNegativeSlotToZero) {
-    testSlot(-123, 0);
-}
+    // fill up cache
+    for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+        sp<GraphicBuffer> buf{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+        mCache.getHwcBuffer(buf, &outSlot, &outBuffer);
+        EXPECT_EQ(buf, outBuffer);
+        EXPECT_EQ(i, outSlot);
+    }
 
-TEST_F(HwcBufferCacheTest, cacheMapsInvalidBufferSlotToZero) {
-    testSlot(BufferQueue::INVALID_BUFFER_SLOT, 0);
+    slot = mCache.getLeastRecentlyUsedSlot();
+    EXPECT_EQ(0, slot);
+
+    mCache.getHwcBuffer(mBuffer1, &outSlot, &outBuffer);
+    EXPECT_EQ(0, outSlot);
+    EXPECT_EQ(mBuffer1, outBuffer);
+
+    slot = mCache.getLeastRecentlyUsedSlot();
+    EXPECT_EQ(1, slot);
 }
 
 } // namespace
diff --git a/services/surfaceflinger/ContainerLayer.cpp b/services/surfaceflinger/ContainerLayer.cpp
index 738f4b6..7927fa9 100644
--- a/services/surfaceflinger/ContainerLayer.cpp
+++ b/services/surfaceflinger/ContainerLayer.cpp
@@ -40,6 +40,6 @@
 }
 
 void ContainerLayer::setPerFrameData(const sp<const DisplayDevice>&, const ui::Transform&,
-                                     const Rect&, int32_t) {}
+                                     const Rect&, int32_t, const ui::Dataspace) {}
 
 } // namespace android
diff --git a/services/surfaceflinger/ContainerLayer.h b/services/surfaceflinger/ContainerLayer.h
index c7cfdcd..7222a3e 100644
--- a/services/surfaceflinger/ContainerLayer.h
+++ b/services/surfaceflinger/ContainerLayer.h
@@ -34,7 +34,8 @@
     bool canReceiveInput() const override;
 
     void setPerFrameData(const sp<const DisplayDevice>& display, const ui::Transform& transform,
-                         const Rect& viewport, int32_t supportedPerFrameMetadata) override;
+                         const Rect& viewport, int32_t supportedPerFrameMetadata,
+                         const ui::Dataspace targetDataspace) override;
 
     bool isCreatedFromMainThread() const override { return true; }
 
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index 27812f7..775fb80 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -111,8 +111,7 @@
     BufferItem item;
     status_t err = acquireBufferLocked(&item, 0);
     if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
-        mHwcBufferCache.getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer,
-                &outSlot, &outBuffer);
+        mHwcBufferCache.getHwcBuffer(mCurrentBuffer, &outSlot, &outBuffer);
         return NO_ERROR;
     } else if (err != NO_ERROR) {
         ALOGE("error acquiring buffer: %s (%d)", strerror(-err), err);
@@ -138,8 +137,7 @@
     mCurrentFence = item.mFence;
 
     outFence = item.mFence;
-    mHwcBufferCache.getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer,
-            &outSlot, &outBuffer);
+    mHwcBufferCache.getHwcBuffer(mCurrentBuffer, &outSlot, &outBuffer);
     outDataspace = static_cast<Dataspace>(item.mDataSpace);
     status_t result = mHwc.setClientTarget(mDisplayId, outSlot, outFence, outBuffer, outDataspace);
     if (result != NO_ERROR) {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index bca0abc..910a527 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -143,8 +143,8 @@
         return error;
     }
 
-    auto display = std::make_unique<impl::Display>(*mComposer.get(), mPowerAdvisor, mCapabilities,
-                                                   displayId, DisplayType::Virtual);
+    auto display = std::make_unique<impl::Display>(*mComposer.get(), mCapabilities, displayId,
+                                                   DisplayType::Virtual);
     display->setConnected(true);
     *outDisplay = display.get();
     mDisplays.emplace(displayId, std::move(display));
@@ -182,8 +182,8 @@
             return;
         }
 
-        auto newDisplay = std::make_unique<impl::Display>(*mComposer.get(), mPowerAdvisor,
-                                                          mCapabilities, displayId, displayType);
+        auto newDisplay = std::make_unique<impl::Display>(*mComposer.get(), mCapabilities,
+                                                          displayId, displayType);
         newDisplay->setConnected(true);
         mDisplays.emplace(displayId, std::move(newDisplay));
     } else if (connection == Connection::Disconnected) {
@@ -254,11 +254,10 @@
 }
 
 namespace impl {
-Display::Display(android::Hwc2::Composer& composer, android::Hwc2::PowerAdvisor& advisor,
+Display::Display(android::Hwc2::Composer& composer,
                  const std::unordered_set<Capability>& capabilities, hwc2_display_t id,
                  DisplayType type)
       : mComposer(composer),
-        mPowerAdvisor(advisor),
         mCapabilities(capabilities),
         mId(id),
         mIsConnected(false),
@@ -636,12 +635,6 @@
 
 Error Display::setColorMode(ColorMode mode, RenderIntent renderIntent)
 {
-    // When the color mode is switched to DISPLAY_P3, we want to boost the GPU frequency
-    // so that GPU composition can finish in time. When color mode is switched from
-    // DISPLAY_P3, we want to reset GPU frequency.
-    const bool expensiveRenderingExpected = (mode == ColorMode::DISPLAY_P3);
-    mPowerAdvisor.setExpensiveRenderingExpected(mId, expensiveRenderingExpected);
-
     auto intError = mComposer.setColorMode(mId, mode, renderIntent);
     return static_cast<Error>(intError);
 }
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 70358a0..f96614f 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -37,8 +37,6 @@
 #include <unordered_set>
 #include <vector>
 
-#include "PowerAdvisor.h"
-
 namespace android {
     struct DisplayedFrameStats;
     class Fence;
@@ -125,7 +123,6 @@
     std::unique_ptr<android::Hwc2::Composer> mComposer;
     std::unordered_set<Capability> mCapabilities;
     std::unordered_map<hwc2_display_t, std::unique_ptr<Display>> mDisplays;
-    android::Hwc2::impl::PowerAdvisor mPowerAdvisor;
     bool mRegisteredCallback = false;
 };
 
@@ -273,9 +270,8 @@
 
 class Display : public HWC2::Display {
 public:
-    Display(android::Hwc2::Composer& composer, android::Hwc2::PowerAdvisor& advisor,
-            const std::unordered_set<Capability>& capabilities, hwc2_display_t id,
-            DisplayType type);
+    Display(android::Hwc2::Composer& composer, const std::unordered_set<Capability>& capabilities,
+            hwc2_display_t id, DisplayType type);
     ~Display() override;
 
     // Required by HWC2
@@ -352,7 +348,6 @@
     // this HWC2::Display, so these references are guaranteed to be valid for
     // the lifetime of this object.
     android::Hwc2::Composer& mComposer;
-    android::Hwc2::PowerAdvisor& mPowerAdvisor;
     const std::unordered_set<Capability>& mCapabilities;
 
     hwc2_display_t mId;
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 12bbae2..039db73 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -38,7 +38,7 @@
 
 PowerAdvisor::PowerAdvisor() = default;
 
-void PowerAdvisor::setExpensiveRenderingExpected(hwc2_display_t displayId, bool expected) {
+void PowerAdvisor::setExpensiveRenderingExpected(DisplayId displayId, bool expected) {
     if (expected) {
         mExpensiveDisplays.insert(displayId);
     } else {
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 573a1a9..5aa1f22 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -22,10 +22,12 @@
 #undef HWC2_INCLUDE_STRINGIFICATION
 #undef HWC2_USE_CPP11
 
+#include <unordered_set>
+
 #include <android/hardware/power/1.3/IPower.h>
 #include <utils/StrongPointer.h>
 
-#include <unordered_set>
+#include "DisplayIdentification.h"
 
 namespace android {
 namespace Hwc2 {
@@ -34,7 +36,7 @@
 public:
     virtual ~PowerAdvisor();
 
-    virtual void setExpensiveRenderingExpected(hwc2_display_t displayId, bool expected) = 0;
+    virtual void setExpensiveRenderingExpected(DisplayId displayId, bool expected) = 0;
 };
 
 namespace impl {
@@ -48,12 +50,12 @@
     PowerAdvisor();
     ~PowerAdvisor() override;
 
-    void setExpensiveRenderingExpected(hwc2_display_t displayId, bool expected) override;
+    void setExpensiveRenderingExpected(DisplayId displayId, bool expected) override;
 
 private:
     sp<V1_3::IPower> getPowerHal();
 
-    std::unordered_set<hwc2_display_t> mExpensiveDisplays;
+    std::unordered_set<DisplayId> mExpensiveDisplays;
     bool mNotifiedExpensiveRendering = false;
     bool mReconnectPowerHal = false;
 };
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 1c2853a..613dc77 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -224,8 +224,7 @@
     if (fbBuffer != nullptr) {
         uint32_t hwcSlot = 0;
         sp<GraphicBuffer> hwcBuffer;
-        mHwcBufferCache.getHwcBuffer(mFbProducerSlot, fbBuffer,
-                &hwcSlot, &hwcBuffer);
+        mHwcBufferCache.getHwcBuffer(fbBuffer, &hwcSlot, &hwcBuffer);
 
         // TODO: Correctly propagate the dataspace from GL composition
         result = mHwc.setClientTarget(*mDisplayId, hwcSlot, mFbFence, hwcBuffer,
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index a6adeb3..5c3fb05 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -103,6 +103,7 @@
     mCurrentState.cornerRadius = 0.0f;
     mCurrentState.api = -1;
     mCurrentState.hasColorTransform = false;
+    mCurrentState.colorSpaceAgnostic = false;
 
     // drawing state & current state are identical
     mDrawingState = mCurrentState;
@@ -147,7 +148,7 @@
             strongRelative->removeZOrderRelative(this);
             mFlinger->setTransactionFlags(eTraversalNeeded);
         }
-        mCurrentState.zOrderRelativeOf = nullptr;
+        setZOrderRelativeOf(nullptr);
     }
 
     // Since we are no longer reachable from CurrentState SurfaceFlinger
@@ -1071,13 +1072,6 @@
         mNeedsFiltering = (!getActiveTransform(c).preserveRects() || type >= ui::Transform::SCALE);
     }
 
-    // If the layer is hidden, signal and clear out all local sync points so
-    // that transactions for layers depending on this layer's frames becoming
-    // visible are not blocked
-    if (c.flags & layer_state_t::eLayerHidden) {
-        clearSyncPoints();
-    }
-
     if (mCurrentState.inputInfoChanged) {
         flags |= eInputInfoChanged;
         mCurrentState.inputInfoChanged = false;
@@ -1164,7 +1158,7 @@
         if (strongRelative != nullptr) {
             strongRelative->removeZOrderRelative(this);
         }
-        mCurrentState.zOrderRelativeOf = nullptr;
+        setZOrderRelativeOf(nullptr);
     }
     setTransactionFlags(eTransactionNeeded);
     return true;
@@ -1184,6 +1178,13 @@
     setTransactionFlags(eTransactionNeeded);
 }
 
+void Layer::setZOrderRelativeOf(const wp<Layer>& relativeOf) {
+    mCurrentState.zOrderRelativeOf = relativeOf;
+    mCurrentState.sequence++;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+}
+
 bool Layer::setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ) {
     sp<Handle> handle = static_cast<Handle*>(relativeToHandle.get());
     if (handle == nullptr) {
@@ -1207,7 +1208,7 @@
     if (oldZOrderRelativeOf != nullptr) {
         oldZOrderRelativeOf->removeZOrderRelative(this);
     }
-    mCurrentState.zOrderRelativeOf = relative;
+    setZOrderRelativeOf(relative);
     relative->addZOrderRelative(this);
 
     setTransactionFlags(eTransactionNeeded);
@@ -1355,6 +1356,17 @@
     return true;
 }
 
+bool Layer::setColorSpaceAgnostic(const bool agnostic) {
+    if (mCurrentState.colorSpaceAgnostic == agnostic) {
+        return false;
+    }
+    mCurrentState.sequence++;
+    mCurrentState.colorSpaceAgnostic = agnostic;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
 uint32_t Layer::getLayerStack() const {
     auto p = mDrawingParent.promote();
     if (p == nullptr) {
@@ -1764,18 +1776,6 @@
     mCurrentParent = layer;
 }
 
-void Layer::clearSyncPoints() {
-    for (const auto& child : mCurrentChildren) {
-        child->clearSyncPoints();
-    }
-
-    Mutex::Autolock lock(mLocalSyncPointMutex);
-    for (auto& point : mLocalSyncPoints) {
-        point->setFrameAvailable();
-    }
-    mLocalSyncPoints.clear();
-}
-
 int32_t Layer::getZ() const {
     return mDrawingState.z;
 }
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 046482c..1afb917 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -210,6 +210,7 @@
         // The deque of callback handles for this frame. The back of the deque contains the most
         // recent callback handle.
         std::deque<sp<CallbackHandle>> callbackHandles;
+        bool colorSpaceAgnostic;
     };
 
     explicit Layer(const LayerCreationArgs& args);
@@ -297,6 +298,7 @@
     virtual bool setColorTransform(const mat4& matrix);
     virtual mat4 getColorTransform() const;
     virtual bool hasColorTransform() const;
+    virtual bool isColorSpaceAgnostic() const { return mDrawingState.colorSpaceAgnostic; }
 
     // Used only to set BufferStateLayer state
     virtual bool setTransform(uint32_t /*transform*/) { return false; };
@@ -315,6 +317,7 @@
         return false;
     };
     virtual bool setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace);
+    virtual bool setColorSpaceAgnostic(const bool agnostic);
 
     ui::Dataspace getDataSpace() const { return mCurrentDataSpace; }
 
@@ -450,7 +453,8 @@
     bool getForceClientComposition(const sp<DisplayDevice>& display);
     virtual void setPerFrameData(const sp<const DisplayDevice>& display,
                                  const ui::Transform& transform, const Rect& viewport,
-                                 int32_t supportedPerFrameMetadata) = 0;
+                                 int32_t supportedPerFrameMetadata,
+                                 const ui::Dataspace targetDataspace) = 0;
 
     // callIntoHwc exists so we can update our local state and call
     // acceptDisplayChanges without unnecessarily updating the device's state
@@ -750,8 +754,6 @@
     virtual bool applyPendingStates(State* stateToCommit);
     virtual uint32_t doTransactionResize(uint32_t flags, Layer::State* stateToCommit);
 
-    void clearSyncPoints();
-
     // Returns mCurrentScaling mode (originating from the
     // Client) or mOverrideScalingMode mode (originating from
     // the Surface Controller) if set.
@@ -899,6 +901,8 @@
 
     // Layer bounds in screen space.
     FloatRect mScreenBounds;
+
+    void setZOrderRelativeOf(const wp<Layer>& relativeOf);
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp
index 665179e..1eccf9a 100644
--- a/services/surfaceflinger/Scheduler/DispSync.cpp
+++ b/services/surfaceflinger/Scheduler/DispSync.cpp
@@ -43,6 +43,7 @@
 namespace android {
 
 DispSync::~DispSync() = default;
+DispSync::Callback::~Callback() = default;
 
 namespace impl {
 
diff --git a/services/surfaceflinger/Scheduler/DispSync.h b/services/surfaceflinger/Scheduler/DispSync.h
index 4a90f10..f629697 100644
--- a/services/surfaceflinger/Scheduler/DispSync.h
+++ b/services/surfaceflinger/Scheduler/DispSync.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_DISPSYNC_H
-#define ANDROID_DISPSYNC_H
+#pragma once
 
 #include <stddef.h>
 
@@ -35,10 +34,16 @@
 public:
     class Callback {
     public:
-        virtual ~Callback() = default;
+        Callback() = default;
+        virtual ~Callback();
         virtual void onDispSyncEvent(nsecs_t when) = 0;
+
+    protected:
+        Callback(Callback const&) = delete;
+        Callback& operator=(Callback const&) = delete;
     };
 
+    DispSync() = default;
     virtual ~DispSync();
 
     virtual void reset() = 0;
@@ -57,6 +62,10 @@
     virtual nsecs_t expectedPresentTime() = 0;
 
     virtual void dump(std::string& result) const = 0;
+
+protected:
+    DispSync(DispSync const&) = delete;
+    DispSync& operator=(DispSync const&) = delete;
 };
 
 namespace impl {
@@ -239,5 +248,3 @@
 } // namespace impl
 
 } // namespace android
-
-#endif // ANDROID_DISPSYNC_H
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
index ab1f460..7e2b03d 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -76,13 +76,13 @@
     mDefaultRefreshRateOffsets.late = {sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs};
 
     mHighRefreshRateOffsets.early = {highFpsEarlySfOffsetNs != -1 ? highFpsEarlySfOffsetNs
-                                                                  : highFpsLateAppOffsetNs,
+                                                                  : highFpsLateSfOffsetNs,
                                      highFpsEarlyAppOffsetNs != -1 ? highFpsEarlyAppOffsetNs
-                                                                   : highFpsLateSfOffsetNs};
+                                                                   : highFpsLateAppOffsetNs};
     mHighRefreshRateOffsets.earlyGl = {highFpsEarlyGlSfOffsetNs != -1 ? highFpsEarlyGlSfOffsetNs
-                                                                      : highFpsLateAppOffsetNs,
+                                                                      : highFpsLateSfOffsetNs,
                                        highFpsEarlyGlAppOffsetNs != -1 ? highFpsEarlyGlAppOffsetNs
-                                                                       : highFpsLateSfOffsetNs};
+                                                                       : highFpsLateAppOffsetNs};
     mHighRefreshRateOffsets.late = {highFpsLateSfOffsetNs, highFpsLateAppOffsetNs};
 }
 
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 026f7c7..cbcc031 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -28,7 +28,7 @@
 namespace scheduler {
 
 /**
- * This class is used to encapsulate configuration for refresh rates. It holds infomation
+ * This class is used to encapsulate configuration for refresh rates. It holds information
  * about available refresh rates on the device, and the mapping between the numbers and human
  * readable names.
  */
@@ -40,8 +40,6 @@
     enum class RefreshRateType { POWER_SAVING, DEFAULT, PERFORMANCE };
 
     struct RefreshRate {
-        // Type of the refresh rate.
-        RefreshRateType type;
         // This config ID corresponds to the position of the config in the vector that is stored
         // on the device.
         int configId;
@@ -59,13 +57,16 @@
     }
     ~RefreshRateConfigs() = default;
 
-    const std::vector<RefreshRate>& getRefreshRates() { return mRefreshRates; }
+    const std::unordered_map<RefreshRateType, RefreshRate>& getRefreshRates() {
+        return mRefreshRates;
+    }
+    const RefreshRate& getRefreshRate(RefreshRateType type) { return mRefreshRates[type]; }
 
 private:
     void init(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
         // This is the rate that HWC encapsulates right now when the device is in DOZE mode.
-        mRefreshRates.push_back(
-                RefreshRate{RefreshRateType::POWER_SAVING, SCREEN_OFF_CONFIG_ID, "ScreenOff", 0});
+        mRefreshRates.emplace(RefreshRateType::POWER_SAVING,
+                              RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0});
 
         if (configs.size() < 1) {
             ALOGE("Device does not have valid configs. Config size is 0.");
@@ -88,9 +89,10 @@
         nsecs_t vsyncPeriod = configIdToVsyncPeriod[0].second;
         if (vsyncPeriod != 0) {
             const float fps = 1e9 / vsyncPeriod;
-            mRefreshRates.push_back(
-                    RefreshRate{RefreshRateType::DEFAULT, configIdToVsyncPeriod[0].first,
-                                base::StringPrintf("%2.ffps", fps), static_cast<uint32_t>(fps)});
+            mRefreshRates.emplace(RefreshRateType::DEFAULT,
+                                  RefreshRate{configIdToVsyncPeriod[0].first,
+                                              base::StringPrintf("%2.ffps", fps),
+                                              static_cast<uint32_t>(fps)});
         }
 
         if (configs.size() < 2) {
@@ -102,13 +104,14 @@
         vsyncPeriod = configIdToVsyncPeriod[1].second;
         if (vsyncPeriod != 0) {
             const float fps = 1e9 / vsyncPeriod;
-            mRefreshRates.push_back(
-                    RefreshRate{RefreshRateType::PERFORMANCE, configIdToVsyncPeriod[1].first,
-                                base::StringPrintf("%2.ffps", fps), static_cast<uint32_t>(fps)});
+            mRefreshRates.emplace(RefreshRateType::PERFORMANCE,
+                                  RefreshRate{configIdToVsyncPeriod[1].first,
+                                              base::StringPrintf("%2.ffps", fps),
+                                              static_cast<uint32_t>(fps)});
         }
     }
 
-    std::vector<RefreshRate> mRefreshRates;
+    std::unordered_map<RefreshRateType, RefreshRate> mRefreshRates;
 };
 
 } // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
index dcb2988..2491081 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -41,10 +41,9 @@
     static constexpr int64_t MS_PER_DAY = 24 * MS_PER_HOUR;
 
 public:
-    explicit RefreshRateStats(
-            const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
-            const std::shared_ptr<TimeStats>& timeStats)
-          : mRefreshRateConfigs(std::make_unique<RefreshRateConfigs>(configs)),
+    explicit RefreshRateStats(const std::shared_ptr<RefreshRateConfigs>& refreshRateConfigs,
+                              const std::shared_ptr<TimeStats>& timeStats)
+          : mRefreshRateConfigs(refreshRateConfigs),
             mTimeStats(timeStats),
             mPreviousRecordedTime(systemTime()) {}
     ~RefreshRateStats() = default;
@@ -84,7 +83,7 @@
         flushTime();
 
         std::unordered_map<std::string, int64_t> totalTime;
-        for (auto config : mRefreshRateConfigs->getRefreshRates()) {
+        for (auto [type, config] : mRefreshRateConfigs->getRefreshRates()) {
             int64_t totalTimeForConfig = 0;
             if (mConfigModesTotalTime.find(config.configId) != mConfigModesTotalTime.end()) {
                 totalTimeForConfig = mConfigModesTotalTime.at(config.configId);
@@ -124,7 +123,7 @@
         mPreviousRecordedTime = currentTime;
 
         mConfigModesTotalTime[mode] += timeElapsedMs;
-        for (const auto& config : mRefreshRateConfigs->getRefreshRates()) {
+        for (const auto& [type, config] : mRefreshRateConfigs->getRefreshRates()) {
             if (config.configId == mode) {
                 mTimeStats->recordRefreshRate(config.fps, timeElapsed);
             }
@@ -143,7 +142,7 @@
     }
 
     // Keeps information about refresh rate configs that device has.
-    std::unique_ptr<RefreshRateConfigs> mRefreshRateConfigs;
+    std::shared_ptr<RefreshRateConfigs> mRefreshRateConfigs;
 
     // Aggregate refresh rate statistics for telemetry.
     std::shared_ptr<TimeStats> mTimeStats;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 990318a..f3a7f87 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -26,7 +26,6 @@
 
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
 #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
-#include <android/hardware/configstore/1.2/ISurfaceFlingerConfigs.h>
 #include <configstore/Utils.h>
 #include <cutils/properties.h>
 #include <ui/DisplayStatInfo.h>
@@ -68,9 +67,14 @@
     mPrimaryDispSync = std::move(primaryDispSync);
     mEventControlThread = std::make_unique<impl::EventControlThread>(function);
 
+    mSetIdleTimerMs = set_idle_timer_ms(0);
+
     char value[PROPERTY_VALUE_MAX];
     property_get("debug.sf.set_idle_timer_ms", value, "0");
-    mSetIdleTimerMs = atoi(value);
+    int int_value = atoi(value);
+    if (int_value) {
+        mSetIdleTimerMs = atoi(value);
+    }
 
     if (mSetIdleTimerMs > 0) {
         mIdleTimer =
@@ -309,14 +313,10 @@
     mLayerHistory.incrementCounter();
 }
 
-void Scheduler::setExpiredIdleTimerCallback(const ExpiredIdleTimerCallback& expiredTimerCallback) {
+void Scheduler::setChangeRefreshRateCallback(
+        const ChangeRefreshRateCallback& changeRefreshRateCallback) {
     std::lock_guard<std::mutex> lock(mCallbackLock);
-    mExpiredTimerCallback = expiredTimerCallback;
-}
-
-void Scheduler::setResetIdleTimerCallback(const ResetIdleTimerCallback& resetTimerCallback) {
-    std::lock_guard<std::mutex> lock(mCallbackLock);
-    mResetTimerCallback = resetTimerCallback;
+    mChangeRefreshRateCallback = changeRefreshRateCallback;
 }
 
 void Scheduler::updateFrameSkipping(const int64_t skipCount) {
@@ -415,16 +415,18 @@
 
 void Scheduler::resetTimerCallback() {
     std::lock_guard<std::mutex> lock(mCallbackLock);
-    if (mResetTimerCallback) {
-        mResetTimerCallback();
+    if (mChangeRefreshRateCallback) {
+        // We do not notify the applications about config changes when idle timer is reset.
+        mChangeRefreshRateCallback(RefreshRateType::PERFORMANCE, ConfigEvent::None);
         ATRACE_INT("ExpiredIdleTimer", 0);
     }
 }
 
 void Scheduler::expiredTimerCallback() {
     std::lock_guard<std::mutex> lock(mCallbackLock);
-    if (mExpiredTimerCallback) {
-        mExpiredTimerCallback();
+    if (mChangeRefreshRateCallback) {
+        // We do not notify the applications about config changes when idle timer expires.
+        mChangeRefreshRateCallback(RefreshRateType::DEFAULT, ConfigEvent::None);
         ATRACE_INT("ExpiredIdleTimer", 1);
     }
 }
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 7f113e7..5b9759b 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -28,6 +28,7 @@
 #include "IdleTimer.h"
 #include "InjectVSyncSource.h"
 #include "LayerHistory.h"
+#include "RefreshRateConfigs.h"
 #include "SchedulerUtils.h"
 
 namespace android {
@@ -36,9 +37,19 @@
 
 class Scheduler {
 public:
-    using ExpiredIdleTimerCallback = std::function<void()>;
+    // Enum to keep track of whether we trigger event to notify choreographer of config changes.
+    enum class ConfigEvent { None, Changed };
+
+    // logical or operator with the semantics of at least one of the events is Changed
+    friend ConfigEvent operator|(const ConfigEvent& first, const ConfigEvent& second) {
+        if (first == ConfigEvent::Changed) return ConfigEvent::Changed;
+        if (second == ConfigEvent::Changed) return ConfigEvent::Changed;
+        return ConfigEvent::None;
+    }
+
+    using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
+    using ChangeRefreshRateCallback = std::function<void(RefreshRateType, ConfigEvent)>;
     using GetVsyncPeriod = std::function<nsecs_t()>;
-    using ResetIdleTimerCallback = std::function<void()>;
 
     // Enum to indicate whether to start the transaction early, or at vsync time.
     enum class TransactionStart { EARLY, NORMAL };
@@ -135,10 +146,11 @@
                                      const std::string layerName);
     // Increments counter in the layer history to indicate that SF has started a new frame.
     void incrementFrameCounter();
-    // Callback that gets invoked once the idle timer expires.
-    void setExpiredIdleTimerCallback(const ExpiredIdleTimerCallback& expiredTimerCallback);
-    // Callback that gets invoked once the idle timer is reset.
-    void setResetIdleTimerCallback(const ResetIdleTimerCallback& resetTimerCallback);
+    // Callback that gets invoked when Scheduler wants to change the refresh rate.
+    void setChangeRefreshRateCallback(const ChangeRefreshRateCallback& changeRefreshRateCallback);
+
+    // Returns whether idle timer is enabled or not
+    bool isIdleTimerEnabled() { return mSetIdleTimerMs > 0; }
     // Returns relevant information about Scheduler for dumpsys purposes.
     std::string doDump();
 
@@ -216,8 +228,7 @@
     std::unique_ptr<scheduler::IdleTimer> mIdleTimer;
 
     std::mutex mCallbackLock;
-    ExpiredIdleTimerCallback mExpiredTimerCallback GUARDED_BY(mCallbackLock);
-    ExpiredIdleTimerCallback mResetTimerCallback GUARDED_BY(mCallbackLock);
+    ChangeRefreshRateCallback mChangeRefreshRateCallback GUARDED_BY(mCallbackLock);
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index cd63a0e..9b0e8c1 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -62,7 +62,6 @@
 #include <ui/GraphicBufferAllocator.h>
 #include <ui/PixelFormat.h>
 #include <ui/UiConfig.h>
-#include <utils/CallStack.h>
 #include <utils/StopWatch.h>
 #include <utils/String16.h>
 #include <utils/String8.h>
@@ -110,7 +109,6 @@
 
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
 #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
-#include <android/hardware/configstore/1.2/ISurfaceFlingerConfigs.h>
 #include <android/hardware/configstore/1.1/types.h>
 #include <configstore/Utils.h>
 
@@ -203,9 +201,6 @@
 const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER");
 const String16 sDump("android.permission.DUMP");
 
-constexpr float kDefaultRefreshRate = 60.f;
-constexpr float kPerformanceRefreshRate = 90.f;
-
 // ---------------------------------------------------------------------------
 int64_t SurfaceFlinger::dispSyncPresentTimeOffset;
 bool SurfaceFlinger::useHwcForRgbToYuv;
@@ -375,9 +370,6 @@
     auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize));
     mMaxGraphicBufferProducerListSize = (listSize > 0) ? size_t(listSize) : defaultListSize;
 
-    property_get("debug.sf.use_90Hz", value, "0");
-    mUse90Hz = atoi(value);
-
     property_get("debug.sf.use_smart_90_for_video", value, "0");
     mUseSmart90ForVideo = atoi(value);
 
@@ -568,19 +560,21 @@
         readPersistentProperties();
         mBootStage = BootStage::FINISHED;
 
-        // TODO(b/122905403): Once the display policy is completely integrated, this flag should go
-        // away and it should be controlled by flipping the switch in setting. The switch in
-        // settings should only be available to P19 devices, if they are opted into 90Hz fishfood.
-        // The boot must be complete before we can set the active config.
+        // set the refresh rate according to the policy
+        const auto displayId = getInternalDisplayIdLocked();
+        LOG_ALWAYS_FATAL_IF(!displayId);
 
-        if (mUse90Hz) {
+        const auto performanceRefreshRate =
+                mRefreshRateConfigs[*displayId]->getRefreshRate(RefreshRateType::PERFORMANCE);
+
+        if (isConfigAllowed(*displayId, performanceRefreshRate.configId)) {
             mPhaseOffsets->setRefreshRateType(
                     scheduler::RefreshRateConfigs::RefreshRateType::PERFORMANCE);
-            setRefreshRateTo(RefreshRateType::PERFORMANCE, ConfigEvent::None);
+            setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::None);
         } else {
             mPhaseOffsets->setRefreshRateType(
                     scheduler::RefreshRateConfigs::RefreshRateType::DEFAULT);
-            setRefreshRateTo(RefreshRateType::DEFAULT, ConfigEvent::None);
+            setRefreshRateTo(RefreshRateType::DEFAULT, Scheduler::ConfigEvent::None);
         }
     }));
 }
@@ -704,19 +698,18 @@
         ALOGE("Run StartPropertySetThread failed!");
     }
 
-    if (mUse90Hz) {
-        mScheduler->setExpiredIdleTimerCallback([this] {
-            Mutex::Autolock lock(mStateLock);
-            setRefreshRateTo(RefreshRateType::DEFAULT, ConfigEvent::None);
-        });
-        mScheduler->setResetIdleTimerCallback([this] {
-            Mutex::Autolock lock(mStateLock);
-            setRefreshRateTo(RefreshRateType::PERFORMANCE, ConfigEvent::None);
-        });
+    if (mScheduler->isIdleTimerEnabled()) {
+        mScheduler->setChangeRefreshRateCallback(
+                [this](RefreshRateType type, Scheduler::ConfigEvent event) {
+                    Mutex::Autolock lock(mStateLock);
+                    setRefreshRateTo(type, event);
+                });
     }
-    mRefreshRateStats = std::make_unique<scheduler::RefreshRateStats>(getHwComposer().getConfigs(
-                                                                              *display->getId()),
-                                                                      mTimeStats);
+    mRefreshRateConfigs[*display->getId()] = std::make_shared<scheduler::RefreshRateConfigs>(
+            getHwComposer().getConfigs(*display->getId()));
+    mRefreshRateStats =
+            std::make_unique<scheduler::RefreshRateStats>(mRefreshRateConfigs[*display->getId()],
+                                                          mTimeStats);
 
     ALOGV("Done initializing");
 }
@@ -920,17 +913,9 @@
 }
 
 void SurfaceFlinger::setDesiredActiveConfig(const sp<IBinder>& displayToken, int mode,
-                                            ConfigEvent event) {
+                                            Scheduler::ConfigEvent event) {
     ATRACE_CALL();
 
-    Vector<DisplayInfo> configs;
-    // Lock is acquired by setRefreshRateTo.
-    getDisplayConfigsLocked(displayToken, &configs);
-    if (mode < 0 || mode >= static_cast<int>(configs.size())) {
-        ALOGE("Attempt to set active config %d for display with %zu configs", mode, configs.size());
-        return;
-    }
-
     // Lock is acquired by setRefreshRateTo.
     const auto display = getDisplayDeviceLocked(displayToken);
     if (!display) {
@@ -949,9 +934,11 @@
     }
 
     // Don't check against the current mode yet. Worst case we set the desired
-    // config twice.
+    // config twice. However event generation config might have changed so we need to update it
+    // accordingly
     std::lock_guard<std::mutex> lock(mActiveConfigLock);
-    mDesiredActiveConfig = ActiveConfigInfo{mode, displayToken, event};
+    const Scheduler::ConfigEvent desiredConfig = mDesiredActiveConfig.event | event;
+    mDesiredActiveConfig = ActiveConfigInfo{mode, displayToken, desiredConfig};
 
     if (!mDesiredActiveConfigChanged) {
         // This is the first time we set the desired
@@ -984,7 +971,7 @@
 
     mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
     ATRACE_INT("ActiveConfigMode", mUpcomingActiveConfig.configId);
-    if (mUpcomingActiveConfig.event != ConfigEvent::None) {
+    if (mUpcomingActiveConfig.event != Scheduler::ConfigEvent::None) {
         mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
                                     mUpcomingActiveConfig.configId);
     }
@@ -1425,7 +1412,8 @@
     return mAllowedConfigs[displayId]->isConfigAllowed(config);
 }
 
-void SurfaceFlinger::setRefreshRateTo(RefreshRateType refreshRate, ConfigEvent event) {
+void SurfaceFlinger::setRefreshRateTo(RefreshRateType refreshRate, Scheduler::ConfigEvent event) {
+    ATRACE_CALL();
     mPhaseOffsets->setRefreshRateType(refreshRate);
 
     const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
@@ -1435,47 +1423,23 @@
         return;
     }
 
-    // TODO(b/113612090): There should be a message queue flush here. Because this esentially
-    // runs on a mainthread, we cannot call postMessageSync. This can be resolved in a better
-    // manner, once the setActiveConfig is synchronous, and is executed at a known time in a
-    // refresh cycle.
-
     // Don't do any updating if the current fps is the same as the new one.
-    const nsecs_t currentVsyncPeriod = getVsyncPeriod();
-    if (currentVsyncPeriod == 0) {
-        return;
-    }
-
-    // TODO(b/113612090): Consider having an enum value for correct refresh rates, rather than
-    // floating numbers.
-    const float currentFps = 1e9 / currentVsyncPeriod;
-    const float newFps = refreshRate == RefreshRateType::PERFORMANCE ? kPerformanceRefreshRate
-                                                                     : kDefaultRefreshRate;
-    if (std::abs(currentFps - newFps) <= 1) {
-        return;
-    }
-
     const auto displayId = getInternalDisplayIdLocked();
     LOG_ALWAYS_FATAL_IF(!displayId);
+    const auto displayToken = getInternalDisplayTokenLocked();
 
-    auto configs = getHwComposer().getConfigs(*displayId);
-    for (int i = 0; i < configs.size(); i++) {
-        if (!isConfigAllowed(*displayId, i)) {
-            ALOGV("Skipping config %d as it is not part of allowed configs", i);
-            continue;
-        }
-
-        const nsecs_t vsyncPeriod = configs.at(i)->getVsyncPeriod();
-        if (vsyncPeriod == 0) {
-            continue;
-        }
-        const float fps = 1e9 / vsyncPeriod;
-        // TODO(b/113612090): There should be a better way at determining which config
-        // has the right refresh rate.
-        if (std::abs(fps - newFps) <= 1) {
-            setDesiredActiveConfig(getInternalDisplayTokenLocked(), i, event);
-        }
+    auto desiredConfigId = mRefreshRateConfigs[*displayId]->getRefreshRate(refreshRate).configId;
+    const auto display = getDisplayDeviceLocked(displayToken);
+    if (desiredConfigId == display->getActiveConfig()) {
+        return;
     }
+
+    if (!isConfigAllowed(*displayId, desiredConfigId)) {
+        ALOGV("Skipping config %d as it is not part of allowed configs", desiredConfigId);
+        return;
+    }
+
+    setDesiredActiveConfig(getInternalDisplayTokenLocked(), desiredConfigId, event);
 }
 
 void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
@@ -1623,7 +1587,7 @@
                 break;
             }
 
-            if (mUse90Hz && mUseSmart90ForVideo) {
+            if (mUseSmart90ForVideo) {
                 // This call is made each time SF wakes up and creates a new frame. It is part
                 // of video detection feature.
                 mScheduler->incrementFrameCounter();
@@ -1789,6 +1753,13 @@
         if (mDrawingState.colorMatrixChanged) {
             display->setColorTransform(mDrawingState.colorMatrix);
         }
+        Dataspace targetDataspace = Dataspace::UNKNOWN;
+        if (useColorManagement) {
+            ColorMode colorMode;
+            RenderIntent renderIntent;
+            pickColorMode(displayDevice, &colorMode, &targetDataspace, &renderIntent);
+            display->setColorMode(colorMode, targetDataspace, renderIntent);
+        }
         for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
             if (layer->isHdrY410()) {
                 layer->forceClientComposition(displayDevice);
@@ -1820,15 +1791,7 @@
 
             const auto& displayState = display->getState();
             layer->setPerFrameData(displayDevice, displayState.transform, displayState.viewport,
-                                   displayDevice->getSupportedPerFrameMetadata());
-        }
-
-        if (useColorManagement) {
-            ColorMode  colorMode;
-            Dataspace dataSpace;
-            RenderIntent renderIntent;
-            pickColorMode(displayDevice, &colorMode, &dataSpace, &renderIntent);
-            display->setColorMode(colorMode, dataSpace, renderIntent);
+                                   displayDevice->getSupportedPerFrameMetadata(), targetDataspace);
         }
     }
 
@@ -1971,78 +1934,22 @@
     getBE().mCompositorTiming.presentLatency = snappedCompositeToPresentLatency;
 }
 
-// debug patch for b/119477596 - add stack guards to catch stack
-// corruptions and disable clang optimizations.
-// The code below is temporary and planned to be removed once stack
-// corruptions are found.
-#pragma clang optimize off
-class StackGuard {
-public:
-    StackGuard(const char* name, const char* func, int line) {
-        guarders.reserve(MIN_CAPACITY);
-        guarders.push_back({this, name, func, line});
-        validate();
-    }
-    ~StackGuard() {
-        for (auto i = guarders.end() - 1; i >= guarders.begin(); --i) {
-            if (i->guard == this) {
-                guarders.erase(i);
-                break;
-            }
-        }
-    }
-
-    static void validate() {
-        for (const auto& guard : guarders) {
-            if (guard.guard->cookie != COOKIE_VALUE) {
-                ALOGE("%s:%d: Stack corruption detected at %s", guard.func, guard.line, guard.name);
-                CallStack stack(LOG_TAG);
-                abort();
-            }
-        }
-    }
-
-private:
-    uint64_t cookie = COOKIE_VALUE;
-    static constexpr uint64_t COOKIE_VALUE = 0xc0febebedeadbeef;
-    static constexpr size_t MIN_CAPACITY = 16;
-
-    struct GuarderElement {
-        StackGuard* guard;
-        const char* name;
-        const char* func;
-        int line;
-    };
-
-    static std::vector<GuarderElement> guarders;
-};
-std::vector<StackGuard::GuarderElement> StackGuard::guarders;
-
-#define DEFINE_STACK_GUARD(__n) StackGuard __n##StackGuard(#__n, __FUNCTION__, __LINE__);
-
-#define ASSERT_ON_STACK_GUARD() StackGuard::validate();
 void SurfaceFlinger::postComposition()
 {
-    DEFINE_STACK_GUARD(begin);
     ATRACE_CALL();
     ALOGV("postComposition");
 
     // Release any buffers which were replaced this frame
     nsecs_t dequeueReadyTime = systemTime();
-    DEFINE_STACK_GUARD(dequeueReadyTime);
     for (auto& layer : mLayersWithQueuedFrames) {
         layer->releasePendingBuffer(dequeueReadyTime);
     }
-    ASSERT_ON_STACK_GUARD();
 
     // |mStateLock| not needed as we are on the main thread
     const auto displayDevice = getDefaultDisplayDeviceLocked();
-    DEFINE_STACK_GUARD(displayDevice);
 
     getBE().mGlCompositionDoneTimeline.updateSignalTimes();
     std::shared_ptr<FenceTime> glCompositionDoneFenceTime;
-    DEFINE_STACK_GUARD(glCompositionDoneFenceTime);
-
     if (displayDevice && getHwComposer().hasClientComposition(displayDevice->getId())) {
         glCompositionDoneFenceTime =
                 std::make_shared<FenceTime>(displayDevice->getCompositionDisplay()
@@ -2053,52 +1960,37 @@
         glCompositionDoneFenceTime = FenceTime::NO_FENCE;
     }
 
-    ASSERT_ON_STACK_GUARD();
-
     getBE().mDisplayTimeline.updateSignalTimes();
     mPreviousPresentFence = displayDevice ? getHwComposer().getPresentFence(*displayDevice->getId())
                                           : Fence::NO_FENCE;
     auto presentFenceTime = std::make_shared<FenceTime>(mPreviousPresentFence);
-    DEFINE_STACK_GUARD(presentFenceTime);
     getBE().mDisplayTimeline.push(presentFenceTime);
 
     DisplayStatInfo stats;
-    DEFINE_STACK_GUARD(stats);
     mScheduler->getDisplayStatInfo(&stats);
 
-    ASSERT_ON_STACK_GUARD();
-
     // We use the mRefreshStartTime which might be sampled a little later than
     // when we started doing work for this frame, but that should be okay
     // since updateCompositorTiming has snapping logic.
     updateCompositorTiming(stats, mRefreshStartTime, presentFenceTime);
     CompositorTiming compositorTiming;
-    DEFINE_STACK_GUARD(compositorTiming);
-
     {
         std::lock_guard<std::mutex> lock(getBE().mCompositorTimingLock);
-        DEFINE_STACK_GUARD(lock);
         compositorTiming = getBE().mCompositorTiming;
-
-        ASSERT_ON_STACK_GUARD();
     }
 
     mDrawingState.traverseInZOrder([&](Layer* layer) {
         bool frameLatched =
                 layer->onPostComposition(displayDevice->getId(), glCompositionDoneFenceTime,
                                          presentFenceTime, compositorTiming);
-        DEFINE_STACK_GUARD(frameLatched);
         if (frameLatched) {
             recordBufferingStats(layer->getName().string(),
                     layer->getOccupancyHistory(false));
         }
-        ASSERT_ON_STACK_GUARD();
     });
 
     if (presentFenceTime->isValid()) {
-        ASSERT_ON_STACK_GUARD();
         mScheduler->addPresentFence(presentFenceTime);
-        ASSERT_ON_STACK_GUARD();
     }
 
     if (!hasSyncFramework) {
@@ -2108,80 +2000,58 @@
         }
     }
 
-    ASSERT_ON_STACK_GUARD();
-
     if (mAnimCompositionPending) {
         mAnimCompositionPending = false;
 
         if (presentFenceTime->isValid()) {
             mAnimFrameTracker.setActualPresentFence(
                     std::move(presentFenceTime));
-
-            ASSERT_ON_STACK_GUARD();
         } else if (displayDevice && getHwComposer().isConnected(*displayDevice->getId())) {
             // The HWC doesn't support present fences, so use the refresh
             // timestamp instead.
             const nsecs_t presentTime =
                     getHwComposer().getRefreshTimestamp(*displayDevice->getId());
-            DEFINE_STACK_GUARD(presentTime);
-
             mAnimFrameTracker.setActualPresentTime(presentTime);
-            ASSERT_ON_STACK_GUARD();
         }
         mAnimFrameTracker.advanceFrame();
     }
 
-    ASSERT_ON_STACK_GUARD();
-
     mTimeStats->incrementTotalFrames();
     if (mHadClientComposition) {
         mTimeStats->incrementClientCompositionFrames();
     }
 
-    ASSERT_ON_STACK_GUARD();
-
     mTimeStats->setPresentFenceGlobal(presentFenceTime);
 
-    ASSERT_ON_STACK_GUARD();
-
     if (displayDevice && getHwComposer().isConnected(*displayDevice->getId()) &&
         !displayDevice->isPoweredOn()) {
         return;
     }
 
     nsecs_t currentTime = systemTime();
-    DEFINE_STACK_GUARD(currentTime);
     if (mHasPoweredOff) {
         mHasPoweredOff = false;
     } else {
         nsecs_t elapsedTime = currentTime - getBE().mLastSwapTime;
-        DEFINE_STACK_GUARD(elapsedTime);
         size_t numPeriods = static_cast<size_t>(elapsedTime / stats.vsyncPeriod);
-        DEFINE_STACK_GUARD(numPeriods);
         if (numPeriods < SurfaceFlingerBE::NUM_BUCKETS - 1) {
             getBE().mFrameBuckets[numPeriods] += elapsedTime;
         } else {
             getBE().mFrameBuckets[SurfaceFlingerBE::NUM_BUCKETS - 1] += elapsedTime;
         }
         getBE().mTotalTime += elapsedTime;
-
-        ASSERT_ON_STACK_GUARD();
     }
     getBE().mLastSwapTime = currentTime;
-    ASSERT_ON_STACK_GUARD();
 
     {
         std::lock_guard lock(mTexturePoolMutex);
-        DEFINE_STACK_GUARD(lock);
         const size_t refillCount = mTexturePoolSize - mTexturePool.size();
-        DEFINE_STACK_GUARD(refillCount);
         if (refillCount > 0) {
             const size_t offset = mTexturePool.size();
             mTexturePool.resize(mTexturePoolSize);
             getRenderEngine().genTextures(refillCount, mTexturePool.data() + offset);
             ATRACE_INT("TexturePoolSize", mTexturePool.size());
         }
-        ASSERT_ON_STACK_GUARD();
     }
 
     mTransactionCompletedThread.addPresentFence(mPreviousPresentFence);
@@ -2190,10 +2060,7 @@
     if (mLumaSampling) {
         mRegionSamplingThread->sampleNow();
     }
-
-    ASSERT_ON_STACK_GUARD();
 }
-#pragma clang optimize on // b/119477596
 
 void SurfaceFlinger::computeLayerBounds() {
     for (const auto& pair : mDisplays) {
@@ -3460,6 +3327,16 @@
     // Perform some cleanup steps if we used client composition.
     if (hasClientComposition) {
         clientCompositionDisplay.clearRegion = clearRegion;
+
+        // We boost GPU frequency here because there will be color spaces conversion
+        // and it's expensive. We boost the GPU frequency so that GPU composition can
+        // finish in time. We must reset GPU frequency afterwards, because high frequency
+        // consumes extra battery.
+        const bool expensiveRenderingExpected =
+                clientCompositionDisplay.outputDataspace == Dataspace::DISPLAY_P3;
+        if (expensiveRenderingExpected && displayId) {
+            mPowerAdvisor.setExpensiveRenderingExpected(*displayId, true);
+        }
         if (!debugRegion.isEmpty()) {
             Region::const_iterator it = debugRegion.begin();
             Region::const_iterator end = debugRegion.end();
@@ -3475,6 +3352,9 @@
         }
         renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayers,
                                 buf->getNativeBuffer(), std::move(fd), readyFence);
+        if (expensiveRenderingExpected && displayId) {
+            mPowerAdvisor.setExpensiveRenderingExpected(*displayId, false);
+        }
     }
     return true;
 }
@@ -4002,6 +3882,11 @@
     if (what & layer_state_t::eMetadataChanged) {
         if (layer->setMetadata(s.metadata)) flags |= eTraversalNeeded;
     }
+    if (what & layer_state_t::eColorSpaceAgnosticChanged) {
+        if (layer->setColorSpaceAgnostic(s.colorSpaceAgnostic)) {
+            flags |= eTraversalNeeded;
+        }
+    }
     std::vector<sp<CallbackHandle>> callbackHandles;
     if ((what & layer_state_t::eListenerCallbacksChanged) && (!s.listenerCallbacks.empty())) {
         mTransactionCompletedThread.run();
@@ -4505,7 +4390,7 @@
                   "    present offset: %9" PRId64 " ns\t     VSYNC period: %9" PRId64 " ns\n\n",
                   dispSyncPresentTimeOffset, getVsyncPeriod());
 
-    StringAppendF(&result, "Scheduler enabled. 90Hz feature: %s\n", mUse90Hz ? "on" : "off");
+    StringAppendF(&result, "Scheduler enabled.");
     StringAppendF(&result, "+  Smart 90 for video detection: %s\n\n",
                   mUseSmart90ForVideo ? "on" : "off");
     mScheduler->dump(mAppConnectionHandle, result);
@@ -5760,21 +5645,30 @@
     // make sure that the current config is still allowed
     int currentConfigIndex = getHwComposer().getActiveConfigIndex(*displayId);
     if (!isConfigAllowed(*displayId, currentConfigIndex)) {
-        // TODO(b/122906558): stop querying HWC for the available configs and instead use the cached
-        // configs queried on boot
-        auto configs = getHwComposer().getConfigs(*displayId);
-
-        for (int i = 0; i < configs.size(); i++) {
-            if (isConfigAllowed(*displayId, i)) {
+        for (const auto& [type, config] : mRefreshRateConfigs[*displayId]->getRefreshRates()) {
+            if (isConfigAllowed(*displayId, config.configId)) {
                 // TODO: we switch to the first allowed config. In the future
                 // we may want to enhance this logic to pick a similar config
                 // to the current one
-                ALOGV("Old config is not allowed - switching to config %d", i);
-                setDesiredActiveConfig(displayToken, i, ConfigEvent::Changed);
+                ALOGV("Old config is not allowed - switching to config %d", config.configId);
+                setDesiredActiveConfig(displayToken, config.configId,
+                                       Scheduler::ConfigEvent::Changed);
                 break;
             }
         }
     }
+
+    // If idle timer and fps detection are disabled and we are in RefreshRateType::DEFAULT,
+    // there is no trigger to move to RefreshRateType::PERFORMANCE, even if it is an allowed.
+    if (!mScheduler->isIdleTimerEnabled() && !mUseSmart90ForVideo) {
+        const auto performanceRefreshRate =
+                mRefreshRateConfigs[*displayId]->getRefreshRate(RefreshRateType::PERFORMANCE);
+        if (isConfigAllowed(*displayId, performanceRefreshRate.configId)) {
+            mPhaseOffsets->setRefreshRateType(
+                    scheduler::RefreshRateConfigs::RefreshRateType::PERFORMANCE);
+            setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::Changed);
+        }
+    }
 }
 
 status_t SurfaceFlinger::setAllowedDisplayConfigs(const android::sp<android::IBinder>& displayToken,
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 8f80175..8de1e97 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -53,6 +53,7 @@
 #include "DisplayDevice.h"
 #include "DisplayHardware/HWC2.h"
 #include "DisplayHardware/HWComposer.h"
+#include "DisplayHardware/PowerAdvisor.h"
 #include "Effects/Daltonizer.h"
 #include "FrameTracker.h"
 #include "LayerStats.h"
@@ -521,13 +522,11 @@
     void signalLayerUpdate();
     void signalRefresh();
 
-    enum class ConfigEvent { None, Changed };
-
     // called on the main thread in response to initializeDisplays()
     void onInitializeDisplays() REQUIRES(mStateLock);
     // Sets the desired active config bit. It obtains the lock, and sets mDesiredActiveConfig.
-    void setDesiredActiveConfig(const sp<IBinder>& displayToken, int mode, ConfigEvent event)
-            REQUIRES(mStateLock);
+    void setDesiredActiveConfig(const sp<IBinder>& displayToken, int mode,
+                                Scheduler::ConfigEvent event) REQUIRES(mStateLock);
     // Once HWC has returned the present fence, this sets the active config and a new refresh
     // rate in SF. It also triggers HWC vsync.
     void setActiveConfigInternal() REQUIRES(mStateLock);
@@ -808,8 +807,8 @@
 
     // Sets the refresh rate by switching active configs, if they are available for
     // the desired refresh rate.
-    void setRefreshRateTo(scheduler::RefreshRateConfigs::RefreshRateType, ConfigEvent event)
-            REQUIRES(mStateLock);
+    void setRefreshRateTo(scheduler::RefreshRateConfigs::RefreshRateType,
+                          Scheduler::ConfigEvent event) REQUIRES(mStateLock);
 
     bool isConfigAllowed(const DisplayId& displayId, int32_t config);
 
@@ -1101,13 +1100,15 @@
     /* ------------------------------------------------------------------------
      * Scheduler
      */
-    bool mUse90Hz = false;
     bool mUseSmart90ForVideo = false;
     std::unique_ptr<Scheduler> mScheduler;
     sp<Scheduler::ConnectionHandle> mAppConnectionHandle;
     sp<Scheduler::ConnectionHandle> mSfConnectionHandle;
     std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
 
+    std::unordered_map<DisplayId, std::shared_ptr<scheduler::RefreshRateConfigs>>
+            mRefreshRateConfigs;
+
     std::mutex mAllowedConfigsLock;
     std::unordered_map<DisplayId, std::unique_ptr<const AllowedDisplayConfigs>> mAllowedConfigs
             GUARDED_BY(mAllowedConfigsLock);
@@ -1115,7 +1116,7 @@
     struct ActiveConfigInfo {
         int configId;
         sp<IBinder> displayToken;
-        ConfigEvent event;
+        Scheduler::ConfigEvent event;
 
         bool operator!=(const ActiveConfigInfo& other) const {
             if (configId != other.configId) {
@@ -1143,11 +1144,11 @@
     InputWindowCommands mPendingInputWindowCommands GUARDED_BY(mStateLock);
     // Should only be accessed by the main thread.
     InputWindowCommands mInputWindowCommands;
-
     ui::DisplayPrimaries mInternalDisplayPrimaries;
 
     sp<SetInputWindowsListener> mSetInputWindowsListener;
     bool mPendingSyncInputWindows GUARDED_BY(mStateLock);
+    Hwc2::impl::PowerAdvisor mPowerAdvisor;
 };
 }; // namespace android
 
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index e676d20..875fca8 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -4,7 +4,6 @@
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
 #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
 #include <android/hardware/configstore/1.1/types.h>
-#include <android/hardware/configstore/1.2/ISurfaceFlingerConfigs.h>
 #include <configstore/Utils.h>
 
 #include <cstdlib>
@@ -16,9 +15,9 @@
 namespace sysprop {
 using namespace android::hardware::configstore;
 using namespace android::hardware::configstore::V1_0;
-using ::android::hardware::configstore::V1_2::DisplayPrimaries;
-using ::android::hardware::graphics::common::V1_2::Dataspace;
-using ::android::hardware::graphics::common::V1_2::PixelFormat;
+using android::hardware::graphics::common::V1_2::Dataspace;
+using android::hardware::graphics::common::V1_2::PixelFormat;
+using android::ui::DisplayPrimaries;
 
 int64_t vsync_event_phase_offset_ns(int64_t defaultValue) {
     auto temp = SurfaceFlingerProperties::vsync_event_phase_offset_ns();
@@ -167,33 +166,15 @@
     auto tmpuseColorManagement = SurfaceFlingerProperties::use_color_management();
     auto tmpHasHDRDisplay = SurfaceFlingerProperties::has_HDR_display();
     auto tmpHasWideColorDisplay = SurfaceFlingerProperties::has_wide_color_display();
-    if (tmpuseColorManagement.has_value() && tmpHasHDRDisplay.has_value() &&
-        tmpHasWideColorDisplay.has_value()) {
-        return *tmpuseColorManagement || *tmpHasHDRDisplay || *tmpHasWideColorDisplay;
-    }
-    auto surfaceFlingerConfigsServiceV1_2 = ISurfaceFlingerConfigs::getService();
-    if (surfaceFlingerConfigsServiceV1_2) {
-        return getBool<V1_2::ISurfaceFlingerConfigs,
-                       &V1_2::ISurfaceFlingerConfigs::useColorManagement>(defaultValue);
-    }
-    return defaultValue;
-}
 
-auto getCompositionPreference(sp<V1_2::ISurfaceFlingerConfigs> configsServiceV1_2) {
-    Dataspace defaultCompositionDataspace = Dataspace::V0_SRGB;
-    PixelFormat defaultCompositionPixelFormat = PixelFormat::RGBA_8888;
-    Dataspace wideColorGamutCompositionDataspace = Dataspace::V0_SRGB;
-    PixelFormat wideColorGamutCompositionPixelFormat = PixelFormat::RGBA_8888;
-    configsServiceV1_2->getCompositionPreference(
-            [&](auto tmpDefaultDataspace, auto tmpDefaultPixelFormat,
-                auto tmpWideColorGamutDataspace, auto tmpWideColorGamutPixelFormat) {
-                defaultCompositionDataspace = tmpDefaultDataspace;
-                defaultCompositionPixelFormat = tmpDefaultPixelFormat;
-                wideColorGamutCompositionDataspace = tmpWideColorGamutDataspace;
-                wideColorGamutCompositionPixelFormat = tmpWideColorGamutPixelFormat;
-            });
-    return std::tuple(defaultCompositionDataspace, defaultCompositionPixelFormat,
-                      wideColorGamutCompositionDataspace, wideColorGamutCompositionPixelFormat);
+    auto tmpuseColorManagementVal = tmpuseColorManagement.has_value() ? *tmpuseColorManagement :
+        defaultValue;
+    auto tmpHasHDRDisplayVal = tmpHasHDRDisplay.has_value() ? *tmpHasHDRDisplay :
+        defaultValue;
+    auto tmpHasWideColorDisplayVal = tmpHasWideColorDisplay.has_value() ? *tmpHasWideColorDisplay :
+        defaultValue;
+
+    return tmpuseColorManagementVal || tmpHasHDRDisplayVal || tmpHasWideColorDisplayVal;
 }
 
 int64_t default_composition_dataspace(Dataspace defaultValue) {
@@ -201,10 +182,6 @@
     if (temp.has_value()) {
         return *temp;
     }
-    auto configsServiceV1_2 = V1_2::ISurfaceFlingerConfigs::getService();
-    if (configsServiceV1_2) {
-        return static_cast<int64_t>(get<0>(getCompositionPreference(configsServiceV1_2)));
-    }
     return static_cast<int64_t>(defaultValue);
 }
 
@@ -213,10 +190,6 @@
     if (temp.has_value()) {
         return *temp;
     }
-    auto configsServiceV1_2 = V1_2::ISurfaceFlingerConfigs::getService();
-    if (configsServiceV1_2) {
-        return static_cast<int32_t>(get<1>(getCompositionPreference(configsServiceV1_2)));
-    }
     return static_cast<int32_t>(defaultValue);
 }
 
@@ -225,10 +198,6 @@
     if (temp.has_value()) {
         return *temp;
     }
-    auto configsServiceV1_2 = V1_2::ISurfaceFlingerConfigs::getService();
-    if (configsServiceV1_2) {
-        return static_cast<int64_t>(get<2>(getCompositionPreference(configsServiceV1_2)));
-    }
     return static_cast<int64_t>(defaultValue);
 }
 
@@ -237,13 +206,17 @@
     if (temp.has_value()) {
         return *temp;
     }
-    auto configsServiceV1_2 = V1_2::ISurfaceFlingerConfigs::getService();
-    if (configsServiceV1_2) {
-        return static_cast<int32_t>(get<3>(getCompositionPreference(configsServiceV1_2)));
-    }
     return static_cast<int32_t>(defaultValue);
 }
 
+int32_t set_idle_timer_ms(int32_t defaultValue) {
+    auto temp = SurfaceFlingerProperties::set_idle_timer_ms();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return defaultValue;
+}
+
 #define DISPLAY_PRIMARY_SIZE 3
 
 constexpr float kSrgbRedX = 0.4123f;
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index c86880e..0199d79 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -3,8 +3,9 @@
 #define SURFACEFLINGERPROPERTIES_H_
 
 #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
-#include <android/hardware/configstore/1.2/ISurfaceFlingerConfigs.h>
+#include <android/hardware/graphics/common/1.2/types.h>
 #include <sysprop/SurfaceFlingerProperties.sysprop.h>
+#include <ui/ConfigStoreTypes.h>
 
 #include <cstdint>
 #include <optional>
@@ -54,7 +55,9 @@
 int32_t wcg_composition_pixel_format(
         android::hardware::graphics::common::V1_2::PixelFormat defaultValue);
 
-android::hardware::configstore::V1_2::DisplayPrimaries getDisplayNativePrimaries();
+int32_t set_idle_timer_ms(int32_t defaultValue);
+
+android::ui::DisplayPrimaries getDisplayNativePrimaries();
 } // namespace sysprop
 } // namespace android
 #endif // SURFACEFLINGERPROPERTIES_H_
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 429636b..949c23c 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -286,3 +286,14 @@
     access: Readonly
     prop_name: "ro.surface_flinger.display_primary_white"
 }
+
+# setIdleTimerMs indicates what is considered a timeout in milliseconds for Scheduler. This value is
+# used by the Scheduler to trigger inactivity callbacks that will switch the display to a lower
+# refresh rate. Setting this property to 0 means there is no timer.
+prop {
+    api_name: "set_idle_timer_ms"
+    type: Integer
+    scope: Internal
+    access: Readonly
+    prop_name: "ro.surface_flinger.set_idle_timer_ms"
+}
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 6deec29..30ae764 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -592,8 +592,6 @@
                     renderengine::LayerSettings layer = layerSettings.back();
                     EXPECT_THAT(layer.source.buffer.buffer, Not(IsNull()));
                     EXPECT_THAT(layer.source.buffer.fence, Not(IsNull()));
-                    EXPECT_EQ(renderengine::Buffer::CachingHint::NO_CACHE,
-                              layer.source.buffer.cacheHint);
                     EXPECT_EQ(DEFAULT_TEXTURE_ID, layer.source.buffer.textureName);
                     EXPECT_EQ(false, layer.source.buffer.isY410BT2020);
                     EXPECT_EQ(true, layer.source.buffer.usePremultipliedAlpha);
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 1487d47..9bf29a2 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -1926,6 +1926,10 @@
     EXPECT_CALL(*mComposer, destroyVirtualDisplay(Case::Display::HWC_DISPLAY_ID))
             .WillOnce(Return(Error::NONE));
     EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
+
+    // Cleanup
+    mFlinger.mutableCurrentState().displays.removeItem(displayToken);
+    mFlinger.mutableDrawingState().displays.removeItem(displayToken);
 }
 
 TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAddedWithNoSurface) {
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
index 3d887ea..4342dc9 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
@@ -45,6 +45,7 @@
 
     std::unique_ptr<RefreshRateStats> mRefreshRateStats;
     std::shared_ptr<android::mock::TimeStats> mTimeStats;
+    std::shared_ptr<RefreshRateConfigs> mRefreshRateConfigs;
 };
 
 RefreshRateStatsTest::RefreshRateStatsTest() {
@@ -61,7 +62,8 @@
 
 void RefreshRateStatsTest::init(std::vector<std::shared_ptr<const HWC2::Display::Config>> configs) {
     mTimeStats = std::make_shared<android::mock::TimeStats>();
-    mRefreshRateStats = std::make_unique<RefreshRateStats>(configs, mTimeStats);
+    mRefreshRateConfigs = std::make_shared<RefreshRateConfigs>(configs);
+    mRefreshRateStats = std::make_unique<RefreshRateStats>(mRefreshRateConfigs, mTimeStats);
 }
 
 namespace {
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index d61973e..79b5ca0 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -324,6 +324,7 @@
     auto& mutableTexturePool() { return mFlinger->mTexturePool; }
     auto& mutableTransactionFlags() { return mFlinger->mTransactionFlags; }
     auto& mutableUseHwcVirtualDisplays() { return mFlinger->mUseHwcVirtualDisplays; }
+    auto& mutablePowerAdvisor() { return mFlinger->mPowerAdvisor; }
 
     auto& mutableComposerSequenceId() { return mFlinger->getBE().mComposerSequenceId; }
     auto& mutableHwcDisplayData() { return getHwComposer().mDisplayData; }
@@ -352,18 +353,11 @@
      * Wrapper classes for Read-write access to private data to set up
      * preconditions and assert post-conditions.
      */
-    class FakePowerAdvisor : public Hwc2::PowerAdvisor {
-    public:
-        FakePowerAdvisor() = default;
-        ~FakePowerAdvisor() override = default;
-        void setExpensiveRenderingExpected(hwc2_display_t, bool) override {}
-    };
-
     struct HWC2Display : public HWC2::impl::Display {
-        HWC2Display(Hwc2::Composer& composer, Hwc2::PowerAdvisor& advisor,
+        HWC2Display(Hwc2::Composer& composer,
                     const std::unordered_set<HWC2::Capability>& capabilities, hwc2_display_t id,
                     HWC2::DisplayType type)
-              : HWC2::impl::Display(composer, advisor, capabilities, id, type) {}
+              : HWC2::impl::Display(composer, capabilities, id, type) {}
         ~HWC2Display() {
             // Prevents a call to disable vsyncs.
             mType = HWC2::DisplayType::Invalid;
@@ -427,14 +421,7 @@
             return *this;
         }
 
-        auto& setPowerAdvisor(Hwc2::PowerAdvisor* powerAdvisor) {
-            mPowerAdvisor = powerAdvisor;
-            return *this;
-        }
-
         void inject(TestableSurfaceFlinger* flinger, Hwc2::Composer* composer) {
-            static FakePowerAdvisor defaultPowerAdvisor;
-            if (mPowerAdvisor == nullptr) mPowerAdvisor = &defaultPowerAdvisor;
             static const std::unordered_set<HWC2::Capability> defaultCapabilities;
             if (mCapabilities == nullptr) mCapabilities = &defaultCapabilities;
 
@@ -442,8 +429,8 @@
             // not refer to an instance owned by FakeHwcDisplayInjector. This
             // class has temporary lifetime, while the constructed HWC2::Display
             // is much longer lived.
-            auto display = std::make_unique<HWC2Display>(*composer, *mPowerAdvisor, *mCapabilities,
-                                                         mHwcDisplayId, mHwcDisplayType);
+            auto display = std::make_unique<HWC2Display>(*composer, *mCapabilities, mHwcDisplayId,
+                                                         mHwcDisplayType);
 
             auto config = HWC2::Display::Config::Builder(*display, mActiveConfig);
             config.setWidth(mWidth);
@@ -478,7 +465,6 @@
         int32_t mDpiY = DEFAULT_DPI;
         int32_t mActiveConfig = DEFAULT_ACTIVE_CONFIG;
         const std::unordered_set<HWC2::Capability>* mCapabilities = nullptr;
-        Hwc2::PowerAdvisor* mPowerAdvisor = nullptr;
     };
 
     class FakeDisplayDeviceInjector {
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
index dc6d83b..7c65f95 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
@@ -29,7 +29,7 @@
     PowerAdvisor();
     ~PowerAdvisor() override;
 
-    MOCK_METHOD2(setExpensiveRenderingExpected, void(hwc2_display_t displayId, bool expected));
+    MOCK_METHOD2(setExpensiveRenderingExpected, void(DisplayId displayId, bool expected));
 };
 
 } // namespace mock