Merge changes from topic "deprecate-content-detection-v1"

* changes:
  Move LayerHistoryV2 to LayerHistory
  SF: Deprecate content detection v1
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 560459b..a8c0500 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -14,6 +14,7 @@
                libs/graphicsenv/
                libs/gui/
                libs/input/
+               libs/nativedisplay/
                libs/renderengine/
                libs/ui/
                libs/vr/
diff --git a/cmds/bugreportz/bugreportz.cpp b/cmds/bugreportz/bugreportz.cpp
index ded0ed3..203d748 100644
--- a/cmds/bugreportz/bugreportz.cpp
+++ b/cmds/bugreportz/bugreportz.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+#include "bugreportz.h"
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -22,11 +26,6 @@
 
 #include <string>
 
-#include <android-base/file.h>
-#include <android-base/strings.h>
-
-#include "bugreportz.h"
-
 static constexpr char BEGIN_PREFIX[] = "BEGIN:";
 static constexpr char PROGRESS_PREFIX[] = "PROGRESS:";
 
@@ -70,6 +69,30 @@
     }
     // Process final line, in case it didn't finish with newline
     write_line(line, show_progress);
+    return EXIT_SUCCESS;
+}
 
+int bugreportz_stream(int s) {
+    while (1) {
+        char buffer[65536];
+        ssize_t bytes_read = TEMP_FAILURE_RETRY(read(s, buffer, sizeof(buffer)));
+        if (bytes_read == 0) {
+            break;
+        } else if (bytes_read == -1) {
+            // EAGAIN really means time out, so change the errno.
+            if (errno == EAGAIN) {
+                errno = ETIMEDOUT;
+            }
+            printf("FAIL:Bugreport read terminated abnormally (%s)\n", strerror(errno));
+            return EXIT_FAILURE;
+        }
+
+        if (!android::base::WriteFully(android::base::borrowed_fd(STDOUT_FILENO), buffer,
+                                       bytes_read)) {
+            printf("Failed to write data to stdout: trying to send %zd bytes (%s)\n", bytes_read,
+                   strerror(errno));
+            return EXIT_FAILURE;
+        }
+    }
     return EXIT_SUCCESS;
 }
diff --git a/cmds/bugreportz/bugreportz.h b/cmds/bugreportz/bugreportz.h
index 7af289b..cdeceae 100644
--- a/cmds/bugreportz/bugreportz.h
+++ b/cmds/bugreportz/bugreportz.h
@@ -19,4 +19,8 @@
 // Ownership of the socket is not transferred.
 int bugreportz(int s, bool show_progress);
 
+// Calls dumpstate using the given socket and write the file content to stdout
+// instead of file location. Ownership of the socket is not transferred.
+int bugreportz_stream(int s);
+
 #endif  // BUGREPORTZ_H
diff --git a/cmds/bugreportz/main.cpp b/cmds/bugreportz/main.cpp
index 1d48e08..cd2652c 100644
--- a/cmds/bugreportz/main.cpp
+++ b/cmds/bugreportz/main.cpp
@@ -26,13 +26,14 @@
 
 #include "bugreportz.h"
 
-static constexpr char VERSION[] = "1.1";
+static constexpr char VERSION[] = "1.2";
 
 static void show_usage() {
     fprintf(stderr,
-            "usage: bugreportz [-hpv]\n"
+            "usage: bugreportz [-hpsv]\n"
             "  -h: to display this help message\n"
             "  -p: display progress\n"
+            "  -s: stream content to standard output\n"
             "  -v: to display the version\n"
             "  or no arguments to generate a zipped bugreport\n");
 }
@@ -43,10 +44,11 @@
 
 int main(int argc, char* argv[]) {
     bool show_progress = false;
+    bool stream_data = false;
     if (argc > 1) {
         /* parse arguments */
         int c;
-        while ((c = getopt(argc, argv, "hpv")) != -1) {
+        while ((c = getopt(argc, argv, "hpsv")) != -1) {
             switch (c) {
                 case 'h':
                     show_usage();
@@ -54,6 +56,9 @@
                 case 'p':
                     show_progress = true;
                     break;
+                case 's':
+                    stream_data = true;
+                    break;
                 case 'v':
                     show_version();
                     return EXIT_SUCCESS;
@@ -75,7 +80,11 @@
     // should be reused instead.
 
     // Start the dumpstatez service.
-    property_set("ctl.start", "dumpstatez");
+    if (stream_data) {
+        property_set("ctl.start", "dumpstate");
+    } else {
+        property_set("ctl.start", "dumpstatez");
+    }
 
     // Socket will not be available until service starts.
     int s = -1;
@@ -103,7 +112,12 @@
                 strerror(errno));
     }
 
-    int ret = bugreportz(s, show_progress);
+    int ret;
+    if (stream_data) {
+        ret = bugreportz_stream(s);
+    } else {
+        ret = bugreportz(s, show_progress);
+    }
 
     if (close(s) == -1) {
         fprintf(stderr, "WARNING: error closing socket: %s\n", strerror(errno));
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 8bdde62..cbb74b3 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -719,6 +719,9 @@
     return timeout_ms > MINIMUM_LOGCAT_TIMEOUT_MS ? timeout_ms : MINIMUM_LOGCAT_TIMEOUT_MS;
 }
 
+// Opens a socket and returns its file descriptor.
+static int open_socket(const char* service);
+
 Dumpstate::ConsentCallback::ConsentCallback() : result_(UNAVAILABLE), start_time_(Nanotime()) {
 }
 
@@ -2287,20 +2290,18 @@
 
 static void ShowUsage() {
     fprintf(stderr,
-            "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o directory] [-d] [-p] "
-            "[-z] [-s] [-S] [-q] [-P] [-R] [-L] [-V version]\n"
+            "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o directory] [-p] "
+            "[-s] [-S] [-q] [-P] [-R] [-L] [-V version]\n"
             "  -h: display this help message\n"
             "  -b: play sound file instead of vibrate, at beginning of job\n"
             "  -e: play sound file instead of vibrate, at end of job\n"
             "  -o: write to custom directory (only in limited mode)\n"
-            "  -d: append date to filename\n"
             "  -p: capture screenshot to filename.png\n"
-            "  -z: generate zipped file\n"
-            "  -s: write output to control socket (for init)\n"
-            "  -S: write file location to control socket (for init; requires -z)\n"
+            "  -s: write zipped file to control socket (for init)\n"
+            "  -S: write file location to control socket (for init)\n"
             "  -q: disable vibrate\n"
             "  -P: send broadcast when started and do progress updates\n"
-            "  -R: take bugreport in remote mode (requires -z and -d, shouldn't be used with -P)\n"
+            "  -R: take bugreport in remote mode (shouldn't be used with -P)\n"
             "  -w: start binder service and make it wait for a call to startBugreport\n"
             "  -L: output limited information that is safe for submission in feedback reports\n"
             "  -v: prints the dumpstate header and exit\n");
@@ -2399,21 +2400,17 @@
 
 /*
  * Prepares state like filename, screenshot path, etc in Dumpstate. Also initializes ZipWriter
- * if we are writing zip files and adds the version file.
+ * and adds the version file. Return false if zip_file could not be open to write.
  */
-static void PrepareToWriteToFile() {
+static bool PrepareToWriteToFile() {
     MaybeResolveSymlink(&ds.bugreport_internal_dir_);
 
     std::string build_id = android::base::GetProperty("ro.build.id", "UNKNOWN_BUILD");
     std::string device_name = android::base::GetProperty("ro.product.name", "UNKNOWN_DEVICE");
     ds.base_name_ = StringPrintf("bugreport-%s-%s", device_name.c_str(), build_id.c_str());
-    if (ds.options_->do_add_date) {
-        char date[80];
-        strftime(date, sizeof(date), "%Y-%m-%d-%H-%M-%S", localtime(&ds.now_));
-        ds.name_ = date;
-    } else {
-        ds.name_ = "undated";
-    }
+    char date[80];
+    strftime(date, sizeof(date), "%Y-%m-%d-%H-%M-%S", localtime(&ds.now_));
+    ds.name_ = date;
 
     if (ds.options_->telephony_only) {
         ds.base_name_ += "-telephony";
@@ -2440,18 +2437,17 @@
         destination.c_str(), ds.base_name_.c_str(), ds.name_.c_str(), ds.log_path_.c_str(),
         ds.tmp_path_.c_str(), ds.screenshot_path_.c_str());
 
-    if (ds.options_->do_zip_file) {
-        ds.path_ = ds.GetPath(ds.CalledByApi() ? "-zip.tmp" : ".zip");
-        MYLOGD("Creating initial .zip file (%s)\n", ds.path_.c_str());
-        create_parent_dirs(ds.path_.c_str());
-        ds.zip_file.reset(fopen(ds.path_.c_str(), "wb"));
-        if (ds.zip_file == nullptr) {
-            MYLOGE("fopen(%s, 'wb'): %s\n", ds.path_.c_str(), strerror(errno));
-        } else {
-            ds.zip_writer_.reset(new ZipWriter(ds.zip_file.get()));
-        }
-        ds.AddTextZipEntry("version.txt", ds.version_);
+    ds.path_ = ds.GetPath(ds.CalledByApi() ? "-zip.tmp" : ".zip");
+    MYLOGD("Creating initial .zip file (%s)\n", ds.path_.c_str());
+    create_parent_dirs(ds.path_.c_str());
+    ds.zip_file.reset(fopen(ds.path_.c_str(), "wb"));
+    if (ds.zip_file == nullptr) {
+        MYLOGE("fopen(%s, 'wb'): %s\n", ds.path_.c_str(), strerror(errno));
+        return false;
     }
+    ds.zip_writer_.reset(new ZipWriter(ds.zip_file.get()));
+    ds.AddTextZipEntry("version.txt", ds.version_);
+    return true;
 }
 
 /*
@@ -2459,14 +2455,9 @@
  * printing zipped file status, etc.
  */
 static void FinalizeFile() {
-    bool do_text_file = true;
-    if (ds.options_->do_zip_file) {
-        if (!ds.FinishZipFile()) {
-            MYLOGE("Failed to finish zip file; sending text bugreport instead\n");
-            do_text_file = true;
-        } else {
-            do_text_file = false;
-        }
+    bool do_text_file = !ds.FinishZipFile();
+    if (do_text_file) {
+        MYLOGE("Failed to finish zip file; sending text bugreport instead\n");
     }
 
     std::string final_path = ds.path_;
@@ -2475,7 +2466,9 @@
         android::os::CopyFileToFile(ds.path_, final_path);
     }
 
-    if (ds.options_->use_control_socket) {
+    if (ds.options_->stream_to_socket) {
+        android::os::CopyFileToFd(ds.path_, ds.control_socket_fd_);
+    } else if (ds.options_->progress_updates_to_socket) {
         if (do_text_file) {
             dprintf(ds.control_socket_fd_,
                     "FAIL:could not create zip file, check %s "
@@ -2531,7 +2524,6 @@
             break;
         case Dumpstate::BugreportMode::BUGREPORT_WEAR:
             options->do_progress_updates = true;
-            options->do_zip_file = true;
             options->do_screenshot = is_screenshot_requested;
             options->dumpstate_hal_mode = DumpstateMode::WEAR;
             break;
@@ -2544,7 +2536,6 @@
             break;
         case Dumpstate::BugreportMode::BUGREPORT_WIFI:
             options->wifi_only = true;
-            options->do_zip_file = true;
             options->do_screenshot = false;
             options->dumpstate_hal_mode = DumpstateMode::WIFI;
             break;
@@ -2555,11 +2546,11 @@
 
 static void LogDumpOptions(const Dumpstate::DumpOptions& options) {
     MYLOGI(
-        "do_zip_file: %d do_vibrate: %d use_socket: %d use_control_socket: %d do_screenshot: %d "
+        "do_vibrate: %d stream_to_socket: %d progress_updates_to_socket: %d do_screenshot: %d "
         "is_remote_mode: %d show_header_only: %d telephony_only: %d "
         "wifi_only: %d do_progress_updates: %d fd: %d bugreport_mode: %s dumpstate_hal_mode: %s "
         "limited_only: %d args: %s\n",
-        options.do_zip_file, options.do_vibrate, options.use_socket, options.use_control_socket,
+        options.do_vibrate, options.stream_to_socket, options.progress_updates_to_socket,
         options.do_screenshot, options.is_remote_mode, options.show_header_only,
         options.telephony_only, options.wifi_only,
         options.do_progress_updates, options.bugreport_fd.get(), options.bugreport_mode.c_str(),
@@ -2570,11 +2561,6 @@
                                         const android::base::unique_fd& bugreport_fd_in,
                                         const android::base::unique_fd& screenshot_fd_in,
                                         bool is_screenshot_requested) {
-    // In the new API world, date is always added; output is always a zip file.
-    // TODO(111441001): remove these options once they are obsolete.
-    do_add_date = true;
-    do_zip_file = true;
-
     // Duplicate the fds because the passed in fds don't outlive the binder transaction.
     bugreport_fd.reset(dup(bugreport_fd_in.get()));
     screenshot_fd.reset(dup(screenshot_fd_in.get()));
@@ -2588,18 +2574,20 @@
     while ((c = getopt(argc, argv, "dho:svqzpLPBRSV:w")) != -1) {
         switch (c) {
             // clang-format off
-            case 'd': do_add_date = true;            break;
-            case 'z': do_zip_file = true;            break;
             case 'o': out_dir = optarg;              break;
-            case 's': use_socket = true;             break;
-            case 'S': use_control_socket = true;     break;
+            case 's': stream_to_socket = true;       break;
+            case 'S': progress_updates_to_socket = true;    break;
             case 'v': show_header_only = true;       break;
             case 'q': do_vibrate = false;            break;
             case 'p': do_screenshot = true;          break;
             case 'P': do_progress_updates = true;    break;
             case 'R': is_remote_mode = true;         break;
             case 'L': limited_only = true;           break;
-            case 'V':                                break;  // compatibility no-op
+            case 'V':
+            case 'd':
+            case 'z':
+                // compatibility no-op
+                break;
             case 'w':
                 // This was already processed
                 break;
@@ -2628,19 +2616,15 @@
 }
 
 bool Dumpstate::DumpOptions::ValidateOptions() const {
-    if (bugreport_fd.get() != -1 && !do_zip_file) {
+    if (bugreport_fd.get() != -1 && stream_to_socket) {
         return false;
     }
 
-    if ((do_zip_file || do_add_date || do_progress_updates) && !OutputToFile()) {
+    if ((progress_updates_to_socket || do_progress_updates) && stream_to_socket) {
         return false;
     }
 
-    if (use_control_socket && !do_zip_file) {
-        return false;
-    }
-
-    if (is_remote_mode && (do_progress_updates || !do_zip_file || !do_add_date)) {
+    if (is_remote_mode && (do_progress_updates || stream_to_socket)) {
         return false;
     }
     return true;
@@ -2715,11 +2699,9 @@
  * The temporary bugreport is then populated via printfs, dumping contents of files and
  * output of commands to stdout.
  *
- * If zipping, the temporary bugreport file is added to the zip archive. Else it's renamed to final
- * text file.
+ * A bunch of other files and dumps are added to the zip archive.
  *
- * If zipping, a bunch of other files and dumps also get added to the zip archive. The log file also
- * gets added to the archive.
+ * The temporary bugreport file and the log file also get added to the archive.
  *
  * Bugreports are first generated in a local directory and later copied to the caller's fd
  * or directory if supplied.
@@ -2767,14 +2749,9 @@
     MYLOGD("dumpstate calling_uid = %d ; calling package = %s \n",
             calling_uid, calling_package.c_str());
 
-    // Redirect output if needed
-    bool is_redirecting = options_->OutputToFile();
-
     // TODO: temporarily set progress until it's part of the Dumpstate constructor
     std::string stats_path =
-        is_redirecting
-            ? android::base::StringPrintf("%s/dumpstate-stats.txt", bugreport_internal_dir_.c_str())
-            : "";
+        android::base::StringPrintf("%s/dumpstate-stats.txt", bugreport_internal_dir_.c_str());
     progress_.reset(new Progress(stats_path));
 
     if (acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME) < 0) {
@@ -2797,35 +2774,33 @@
 
     // If we are going to use a socket, do it as early as possible
     // to avoid timeouts from bugreport.
-    if (options_->use_socket) {
-        if (!redirect_to_socket(stdout, "dumpstate")) {
-            return ERROR;
-        }
-    }
-
-    if (options_->use_control_socket) {
+    if (options_->stream_to_socket || options_->progress_updates_to_socket) {
         MYLOGD("Opening control socket\n");
-        control_socket_fd_ = open_socket("dumpstate");
+        control_socket_fd_ = open_socket_fn_("dumpstate");
         if (control_socket_fd_ == -1) {
             return ERROR;
         }
-        options_->do_progress_updates = 1;
+        if (options_->progress_updates_to_socket) {
+            options_->do_progress_updates = 1;
+        }
     }
 
-    if (is_redirecting) {
-        PrepareToWriteToFile();
+    if (!PrepareToWriteToFile()) {
+        return ERROR;
+    }
 
-        if (options_->do_progress_updates) {
-            // clang-format off
-            std::vector<std::string> am_args = {
-                 "--receiver-permission", "android.permission.DUMP",
-            };
-            // clang-format on
-            // Send STARTED broadcast for apps that listen to bugreport generation events
-            SendBroadcast("com.android.internal.intent.action.BUGREPORT_STARTED", am_args);
-            if (options_->use_control_socket) {
-                dprintf(control_socket_fd_, "BEGIN:%s\n", path_.c_str());
-            }
+    // Interactive, wear & telephony modes are default to true.
+    // and may enable from cli option or when using control socket
+    if (options_->do_progress_updates) {
+        // clang-format off
+        std::vector<std::string> am_args = {
+                "--receiver-permission", "android.permission.DUMP",
+        };
+        // clang-format on
+        // Send STARTED broadcast for apps that listen to bugreport generation events
+        SendBroadcast("com.android.internal.intent.action.BUGREPORT_STARTED", am_args);
+        if (options_->progress_updates_to_socket) {
+            dprintf(control_socket_fd_, "BEGIN:%s\n", path_.c_str());
         }
     }
 
@@ -2840,40 +2815,38 @@
         Vibrate(150);
     }
 
-    if (options_->do_zip_file && zip_file != nullptr) {
+    if (zip_file != nullptr) {
         if (chown(path_.c_str(), AID_SHELL, AID_SHELL)) {
             MYLOGE("Unable to change ownership of zip file %s: %s\n", path_.c_str(),
-                   strerror(errno));
+                    strerror(errno));
         }
     }
 
     int dup_stdout_fd;
     int dup_stderr_fd;
-    if (is_redirecting) {
-        // Redirect stderr to log_path_ for debugging.
-        TEMP_FAILURE_RETRY(dup_stderr_fd = dup(fileno(stderr)));
-        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));
-        }
+    // Redirect stderr to log_path_ for debugging.
+    TEMP_FAILURE_RETRY(dup_stderr_fd = dup(fileno(stderr)));
+    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));
+    }
 
-        // Redirect stdout to tmp_path_. This is the main bugreport entry and will be
-        // moved into zip file later, if zipping.
-        TEMP_FAILURE_RETRY(dup_stdout_fd = dup(fileno(stdout)));
-        // TODO: why not write to a file instead of stdout to overcome this problem?
-        /* 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. */
-        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));
-        }
+    // Redirect stdout to tmp_path_. This is the main bugreport entry and will be
+    // moved into zip file later, if zipping.
+    TEMP_FAILURE_RETRY(dup_stdout_fd = dup(fileno(stdout)));
+    // TODO: why not write to a file instead of stdout to overcome this problem?
+    /* 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. */
+    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));
     }
 
     // Don't buffer stdout
@@ -2927,14 +2900,10 @@
     }
 
     /* close output if needed */
-    if (is_redirecting) {
-        TEMP_FAILURE_RETRY(dup2(dup_stdout_fd, fileno(stdout)));
-    }
+    TEMP_FAILURE_RETRY(dup2(dup_stdout_fd, fileno(stdout)));
 
     // Zip the (now complete) .tmp file within the internal directory.
-    if (options_->OutputToFile()) {
-        FinalizeFile();
-    }
+    FinalizeFile();
 
     // Share the final file with the caller if the user has consented or Shell is the caller.
     Dumpstate::RunStatus status = Dumpstate::RunStatus::OK;
@@ -2977,11 +2946,9 @@
     progress_->Save();
     MYLOGI("done (id %d)\n", id_);
 
-    if (is_redirecting) {
-        TEMP_FAILURE_RETRY(dup2(dup_stderr_fd, fileno(stderr)));
-    }
+    TEMP_FAILURE_RETRY(dup2(dup_stderr_fd, fileno(stderr)));
 
-    if (options_->use_control_socket && control_socket_fd_ != -1) {
+    if (control_socket_fd_ != -1) {
         MYLOGD("Closing control socket\n");
         close(control_socket_fd_);
     }
@@ -3050,10 +3017,7 @@
 }
 
 void Dumpstate::EnableParallelRunIfNeeded() {
-    // The thread pool needs to create temporary files to receive dump results.
-    // That's why we only enable it when the bugreport client chooses to output
-    // to a file.
-    if (!PropertiesHelper::IsParallelRun() || !options_->OutputToFile()) {
+    if (!PropertiesHelper::IsParallelRun()) {
         return;
     }
     dump_pool_ = std::make_unique<DumpPool>(bugreport_internal_dir_);
@@ -3198,7 +3162,8 @@
       options_(new Dumpstate::DumpOptions()),
       last_reported_percent_progress_(0),
       version_(version),
-      now_(time(nullptr)) {
+      now_(time(nullptr)),
+      open_socket_fn_(open_socket) {
 }
 
 Dumpstate& Dumpstate::GetInstance() {
@@ -3832,7 +3797,7 @@
     RunCommand(title, dumpsys, options, false, out_fd);
 }
 
-int open_socket(const char *service) {
+static int open_socket(const char* service) {
     int s = android_get_control_socket(service);
     if (s < 0) {
         MYLOGE("android_get_control_socket(%s): %s\n", service, strerror(errno));
@@ -3867,19 +3832,6 @@
     return fd;
 }
 
-/* redirect output to a service control socket */
-bool redirect_to_socket(FILE* redirect, const char* service) {
-    int fd = open_socket(service);
-    if (fd == -1) {
-        return false;
-    }
-    fflush(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.
 void create_parent_dirs(const char *path) {
     char *chp = const_cast<char *> (path);
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 3b9b1b7..255243f 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -54,6 +54,7 @@
 
 class DumpstateTest;
 class ProgressTest;
+class ZippedBugReportStreamTest;
 
 }  // namespace dumpstate
 }  // namespace os
@@ -197,6 +198,7 @@
  */
 class Dumpstate {
     friend class android::os::dumpstate::DumpstateTest;
+    friend class android::os::dumpstate::ZippedBugReportStreamTest;
 
   public:
     enum RunStatus { OK, HELP, INVALID_INPUT, ERROR, USER_CONSENT_DENIED, USER_CONSENT_TIMED_OUT };
@@ -388,12 +390,11 @@
      * Structure to hold options that determine the behavior of dumpstate.
      */
     struct DumpOptions {
-        bool do_add_date = false;
-        bool do_zip_file = false;
         bool do_vibrate = true;
-        // Writes bugreport content to a socket; only flatfile format is supported.
-        bool use_socket = false;
-        bool use_control_socket = false;
+        // Writes bugreport zipped file to a socket.
+        bool stream_to_socket = false;
+        // Writes generation progress updates to a socket.
+        bool progress_updates_to_socket = false;
         bool do_screenshot = false;
         bool is_screenshot_copied = false;
         bool is_remote_mode = false;
@@ -434,13 +435,6 @@
         /* Returns true if the options set so far are consistent. */
         bool ValidateOptions() const;
 
-        /* Returns if options specified require writing bugreport to a file */
-        bool OutputToFile() const {
-            // If we are not writing to socket, we will write to a file. If bugreport_fd is
-            // specified, it is preferred. If not bugreport is written to /bugreports.
-            return !use_socket;
-        }
-
         /* Returns if options specified require writing to custom file location */
         bool OutputToCustomFile() {
             // Custom location is only honored in limited mode.
@@ -466,7 +460,8 @@
 
     std::unique_ptr<Progress> progress_;
 
-    // When set, defines a socket file-descriptor use to report progress to bugreportz.
+    // When set, defines a socket file-descriptor use to report progress to bugreportz
+    // or to stream the zipped file to.
     int control_socket_fd_ = -1;
 
     // Bugreport format version;
@@ -478,8 +473,8 @@
     // `bugreport-BUILD_ID`.
     std::string base_name_;
 
-    // Name is the suffix part of the bugreport files - it's typically the date (when invoked with
-    // `-d`), but it could be changed by the user..
+    // Name is the suffix part of the bugreport files - it's typically the date,
+    // but it could be changed by the user..
     std::string name_;
 
     std::string bugreport_internal_dir_ = DUMPSTATE_DIRECTORY;
@@ -567,6 +562,8 @@
     // called by Shell.
     RunStatus CopyBugreportIfUserConsented(int32_t calling_uid);
 
+    std::function<int(const char *)> open_socket_fn_;
+
     // Used by GetInstance() only.
     explicit Dumpstate(const std::string& version = VERSION_CURRENT);
 
@@ -600,16 +597,6 @@
 int dump_files(const std::string& title, const char* dir, bool (*skip)(const char* path),
                int (*dump_from_fd)(const char* title, const char* path, int fd));
 
-/** opens a socket and returns its file descriptor */
-int open_socket(const char *service);
-
-/*
- * Redirects 'redirect' to a service control socket.
- *
- * Returns true if redirect succeeds.
- */
-bool redirect_to_socket(FILE* redirect, const char* service);
-
 /*
  * Redirects 'redirect' to a file indicated by 'path', truncating it.
  *
diff --git a/cmds/dumpstate/dumpstate.rc b/cmds/dumpstate/dumpstate.rc
index e491a4b..a80da4e 100644
--- a/cmds/dumpstate/dumpstate.rc
+++ b/cmds/dumpstate/dumpstate.rc
@@ -11,7 +11,7 @@
 
 # dumpstatez generates a zipped bugreport but also uses a socket to print the file location once
 # it is finished.
-service dumpstatez /system/bin/dumpstate -S -d -z
+service dumpstatez /system/bin/dumpstate -S
     socket dumpstate stream 0660 shell log
     class main
     disabled
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index 70bdbcc..fe6a34a 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -209,9 +209,7 @@
     static void GenerateBugreport() {
         // clang-format off
         char* argv[] = {
-            (char*)"dumpstate",
-            (char*)"-d",
-            (char*)"-z"
+            (char*)"dumpstate"
         };
         // clang-format on
         sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout)), sections));
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index fdeea24..b2518ad 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "dumpstate"
-#include <cutils/log.h>
+#define LOG_TAG "dumpstate_test"
 
 #include "DumpstateInternal.h"
 #include "DumpstateService.h"
@@ -24,6 +23,7 @@
 #include "DumpPool.h"
 
 #include <gmock/gmock.h>
+#include <gmock/gmock-matchers.h>
 #include <gtest/gtest.h>
 
 #include <fcntl.h>
@@ -39,7 +39,9 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <android/hardware/dumpstate/1.1/types.h>
+#include <cutils/log.h>
 #include <cutils/properties.h>
+#include <ziparchive/zip_archive.h>
 
 namespace android {
 namespace os {
@@ -175,11 +177,9 @@
 
     EXPECT_EQ(status, Dumpstate::RunStatus::OK);
 
-    EXPECT_FALSE(options_.do_add_date);
-    EXPECT_FALSE(options_.do_zip_file);
     EXPECT_EQ("", options_.out_dir);
-    EXPECT_FALSE(options_.use_socket);
-    EXPECT_FALSE(options_.use_control_socket);
+    EXPECT_FALSE(options_.stream_to_socket);
+    EXPECT_FALSE(options_.progress_updates_to_socket);
     EXPECT_FALSE(options_.show_header_only);
     EXPECT_TRUE(options_.do_vibrate);
     EXPECT_FALSE(options_.do_screenshot);
@@ -194,17 +194,13 @@
     char* argv[] = {
         const_cast<char*>("dumpstatez"),
         const_cast<char*>("-S"),
-        const_cast<char*>("-d"),
-        const_cast<char*>("-z"),
     };
     // clang-format on
 
     Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv);
 
     EXPECT_EQ(status, Dumpstate::RunStatus::OK);
-    EXPECT_TRUE(options_.do_add_date);
-    EXPECT_TRUE(options_.do_zip_file);
-    EXPECT_TRUE(options_.use_control_socket);
+    EXPECT_TRUE(options_.progress_updates_to_socket);
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
@@ -212,7 +208,7 @@
     EXPECT_FALSE(options_.do_screenshot);
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
-    EXPECT_FALSE(options_.use_socket);
+    EXPECT_FALSE(options_.stream_to_socket);
     EXPECT_FALSE(options_.limited_only);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
@@ -228,13 +224,11 @@
     Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv);
 
     EXPECT_EQ(status, Dumpstate::RunStatus::OK);
-    EXPECT_TRUE(options_.use_socket);
+    EXPECT_TRUE(options_.stream_to_socket);
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
-    EXPECT_FALSE(options_.do_add_date);
-    EXPECT_FALSE(options_.do_zip_file);
-    EXPECT_FALSE(options_.use_control_socket);
+    EXPECT_FALSE(options_.progress_updates_to_socket);
     EXPECT_FALSE(options_.show_header_only);
     EXPECT_FALSE(options_.do_screenshot);
     EXPECT_FALSE(options_.do_progress_updates);
@@ -245,105 +239,93 @@
 
 TEST_F(DumpOptionsTest, InitializeFullBugReport) {
     options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_FULL, fd, fd, true);
-    EXPECT_TRUE(options_.do_add_date);
     EXPECT_TRUE(options_.do_screenshot);
-    EXPECT_TRUE(options_.do_zip_file);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::FULL);
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
-    EXPECT_FALSE(options_.use_control_socket);
+    EXPECT_FALSE(options_.progress_updates_to_socket);
     EXPECT_FALSE(options_.show_header_only);
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
-    EXPECT_FALSE(options_.use_socket);
+    EXPECT_FALSE(options_.stream_to_socket);
     EXPECT_FALSE(options_.limited_only);
 }
 
 TEST_F(DumpOptionsTest, InitializeInteractiveBugReport) {
     options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, fd, fd, true);
-    EXPECT_TRUE(options_.do_add_date);
-    EXPECT_TRUE(options_.do_zip_file);
     EXPECT_TRUE(options_.do_progress_updates);
     EXPECT_TRUE(options_.do_screenshot);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::INTERACTIVE);
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
-    EXPECT_FALSE(options_.use_control_socket);
+    EXPECT_FALSE(options_.progress_updates_to_socket);
     EXPECT_FALSE(options_.show_header_only);
     EXPECT_FALSE(options_.is_remote_mode);
-    EXPECT_FALSE(options_.use_socket);
+    EXPECT_FALSE(options_.stream_to_socket);
     EXPECT_FALSE(options_.limited_only);
 }
 
 TEST_F(DumpOptionsTest, InitializeRemoteBugReport) {
     options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_REMOTE, fd, fd, false);
-    EXPECT_TRUE(options_.do_add_date);
-    EXPECT_TRUE(options_.do_zip_file);
     EXPECT_TRUE(options_.is_remote_mode);
     EXPECT_FALSE(options_.do_vibrate);
     EXPECT_FALSE(options_.do_screenshot);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::REMOTE);
 
     // Other options retain default values
-    EXPECT_FALSE(options_.use_control_socket);
+    EXPECT_FALSE(options_.progress_updates_to_socket);
     EXPECT_FALSE(options_.show_header_only);
     EXPECT_FALSE(options_.do_progress_updates);
-    EXPECT_FALSE(options_.use_socket);
+    EXPECT_FALSE(options_.stream_to_socket);
     EXPECT_FALSE(options_.limited_only);
 }
 
 TEST_F(DumpOptionsTest, InitializeWearBugReport) {
     options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WEAR, fd, fd, true);
-    EXPECT_TRUE(options_.do_add_date);
     EXPECT_TRUE(options_.do_screenshot);
-    EXPECT_TRUE(options_.do_zip_file);
     EXPECT_TRUE(options_.do_progress_updates);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::WEAR);
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
-    EXPECT_FALSE(options_.use_control_socket);
+    EXPECT_FALSE(options_.progress_updates_to_socket);
     EXPECT_FALSE(options_.show_header_only);
     EXPECT_FALSE(options_.is_remote_mode);
-    EXPECT_FALSE(options_.use_socket);
+    EXPECT_FALSE(options_.stream_to_socket);
     EXPECT_FALSE(options_.limited_only);
 }
 
 TEST_F(DumpOptionsTest, InitializeTelephonyBugReport) {
     options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_TELEPHONY, fd, fd, false);
-    EXPECT_TRUE(options_.do_add_date);
     EXPECT_FALSE(options_.do_screenshot);
-    EXPECT_TRUE(options_.do_zip_file);
     EXPECT_TRUE(options_.telephony_only);
     EXPECT_TRUE(options_.do_progress_updates);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::CONNECTIVITY);
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
-    EXPECT_FALSE(options_.use_control_socket);
+    EXPECT_FALSE(options_.progress_updates_to_socket);
     EXPECT_FALSE(options_.show_header_only);
     EXPECT_FALSE(options_.is_remote_mode);
-    EXPECT_FALSE(options_.use_socket);
+    EXPECT_FALSE(options_.stream_to_socket);
     EXPECT_FALSE(options_.limited_only);
 }
 
 TEST_F(DumpOptionsTest, InitializeWifiBugReport) {
     options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WIFI, fd, fd, false);
-    EXPECT_TRUE(options_.do_add_date);
     EXPECT_FALSE(options_.do_screenshot);
-    EXPECT_TRUE(options_.do_zip_file);
     EXPECT_TRUE(options_.wifi_only);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::WIFI);
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
-    EXPECT_FALSE(options_.use_control_socket);
+    EXPECT_FALSE(options_.progress_updates_to_socket);
     EXPECT_FALSE(options_.show_header_only);
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
-    EXPECT_FALSE(options_.use_socket);
+    EXPECT_FALSE(options_.stream_to_socket);
     EXPECT_FALSE(options_.limited_only);
 }
 
@@ -352,8 +334,6 @@
     char* argv[] = {
         const_cast<char*>("dumpstatez"),
         const_cast<char*>("-S"),
-        const_cast<char*>("-d"),
-        const_cast<char*>("-z"),
         const_cast<char*>("-q"),
         const_cast<char*>("-L"),
         const_cast<char*>("-o abc")
@@ -363,9 +343,7 @@
     Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv);
 
     EXPECT_EQ(status, Dumpstate::RunStatus::OK);
-    EXPECT_TRUE(options_.do_add_date);
-    EXPECT_TRUE(options_.do_zip_file);
-    EXPECT_TRUE(options_.use_control_socket);
+    EXPECT_TRUE(options_.progress_updates_to_socket);
     EXPECT_FALSE(options_.do_vibrate);
     EXPECT_TRUE(options_.limited_only);
     EXPECT_EQ(" abc", std::string(options_.out_dir));
@@ -375,7 +353,7 @@
     EXPECT_FALSE(options_.do_screenshot);
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
-    EXPECT_FALSE(options_.use_socket);
+    EXPECT_FALSE(options_.stream_to_socket);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
@@ -392,18 +370,16 @@
     Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv);
 
     EXPECT_EQ(status, Dumpstate::RunStatus::OK);
-    EXPECT_TRUE(options_.do_add_date);
     EXPECT_TRUE(options_.do_screenshot);
-    EXPECT_TRUE(options_.do_zip_file);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
-    EXPECT_FALSE(options_.use_control_socket);
+    EXPECT_FALSE(options_.progress_updates_to_socket);
     EXPECT_FALSE(options_.show_header_only);
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
-    EXPECT_FALSE(options_.use_socket);
+    EXPECT_FALSE(options_.stream_to_socket);
     EXPECT_FALSE(options_.wifi_only);
     EXPECT_FALSE(options_.limited_only);
 }
@@ -412,8 +388,6 @@
     // clang-format off
     char* argv[] = {
         const_cast<char*>("dumpstate"),
-        const_cast<char*>("-d"),
-        const_cast<char*>("-z"),
         const_cast<char*>("-s"),
         const_cast<char*>("-S"),
 
@@ -423,11 +397,9 @@
     Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv);
 
     EXPECT_EQ(status, Dumpstate::RunStatus::OK);
-    EXPECT_TRUE(options_.do_add_date);
-    EXPECT_TRUE(options_.do_zip_file);
     // TODO: Maybe we should trim the filename
-    EXPECT_TRUE(options_.use_socket);
-    EXPECT_TRUE(options_.use_control_socket);
+    EXPECT_TRUE(options_.stream_to_socket);
+    EXPECT_TRUE(options_.progress_updates_to_socket);
 
     // Other options retain default values
     EXPECT_FALSE(options_.show_header_only);
@@ -461,10 +433,8 @@
     EXPECT_TRUE(options_.is_remote_mode);
 
     // Other options retain default values
-    EXPECT_FALSE(options_.do_add_date);
-    EXPECT_FALSE(options_.do_zip_file);
-    EXPECT_FALSE(options_.use_socket);
-    EXPECT_FALSE(options_.use_control_socket);
+    EXPECT_FALSE(options_.stream_to_socket);
+    EXPECT_FALSE(options_.progress_updates_to_socket);
     EXPECT_FALSE(options_.limited_only);
     EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
@@ -497,40 +467,31 @@
     EXPECT_EQ(status, Dumpstate::RunStatus::INVALID_INPUT);
 }
 
-TEST_F(DumpOptionsTest, ValidateOptionsNeedOutfile1) {
-    options_.do_zip_file = true;
-    // Writing to socket = !writing to file.
-    options_.use_socket = true;
+TEST_F(DumpOptionsTest, ValidateOptionsSocketUsage1) {
+    options_.progress_updates_to_socket = true;
+    options_.stream_to_socket = true;
     EXPECT_FALSE(options_.ValidateOptions());
 
-    options_.use_socket = false;
+    options_.stream_to_socket = false;
     EXPECT_TRUE(options_.ValidateOptions());
 }
 
-TEST_F(DumpOptionsTest, ValidateOptionsNeedOutfile2) {
+TEST_F(DumpOptionsTest, ValidateOptionsSocketUsage2) {
     options_.do_progress_updates = true;
     // Writing to socket = !writing to file.
-    options_.use_socket = true;
+    options_.stream_to_socket = true;
     EXPECT_FALSE(options_.ValidateOptions());
 
-    options_.use_socket = false;
-    EXPECT_TRUE(options_.ValidateOptions());
-}
-
-TEST_F(DumpOptionsTest, ValidateOptionsNeedZipfile) {
-    options_.use_control_socket = true;
-    EXPECT_FALSE(options_.ValidateOptions());
-
-    options_.do_zip_file = true;
+    options_.stream_to_socket = false;
     EXPECT_TRUE(options_.ValidateOptions());
 }
 
 TEST_F(DumpOptionsTest, ValidateOptionsRemoteMode) {
+    options_.do_progress_updates = true;
     options_.is_remote_mode = true;
     EXPECT_FALSE(options_.ValidateOptions());
 
-    options_.do_zip_file = true;
-    options_.do_add_date = true;
+    options_.do_progress_updates = false;
     EXPECT_TRUE(options_.ValidateOptions());
 }
 
@@ -1017,23 +978,13 @@
     ds.listener_.clear();
 }
 
-TEST_F(DumpstateTest, DumpPool_withOutputToFileAndParallelRunEnabled_notNull) {
-    ds.options_->use_socket = false;
+TEST_F(DumpstateTest, DumpPool_withParallelRunEnabled_notNull) {
     SetParallelRun(true);
     EnableParallelRunIfNeeded();
-    EXPECT_TRUE(ds.options_->OutputToFile());
     EXPECT_TRUE(ds.zip_entry_tasks_);
     EXPECT_TRUE(ds.dump_pool_);
 }
 
-TEST_F(DumpstateTest, DumpPool_withNotOutputToFile_isNull) {
-    ds.options_->use_socket = true;
-    EnableParallelRunIfNeeded();
-    EXPECT_FALSE(ds.options_->OutputToFile());
-    EXPECT_FALSE(ds.zip_entry_tasks_);
-    EXPECT_FALSE(ds.dump_pool_);
-}
-
 TEST_F(DumpstateTest, DumpPool_withParallelRunDisabled_isNull) {
     SetParallelRun(false);
     EnableParallelRunIfNeeded();
@@ -1041,6 +992,69 @@
     EXPECT_FALSE(ds.dump_pool_);
 }
 
+class ZippedBugReportStreamTest : public DumpstateBaseTest {
+  public:
+    void SetUp() {
+        DumpstateBaseTest::SetUp();
+        ds_.options_.reset(new Dumpstate::DumpOptions());
+    }
+    void TearDown() {
+        CloseArchive(handle_);
+    }
+
+    // Set bugreport mode and options before here.
+    void GenerateBugreport() {
+        ds_.Initialize();
+        EXPECT_EQ(Dumpstate::RunStatus::OK, ds_.Run(/*calling_uid=*/-1, /*calling_package=*/""));
+    }
+
+    // Most bugreports droproot, ensure the file can be opened by shell to verify file content.
+    void CreateFd(const std::string& path, android::base::unique_fd* out_fd) {
+        out_fd->reset(TEMP_FAILURE_RETRY(open(path.c_str(),
+                                              O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+                                              S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)));
+        ASSERT_GE(out_fd->get(), 0) << "could not create FD for path " << path;
+    }
+
+    void VerifyEntry(const ZipArchiveHandle archive, const std::string_view entry_name,
+                     ZipEntry* data) {
+        int32_t e = FindEntry(archive, entry_name, data);
+        EXPECT_EQ(0, e) << ErrorCodeString(e) << " entry name: " << entry_name;
+    }
+
+    // While testing dumpstate in process, using STDOUT may get confused about
+    // the internal fd redirection. Redirect to a dedicate fd to save content.
+    void RedirectOutputToFd(android::base::unique_fd& ufd) {
+        ds_.open_socket_fn_ = [&](const char*) -> int { return ufd.release(); };
+    };
+
+    Dumpstate& ds_ = Dumpstate::GetInstance();
+    ZipArchiveHandle handle_;
+};
+
+// Generate a quick wifi report redirected to a file, open it and verify entry exist.
+TEST_F(ZippedBugReportStreamTest, StreamWifiReport) {
+    std::string out_path = kTestDataPath + "out.zip";
+    android::base::unique_fd out_fd;
+    CreateFd(out_path, &out_fd);
+    ds_.options_->wifi_only = true;
+    ds_.options_->stream_to_socket = true;
+    RedirectOutputToFd(out_fd);
+
+    GenerateBugreport();
+    OpenArchive(out_path.c_str(), &handle_);
+
+    ZipEntry entry;
+    VerifyEntry(handle_, "main_entry.txt", &entry);
+    std::string bugreport_txt_name;
+    bugreport_txt_name.resize(entry.uncompressed_length);
+    ExtractToMemory(handle_, &entry, reinterpret_cast<uint8_t*>(bugreport_txt_name.data()),
+                    entry.uncompressed_length);
+    EXPECT_THAT(bugreport_txt_name,
+                testing::ContainsRegex("(bugreport-.+-wifi(-[[:digit:]]+){6}\\.txt)"));
+    VerifyEntry(handle_, bugreport_txt_name, &entry);
+}
+
 class DumpstateServiceTest : public DumpstateBaseTest {
   public:
     DumpstateService dss;
diff --git a/cmds/installd/QuotaUtils.cpp b/cmds/installd/QuotaUtils.cpp
index e080291..6027139 100644
--- a/cmds/installd/QuotaUtils.cpp
+++ b/cmds/installd/QuotaUtils.cpp
@@ -35,7 +35,7 @@
 /* Map of all quota mounts from target to source */
 std::unordered_map<std::string, std::string> mQuotaReverseMounts;
 
-std::string& FindQuotaDeviceForUuid(const std::string& uuid) {
+std::string FindQuotaDeviceForUuid(const std::string& uuid) {
     std::lock_guard<std::recursive_mutex> lock(mMountsLock);
     auto path = create_data_path(uuid.empty() ? nullptr : uuid.c_str());
     return mQuotaReverseMounts[path];
diff --git a/data/etc/android.hardware.uwb.xml b/data/etc/android.hardware.uwb.xml
new file mode 100644
index 0000000..794ba27
--- /dev/null
+++ b/data/etc/android.hardware.uwb.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<!-- Adds the feature indicating support for the Ultra Wideband API -->
+<permissions>
+    <feature name="android.hardware.uwb" />
+</permissions>
diff --git a/include/ftl/ArrayTraits.h b/include/ftl/ArrayTraits.h
index 28f717a..ff685c5 100644
--- a/include/ftl/ArrayTraits.h
+++ b/include/ftl/ArrayTraits.h
@@ -19,6 +19,7 @@
 #include <algorithm>
 #include <iterator>
 #include <new>
+#include <type_traits>
 
 #define FTL_ARRAY_TRAIT(T, U) using U = typename ArrayTraits<T>::U
 
@@ -40,11 +41,16 @@
     using const_iterator = const_pointer;
     using const_reverse_iterator = std::reverse_iterator<const_iterator>;
 
-    // TODO: Replace with std::construct_at in C++20.
     template <typename... Args>
     static pointer construct_at(const_iterator it, Args&&... args) {
         void* const ptr = const_cast<void*>(static_cast<const void*>(it));
-        return new (ptr) value_type{std::forward<Args>(args)...};
+        if constexpr (std::is_constructible_v<value_type, Args...>) {
+            // TODO: Replace with std::construct_at in C++20.
+            return new (ptr) value_type(std::forward<Args>(args)...);
+        } else {
+            // Fall back to list initialization.
+            return new (ptr) value_type{std::forward<Args>(args)...};
+        }
     }
 };
 
diff --git a/include/ftl/InitializerList.h b/include/ftl/InitializerList.h
new file mode 100644
index 0000000..bb99280
--- /dev/null
+++ b/include/ftl/InitializerList.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <tuple>
+#include <utility>
+
+namespace android::ftl {
+
+// Compile-time counterpart of std::initializer_list<T> that stores per-element constructor
+// arguments with heterogeneous types. For a container with elements of type T, given Sizes
+// (S0, S1, ..., SN), N elements are initialized: the first element is initialized with the
+// first S0 arguments, the second element is initialized with the next S1 arguments, and so
+// on. The list of Types (T0, ..., TM) is flattened, so M is equal to the sum of the Sizes.
+//
+// The InitializerList is created using ftl::init::list, and is consumed by constructors of
+// containers. The function call operator is overloaded such that arguments are accumulated
+// in a tuple with each successive call. For instance, the following calls initialize three
+// strings using different constructors, i.e. string literal, default, and count/character:
+//
+//     ... = ftl::init::list<std::string>("abc")()(3u, '?');
+//
+// The following syntax is a shorthand for key-value pairs, where the first argument is the
+// key, and the rest construct the value. The types of the key and value are deduced if the
+// first pair contains exactly two arguments:
+//
+//     ... = ftl::init::map<int, std::string>(-1, "abc")(-2)(-3, 3u, '?');
+//
+//     ... = ftl::init::map(0, 'a')(1, 'b')(2, 'c');
+//
+// WARNING: The InitializerList returned by an ftl::init::list expression must be consumed
+// immediately, since temporary arguments are destroyed after the full expression. Storing
+// an InitializerList results in dangling references.
+//
+template <typename T, typename Sizes = std::index_sequence<>, typename... Types>
+struct InitializerList;
+
+template <typename T, size_t... Sizes, typename... Types>
+struct InitializerList<T, std::index_sequence<Sizes...>, Types...> {
+    // Creates a superset InitializerList by appending the number of arguments to Sizes, and
+    // expanding Types with forwarding references for each argument.
+    template <typename... Args>
+    [[nodiscard]] constexpr auto operator()(Args&&... args) && -> InitializerList<
+            T, std::index_sequence<Sizes..., sizeof...(Args)>, Types..., Args&&...> {
+        return {std::tuple_cat(std::move(tuple),
+                               std::forward_as_tuple(std::forward<Args>(args)...))};
+    }
+
+    // The temporary InitializerList returned by operator() is bound to an rvalue reference in
+    // container constructors, which extends the lifetime of any temporary arguments that this
+    // tuple refers to until the completion of the full expression containing the construction.
+    std::tuple<Types...> tuple;
+};
+
+template <typename K, typename V>
+struct KeyValue {};
+
+// Shorthand for key-value pairs that assigns the first argument to the key, and the rest to the
+// value. The specialization is on KeyValue rather than std::pair, so that ftl::init::list works
+// with the latter.
+template <typename K, typename V, size_t... Sizes, typename... Types>
+struct InitializerList<KeyValue<K, V>, std::index_sequence<Sizes...>, Types...> {
+    // Accumulate the three arguments to std::pair's piecewise constructor.
+    template <typename... Args>
+    [[nodiscard]] constexpr auto operator()(K&& k, Args&&... args) && -> InitializerList<
+            KeyValue<K, V>, std::index_sequence<Sizes..., 3>, Types..., std::piecewise_construct_t,
+            std::tuple<K&&>, std::tuple<Args&&...>> {
+        return {std::tuple_cat(std::move(tuple),
+                               std::forward_as_tuple(std::piecewise_construct,
+                                                     std::forward_as_tuple(std::forward<K>(k)),
+                                                     std::forward_as_tuple(
+                                                             std::forward<Args>(args)...)))};
+    }
+
+    std::tuple<Types...> tuple;
+};
+
+namespace init {
+
+template <typename T, typename... Args>
+[[nodiscard]] constexpr auto list(Args&&... args) {
+    return InitializerList<T>{}(std::forward<Args>(args)...);
+}
+
+template <typename K, typename V, typename... Args>
+[[nodiscard]] constexpr auto map(Args&&... args) {
+    return list<KeyValue<K, V>>(std::forward<Args>(args)...);
+}
+
+template <typename K, typename V>
+[[nodiscard]] constexpr auto map(K&& k, V&& v) {
+    return list<KeyValue<K, V>>(std::forward<K>(k), std::forward<V>(v));
+}
+
+} // namespace init
+} // namespace android::ftl
diff --git a/include/ftl/SmallMap.h b/include/ftl/SmallMap.h
new file mode 100644
index 0000000..87ae99c
--- /dev/null
+++ b/include/ftl/SmallMap.h
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <ftl/InitializerList.h>
+#include <ftl/SmallVector.h>
+
+#include <functional>
+#include <optional>
+#include <type_traits>
+#include <utility>
+
+namespace android::ftl {
+
+// Associative container with unique, unordered keys. Unlike std::unordered_map, key-value pairs are
+// stored in contiguous storage for cache efficiency. The map is allocated statically until its size
+// exceeds N, at which point mappings are relocated to dynamic memory.
+//
+// SmallMap<K, V, 0> unconditionally allocates on the heap.
+//
+// Example usage:
+//
+//    ftl::SmallMap<int, std::string, 3> map;
+//    assert(map.empty());
+//    assert(!map.dynamic());
+//
+//    map = ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
+//    assert(map.size() == 3u);
+//    assert(!map.dynamic());
+//
+//    assert(map.contains(123));
+//    assert(map.find(42, [](const std::string& s) { return s.size(); }) == 3u);
+//
+//    const auto opt = map.find(-1);
+//    assert(opt);
+//
+//    std::string& ref = *opt;
+//    assert(ref.empty());
+//    ref = "xyz";
+//
+//    assert(map == SmallMap(ftl::init::map(-1, "xyz")(42, "???")(123, "abc")));
+//
+template <typename K, typename V, size_t N>
+class SmallMap final {
+    using Map = SmallVector<std::pair<const K, V>, N>;
+
+public:
+    using key_type = K;
+    using mapped_type = V;
+
+    using value_type = typename Map::value_type;
+    using size_type = typename Map::size_type;
+    using difference_type = typename Map::difference_type;
+
+    using reference = typename Map::reference;
+    using iterator = typename Map::iterator;
+
+    using const_reference = typename Map::const_reference;
+    using const_iterator = typename Map::const_iterator;
+
+    // Creates an empty map.
+    SmallMap() = default;
+
+    // Constructs at most N key-value pairs in place by forwarding per-pair constructor arguments.
+    // The template arguments K, V, and N are inferred using the deduction guide defined below.
+    // The syntax for listing pairs is as follows:
+    //
+    //     ftl::SmallMap map = ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
+    //
+    //     static_assert(std::is_same_v<decltype(map), ftl::SmallMap<int, std::string, 3>>);
+    //     assert(map.size() == 3u);
+    //     assert(map.contains(-1) && map.find(-1)->get().empty());
+    //     assert(map.contains(42) && map.find(42)->get() == "???");
+    //     assert(map.contains(123) && map.find(123)->get() == "abc");
+    //
+    // The types of the key and value are deduced if the first pair contains exactly two arguments:
+    //
+    //     ftl::SmallMap map = ftl::init::map(0, 'a')(1, 'b')(2, 'c');
+    //     static_assert(std::is_same_v<decltype(map), ftl::SmallMap<int, char, 3>>);
+    //
+    template <typename U, size_t... Sizes, typename... Types>
+    SmallMap(InitializerList<U, std::index_sequence<Sizes...>, Types...>&& init)
+          : mMap(std::move(init)) {
+        // TODO: Enforce unique keys.
+    }
+
+    size_type max_size() const { return mMap.max_size(); }
+    size_type size() const { return mMap.size(); }
+    bool empty() const { return mMap.empty(); }
+
+    // Returns whether the map is backed by static or dynamic storage.
+    bool dynamic() const { return mMap.dynamic(); }
+
+    iterator begin() { return mMap.begin(); }
+    const_iterator begin() const { return cbegin(); }
+    const_iterator cbegin() const { return mMap.cbegin(); }
+
+    iterator end() { return mMap.end(); }
+    const_iterator end() const { return cend(); }
+    const_iterator cend() const { return mMap.cend(); }
+
+    // Returns whether a mapping exists for the given key.
+    bool contains(const key_type& key) const {
+        return find(key, [](const mapped_type&) {});
+    }
+
+    // Returns a reference to the value for the given key, or std::nullopt if the key was not found.
+    //
+    //     ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
+    //
+    //     const auto opt = map.find('c');
+    //     assert(opt == 'C');
+    //
+    //     char d = 'd';
+    //     const auto ref = map.find('d').value_or(std::ref(d));
+    //     ref.get() = 'D';
+    //     assert(d == 'D');
+    //
+    auto find(const key_type& key) const
+            -> std::optional<std::reference_wrapper<const mapped_type>> {
+        return find(key, [](const mapped_type& v) { return std::cref(v); });
+    }
+
+    auto find(const key_type& key) -> std::optional<std::reference_wrapper<mapped_type>> {
+        return find(key, [](mapped_type& v) { return std::ref(v); });
+    }
+
+    // Returns the result R of a unary operation F on (a constant or mutable reference to) the value
+    // for the given key, or std::nullopt if the key was not found. If F has a return type of void,
+    // then the Boolean result indicates whether the key was found.
+    //
+    //     ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
+    //
+    //     assert(map.find('c', [](char c) { return std::toupper(c); }) == 'Z');
+    //     assert(map.find('c', [](char& c) { c = std::toupper(c); }));
+    //
+    template <typename F, typename R = std::invoke_result_t<F, const mapped_type&>>
+    auto find(const key_type& key, F f) const
+            -> std::conditional_t<std::is_void_v<R>, bool, std::optional<R>> {
+        for (auto& [k, v] : *this) {
+            if (k == key) {
+                if constexpr (std::is_void_v<R>) {
+                    f(v);
+                    return true;
+                } else {
+                    return f(v);
+                }
+            }
+        }
+
+        return {};
+    }
+
+    template <typename F>
+    auto find(const key_type& key, F f) {
+        return std::as_const(*this).find(key, [&f](const mapped_type& v) {
+            return f(const_cast<mapped_type&>(v));
+        });
+    }
+
+private:
+    Map mMap;
+};
+
+// Deduction guide for in-place constructor.
+template <typename K, typename V, size_t... Sizes, typename... Types>
+SmallMap(InitializerList<KeyValue<K, V>, std::index_sequence<Sizes...>, Types...>&&)
+        -> SmallMap<K, V, sizeof...(Sizes)>;
+
+// Returns whether the key-value pairs of two maps are equal.
+template <typename K, typename V, size_t N, typename Q, typename W, size_t M>
+bool operator==(const SmallMap<K, V, N>& lhs, const SmallMap<Q, W, M>& rhs) {
+    if (lhs.size() != rhs.size()) return false;
+
+    for (const auto& [k, v] : lhs) {
+        const auto& lv = v;
+        if (!rhs.find(k, [&lv](const auto& rv) { return lv == rv; }).value_or(false)) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+// TODO: Remove in C++20.
+template <typename K, typename V, size_t N, typename Q, typename W, size_t M>
+inline bool operator!=(const SmallMap<K, V, N>& lhs, const SmallMap<Q, W, M>& rhs) {
+    return !(lhs == rhs);
+}
+
+} // namespace android::ftl
diff --git a/include/ftl/SmallVector.h b/include/ftl/SmallVector.h
index cecec7f..2f05a9b 100644
--- a/include/ftl/SmallVector.h
+++ b/include/ftl/SmallVector.h
@@ -64,6 +64,16 @@
 //     assert(vector == (ftl::SmallVector{'h', 'i', '\0'}));
 //     assert(!vector.dynamic());
 //
+//     ftl::SmallVector strings = ftl::init::list<std::string>("abc")
+//                                                            ("123456", 3u)
+//                                                            (3u, '?');
+//     assert(strings.size() == 3u);
+//     assert(!strings.dynamic());
+//
+//     assert(strings[0] == "abc");
+//     assert(strings[1] == "123");
+//     assert(strings[2] == "???");
+//
 template <typename T, size_t N>
 class SmallVector final : ArrayTraits<T>, ArrayComparators<SmallVector> {
     using Static = StaticVector<T, N>;
@@ -365,8 +375,9 @@
 SmallVector(T&&, Us&&...) -> SmallVector<V, 1 + sizeof...(Us)>;
 
 // Deduction guide for in-place constructor.
-template <typename T, typename... Us>
-SmallVector(std::in_place_type_t<T>, Us&&...) -> SmallVector<T, sizeof...(Us)>;
+template <typename T, size_t... Sizes, typename... Types>
+SmallVector(InitializerList<T, std::index_sequence<Sizes...>, Types...>&&)
+        -> SmallVector<T, sizeof...(Sizes)>;
 
 // Deduction guide for StaticVector conversion.
 template <typename T, size_t N>
diff --git a/include/ftl/StaticVector.h b/include/ftl/StaticVector.h
index 457095d..c132556 100644
--- a/include/ftl/StaticVector.h
+++ b/include/ftl/StaticVector.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <ftl/ArrayTraits.h>
+#include <ftl/InitializerList.h>
 
 #include <algorithm>
 #include <cassert>
@@ -32,7 +33,7 @@
 // Fixed-capacity, statically allocated counterpart of std::vector. Akin to std::array, StaticVector
 // allocates contiguous storage for N elements of type T at compile time, but stores at most (rather
 // than exactly) N elements. Unlike std::array, its default constructor does not require T to have a
-// default constructor, since elements are constructed in-place as the vector grows. Operations that
+// default constructor, since elements are constructed in place as the vector grows. Operations that
 // insert an element (emplace_back, push_back, etc.) fail when the vector is full. The API otherwise
 // adheres to standard containers, except the unstable_erase operation that does not preserve order,
 // and the replace operation that destructively emplaces.
@@ -63,6 +64,14 @@
 //     vector = ftl::StaticVector(array);
 //     assert(vector == (ftl::StaticVector{'h', 'i', '\0'}));
 //
+//     ftl::StaticVector strings = ftl::init::list<std::string>("abc")
+//                                                             ("123456", 3u)
+//                                                             (3u, '?');
+//     assert(strings.size() == 3u);
+//     assert(strings[0] == "abc");
+//     assert(strings[1] == "123");
+//     assert(strings[2] == "???");
+//
 template <typename T, size_t N>
 class StaticVector final : ArrayTraits<T>,
                            ArrayIterators<StaticVector<T, N>, T>,
@@ -153,15 +162,22 @@
         static_assert(sizeof...(elements) < N, "Too many elements");
     }
 
-    // Constructs at most N elements. The template arguments T and N are inferred using the
-    // deduction guide defined below. Element types must be convertible to the specified T:
+    // Constructs at most N elements in place by forwarding per-element constructor arguments. The
+    // template arguments T and N are inferred using the deduction guide defined below. The syntax
+    // for listing arguments is as follows:
     //
-    //     ftl::StaticVector vector(std::in_place_type<std::string>, "red", "velvet", "cake");
+    //     ftl::StaticVector vector = ftl::init::list<std::string>("abc")()(3u, '?');
+    //
     //     static_assert(std::is_same_v<decltype(vector), ftl::StaticVector<std::string, 3>>);
+    //     assert(vector.full());
+    //     assert(vector[0] == "abc");
+    //     assert(vector[1].empty());
+    //     assert(vector[2] == "???");
     //
-    template <typename... Es>
-    explicit StaticVector(std::in_place_type_t<T>, Es... elements)
-          : StaticVector(std::forward<Es>(elements)...) {}
+    template <typename U, size_t Size, size_t... Sizes, typename... Types>
+    StaticVector(InitializerList<U, std::index_sequence<Size, Sizes...>, Types...>&& init)
+          : StaticVector(std::index_sequence<0, 0, Size>{}, std::make_index_sequence<Size>{},
+                         std::index_sequence<Sizes...>{}, init.tuple) {}
 
     ~StaticVector() { std::destroy(begin(), end()); }
 
@@ -292,6 +308,32 @@
     template <size_t I>
     explicit StaticVector(std::index_sequence<I>) : mSize(I) {}
 
+    // Recursion for in-place constructor.
+    //
+    // Construct element I by extracting its arguments from the InitializerList tuple. ArgIndex
+    // is the position of its first argument in Args, and ArgCount is the number of arguments.
+    // The Indices sequence corresponds to [0, ArgCount).
+    //
+    // The Sizes sequence lists the argument counts for elements after I, so Size is the ArgCount
+    // for the next element. The recursion stops when Sizes is empty for the last element.
+    //
+    template <size_t I, size_t ArgIndex, size_t ArgCount, size_t... Indices, size_t Size,
+              size_t... Sizes, typename... Args>
+    StaticVector(std::index_sequence<I, ArgIndex, ArgCount>, std::index_sequence<Indices...>,
+                 std::index_sequence<Size, Sizes...>, std::tuple<Args...>& tuple)
+          : StaticVector(std::index_sequence<I + 1, ArgIndex + ArgCount, Size>{},
+                         std::make_index_sequence<Size>{}, std::index_sequence<Sizes...>{}, tuple) {
+        construct_at(begin() + I, std::move(std::get<ArgIndex + Indices>(tuple))...);
+    }
+
+    // Base case for in-place constructor.
+    template <size_t I, size_t ArgIndex, size_t ArgCount, size_t... Indices, typename... Args>
+    StaticVector(std::index_sequence<I, ArgIndex, ArgCount>, std::index_sequence<Indices...>,
+                 std::index_sequence<>, std::tuple<Args...>& tuple)
+          : mSize(I + 1) {
+        construct_at(begin() + I, std::move(std::get<ArgIndex + Indices>(tuple))...);
+    }
+
     size_type mSize = 0;
     std::aligned_storage_t<sizeof(value_type), alignof(value_type)> mData[N];
 };
@@ -306,8 +348,9 @@
 StaticVector(T&&, Us&&...) -> StaticVector<V, 1 + sizeof...(Us)>;
 
 // Deduction guide for in-place constructor.
-template <typename T, typename... Us>
-StaticVector(std::in_place_type_t<T>, Us&&...) -> StaticVector<T, sizeof...(Us)>;
+template <typename T, size_t... Sizes, typename... Types>
+StaticVector(InitializerList<T, std::index_sequence<Sizes...>, Types...>&&)
+        -> StaticVector<T, sizeof...(Sizes)>;
 
 template <typename T, size_t N>
 template <typename E>
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 015954d..90feedd 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -145,6 +145,7 @@
     cflags: [
         "-Wall",
         "-Wextra",
+        "-Wextra-semi",
         "-Werror",
         "-Wzero-as-null-pointer-constant",
         "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 8264154..bf4387a 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -137,8 +137,8 @@
 }
 
 BpBinder::BpBinder(int32_t handle, int32_t trackedUid)
-    : mHandle(handle)
-    , mStability(0)
+    : mStability(0)
+    , mHandle(handle)
     , mAlive(1)
     , mObitsSent(0)
     , mObituaries(nullptr)
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
index cf9bb46..08169f5 100644
--- a/libs/binder/IActivityManager.cpp
+++ b/libs/binder/IActivityManager.cpp
@@ -135,6 +135,6 @@
 
 // ------------------------------------------------------------------------------------
 
-IMPLEMENT_META_INTERFACE(ActivityManager, "android.app.IActivityManager");
+IMPLEMENT_META_INTERFACE(ActivityManager, "android.app.IActivityManager")
 
 } // namespace android
diff --git a/libs/binder/IAppOpsCallback.cpp b/libs/binder/IAppOpsCallback.cpp
index b9eb281..2b3f462 100644
--- a/libs/binder/IAppOpsCallback.cpp
+++ b/libs/binder/IAppOpsCallback.cpp
@@ -43,7 +43,7 @@
     }
 };
 
-IMPLEMENT_META_INTERFACE(AppOpsCallback, "com.android.internal.app.IAppOpsCallback");
+IMPLEMENT_META_INTERFACE(AppOpsCallback, "com.android.internal.app.IAppOpsCallback")
 
 // ----------------------------------------------------------------------
 
diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp
index ee0cd62..1897969 100644
--- a/libs/binder/IAppOpsService.cpp
+++ b/libs/binder/IAppOpsService.cpp
@@ -162,7 +162,7 @@
     }
 };
 
-IMPLEMENT_META_INTERFACE(AppOpsService, "com.android.internal.app.IAppOpsService");
+IMPLEMENT_META_INTERFACE(AppOpsService, "com.android.internal.app.IAppOpsService")
 
 // ----------------------------------------------------------------------
 
diff --git a/libs/binder/IBatteryStats.cpp b/libs/binder/IBatteryStats.cpp
index a47dbac..d0085df 100644
--- a/libs/binder/IBatteryStats.cpp
+++ b/libs/binder/IBatteryStats.cpp
@@ -130,7 +130,7 @@
 
 };
 
-IMPLEMENT_META_INTERFACE(BatteryStats, "com.android.internal.app.IBatteryStats");
+IMPLEMENT_META_INTERFACE(BatteryStats, "com.android.internal.app.IBatteryStats")
 
 // ----------------------------------------------------------------------
 
diff --git a/libs/binder/IMediaResourceMonitor.cpp b/libs/binder/IMediaResourceMonitor.cpp
index 5f3d670..f5fa817 100644
--- a/libs/binder/IMediaResourceMonitor.cpp
+++ b/libs/binder/IMediaResourceMonitor.cpp
@@ -38,7 +38,7 @@
     }
 };
 
-IMPLEMENT_META_INTERFACE(MediaResourceMonitor, "android.media.IMediaResourceMonitor");
+IMPLEMENT_META_INTERFACE(MediaResourceMonitor, "android.media.IMediaResourceMonitor")
 
 // ----------------------------------------------------------------------
 
diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp
index d8b44f9..cca8f81 100644
--- a/libs/binder/IMemory.cpp
+++ b/libs/binder/IMemory.cpp
@@ -223,7 +223,7 @@
 
 // ---------------------------------------------------------------------------
 
-IMPLEMENT_META_INTERFACE(Memory, "android.utils.IMemory");
+IMPLEMENT_META_INTERFACE(Memory, "android.utils.IMemory")
 
 BnMemory::BnMemory() {
 }
@@ -388,7 +388,7 @@
 
 // ---------------------------------------------------------------------------
 
-IMPLEMENT_META_INTERFACE(MemoryHeap, "android.utils.IMemoryHeap");
+IMPLEMENT_META_INTERFACE(MemoryHeap, "android.utils.IMemoryHeap")
 
 BnMemoryHeap::BnMemoryHeap() {
 }
diff --git a/libs/binder/IPermissionController.cpp b/libs/binder/IPermissionController.cpp
index d9bf3cc..f94f413 100644
--- a/libs/binder/IPermissionController.cpp
+++ b/libs/binder/IPermissionController.cpp
@@ -103,7 +103,7 @@
     }
 };
 
-IMPLEMENT_META_INTERFACE(PermissionController, "android.os.IPermissionController");
+IMPLEMENT_META_INTERFACE(PermissionController, "android.os.IPermissionController")
 
 // ----------------------------------------------------------------------
 
diff --git a/libs/binder/IProcessInfoService.cpp b/libs/binder/IProcessInfoService.cpp
index a38a27a..570edb9 100644
--- a/libs/binder/IProcessInfoService.cpp
+++ b/libs/binder/IProcessInfoService.cpp
@@ -84,7 +84,7 @@
     }
 };
 
-IMPLEMENT_META_INTERFACE(ProcessInfoService, "android.os.IProcessInfoService");
+IMPLEMENT_META_INTERFACE(ProcessInfoService, "android.os.IProcessInfoService")
 
 // ----------------------------------------------------------------------
 
diff --git a/libs/binder/IResultReceiver.cpp b/libs/binder/IResultReceiver.cpp
index 556288c..cd92217 100644
--- a/libs/binder/IResultReceiver.cpp
+++ b/libs/binder/IResultReceiver.cpp
@@ -42,7 +42,7 @@
     }
 };
 
-IMPLEMENT_META_INTERFACE(ResultReceiver, "com.android.internal.os.IResultReceiver");
+IMPLEMENT_META_INTERFACE(ResultReceiver, "com.android.internal.os.IResultReceiver")
 
 // ----------------------------------------------------------------------
 
diff --git a/libs/binder/IShellCallback.cpp b/libs/binder/IShellCallback.cpp
index a3e2b67..86dd5c4 100644
--- a/libs/binder/IShellCallback.cpp
+++ b/libs/binder/IShellCallback.cpp
@@ -52,7 +52,7 @@
     }
 };
 
-IMPLEMENT_META_INTERFACE(ShellCallback, "com.android.internal.os.IShellCallback");
+IMPLEMENT_META_INTERFACE(ShellCallback, "com.android.internal.os.IShellCallback")
 
 // ----------------------------------------------------------------------
 
diff --git a/libs/binder/IUidObserver.cpp b/libs/binder/IUidObserver.cpp
index 4714234..a1b08db 100644
--- a/libs/binder/IUidObserver.cpp
+++ b/libs/binder/IUidObserver.cpp
@@ -71,7 +71,7 @@
 
 // ----------------------------------------------------------------------
 
-IMPLEMENT_META_INTERFACE(UidObserver, "android.app.IUidObserver");
+IMPLEMENT_META_INTERFACE(UidObserver, "android.app.IUidObserver")
 
 // ----------------------------------------------------------------------
 
diff --git a/libs/binder/PermissionCache.cpp b/libs/binder/PermissionCache.cpp
index 75a6d22..6eae5ef 100644
--- a/libs/binder/PermissionCache.cpp
+++ b/libs/binder/PermissionCache.cpp
@@ -27,7 +27,7 @@
 
 // ----------------------------------------------------------------------------
 
-ANDROID_SINGLETON_STATIC_INSTANCE(PermissionCache) ;
+ANDROID_SINGLETON_STATIC_INSTANCE(PermissionCache)
 
 // ----------------------------------------------------------------------------
 
diff --git a/libs/binder/ProcessInfoService.cpp b/libs/binder/ProcessInfoService.cpp
index 00d6eef..f75141e 100644
--- a/libs/binder/ProcessInfoService.cpp
+++ b/libs/binder/ProcessInfoService.cpp
@@ -99,6 +99,6 @@
     }
 }
 
-ANDROID_SINGLETON_STATIC_INSTANCE(ProcessInfoService);
+ANDROID_SINGLETON_STATIC_INSTANCE(ProcessInfoService)
 
 } // namespace android
diff --git a/libs/binder/Static.cpp b/libs/binder/Static.cpp
index db0f1c7..565f2e2 100644
--- a/libs/binder/Static.cpp
+++ b/libs/binder/Static.cpp
@@ -33,7 +33,7 @@
 {
 public:
     LogTextOutput() : BufferedTextOutput(MULTITHREADED) { }
-    virtual ~LogTextOutput() { };
+    virtual ~LogTextOutput() { }
 
 protected:
     virtual status_t writeLines(const struct iovec& vec, size_t N)
@@ -49,7 +49,7 @@
 {
 public:
     explicit FdTextOutput(int fd) : BufferedTextOutput(MULTITHREADED), mFD(fd) { }
-    virtual ~FdTextOutput() { };
+    virtual ~FdTextOutput() { }
 
 protected:
     virtual status_t writeLines(const struct iovec& vec, size_t N)
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index dafc879..1cfb560 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -53,9 +53,7 @@
     },
     {
       "name": "libbinder_rs-internal_test"
-    }
-  ],
-  "postsubmit": [
+    },
     {
       "name": "rustBinderTest"
     }
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index 64d0657..2735315 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -28,7 +28,7 @@
 
 namespace internal {
 class Stability;
-};
+}
 
 using binder_proxy_limit_callback = void(*)(int);
 
@@ -109,19 +109,18 @@
         KeyedVector<const void*, entry_t> mObjects;
     };
 
-protected:
+private:
                         BpBinder(int32_t handle,int32_t trackedUid);
     virtual             ~BpBinder();
     virtual void        onFirstRef();
     virtual void        onLastStrongRef(const void* id);
     virtual bool        onIncStrongAttempted(uint32_t flags, const void* id);
 
-private:
-    const   int32_t             mHandle;
-
     friend ::android::internal::Stability;
             int32_t             mStability;
 
+    const   int32_t             mHandle;
+
     struct Obituary {
         wp<DeathRecipient> recipient;
         void* cookie;
diff --git a/libs/binder/include/binder/IShellCallback.h b/libs/binder/include/binder/IShellCallback.h
index 17e34db..6d3fe4a 100644
--- a/libs/binder/include/binder/IShellCallback.h
+++ b/libs/binder/include/binder/IShellCallback.h
@@ -25,7 +25,7 @@
 class IShellCallback : public IInterface
 {
 public:
-    DECLARE_META_INTERFACE(ShellCallback);
+    DECLARE_META_INTERFACE(ShellCallback)
 
     virtual int openFile(const String16& path, const String16& seLinuxContext,
             const String16& mode) = 0;
diff --git a/libs/binder/include/binder/IpPrefix.h b/libs/binder/include/binder/IpPrefix.h
index c1cd3c2..a8faa3f 100644
--- a/libs/binder/include/binder/IpPrefix.h
+++ b/libs/binder/include/binder/IpPrefix.h
@@ -73,8 +73,8 @@
 private:
     union InternalUnion {
         InternalUnion() = default;
-        explicit InternalUnion(const struct in6_addr &addr):mIn6Addr(addr) { };
-        explicit InternalUnion(const struct in_addr &addr):mInAddr(addr) { };
+        explicit InternalUnion(const struct in6_addr &addr):mIn6Addr(addr) { }
+        explicit InternalUnion(const struct in_addr &addr):mInAddr(addr) { }
         struct in6_addr mIn6Addr;
         struct in_addr mInAddr;
     } mUnion;
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 4b2d50d..cfe1f3a 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -131,7 +131,7 @@
     status_t            writeString8(const char* str, size_t len);
     status_t            writeString16(const String16& str);
     status_t            writeString16(const std::optional<String16>& str);
-    status_t            writeString16(const std::unique_ptr<String16>& str);
+    status_t            writeString16(const std::unique_ptr<String16>& str) __attribute__((deprecated("use std::optional version instead")));
     status_t            writeString16(const char16_t* str, size_t len);
     status_t            writeStrongBinder(const sp<IBinder>& val);
     status_t            writeInt32Array(size_t len, const int32_t *val);
@@ -143,48 +143,48 @@
     // Take a UTF8 encoded string, convert to UTF16, write it to the parcel.
     status_t            writeUtf8AsUtf16(const std::string& str);
     status_t            writeUtf8AsUtf16(const std::optional<std::string>& str);
-    status_t            writeUtf8AsUtf16(const std::unique_ptr<std::string>& str);
+    status_t            writeUtf8AsUtf16(const std::unique_ptr<std::string>& str) __attribute__((deprecated("use std::optional version instead")));
 
     status_t            writeByteVector(const std::optional<std::vector<int8_t>>& val);
-    status_t            writeByteVector(const std::unique_ptr<std::vector<int8_t>>& val);
+    status_t            writeByteVector(const std::unique_ptr<std::vector<int8_t>>& val) __attribute__((deprecated("use std::optional version instead")));
     status_t            writeByteVector(const std::vector<int8_t>& val);
     status_t            writeByteVector(const std::optional<std::vector<uint8_t>>& val);
-    status_t            writeByteVector(const std::unique_ptr<std::vector<uint8_t>>& val);
+    status_t            writeByteVector(const std::unique_ptr<std::vector<uint8_t>>& val) __attribute__((deprecated("use std::optional version instead")));
     status_t            writeByteVector(const std::vector<uint8_t>& val);
     status_t            writeInt32Vector(const std::optional<std::vector<int32_t>>& val);
-    status_t            writeInt32Vector(const std::unique_ptr<std::vector<int32_t>>& val);
+    status_t            writeInt32Vector(const std::unique_ptr<std::vector<int32_t>>& val) __attribute__((deprecated("use std::optional version instead")));
     status_t            writeInt32Vector(const std::vector<int32_t>& val);
     status_t            writeInt64Vector(const std::optional<std::vector<int64_t>>& val);
-    status_t            writeInt64Vector(const std::unique_ptr<std::vector<int64_t>>& val);
+    status_t            writeInt64Vector(const std::unique_ptr<std::vector<int64_t>>& val) __attribute__((deprecated("use std::optional version instead")));
     status_t            writeInt64Vector(const std::vector<int64_t>& val);
     status_t            writeUint64Vector(const std::optional<std::vector<uint64_t>>& val);
-    status_t            writeUint64Vector(const std::unique_ptr<std::vector<uint64_t>>& val);
+    status_t            writeUint64Vector(const std::unique_ptr<std::vector<uint64_t>>& val) __attribute__((deprecated("use std::optional version instead")));
     status_t            writeUint64Vector(const std::vector<uint64_t>& val);
     status_t            writeFloatVector(const std::optional<std::vector<float>>& val);
-    status_t            writeFloatVector(const std::unique_ptr<std::vector<float>>& val);
+    status_t            writeFloatVector(const std::unique_ptr<std::vector<float>>& val) __attribute__((deprecated("use std::optional version instead")));
     status_t            writeFloatVector(const std::vector<float>& val);
     status_t            writeDoubleVector(const std::optional<std::vector<double>>& val);
-    status_t            writeDoubleVector(const std::unique_ptr<std::vector<double>>& val);
+    status_t            writeDoubleVector(const std::unique_ptr<std::vector<double>>& val) __attribute__((deprecated("use std::optional version instead")));
     status_t            writeDoubleVector(const std::vector<double>& val);
     status_t            writeBoolVector(const std::optional<std::vector<bool>>& val);
-    status_t            writeBoolVector(const std::unique_ptr<std::vector<bool>>& val);
+    status_t            writeBoolVector(const std::unique_ptr<std::vector<bool>>& val) __attribute__((deprecated("use std::optional version instead")));
     status_t            writeBoolVector(const std::vector<bool>& val);
     status_t            writeCharVector(const std::optional<std::vector<char16_t>>& val);
-    status_t            writeCharVector(const std::unique_ptr<std::vector<char16_t>>& val);
+    status_t            writeCharVector(const std::unique_ptr<std::vector<char16_t>>& val) __attribute__((deprecated("use std::optional version instead")));
     status_t            writeCharVector(const std::vector<char16_t>& val);
     status_t            writeString16Vector(
                             const std::optional<std::vector<std::optional<String16>>>& val);
     status_t            writeString16Vector(
-                            const std::unique_ptr<std::vector<std::unique_ptr<String16>>>& val);
+                            const std::unique_ptr<std::vector<std::unique_ptr<String16>>>& val) __attribute__((deprecated("use std::optional version instead")));
     status_t            writeString16Vector(const std::vector<String16>& val);
     status_t            writeUtf8VectorAsUtf16Vector(
                             const std::optional<std::vector<std::optional<std::string>>>& val);
     status_t            writeUtf8VectorAsUtf16Vector(
-                            const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& val);
+                            const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& val) __attribute__((deprecated("use std::optional version instead")));
     status_t            writeUtf8VectorAsUtf16Vector(const std::vector<std::string>& val);
 
     status_t            writeStrongBinderVector(const std::optional<std::vector<sp<IBinder>>>& val);
-    status_t            writeStrongBinderVector(const std::unique_ptr<std::vector<sp<IBinder>>>& val);
+    status_t            writeStrongBinderVector(const std::unique_ptr<std::vector<sp<IBinder>>>& val) __attribute__((deprecated("use std::optional version instead")));
     status_t            writeStrongBinderVector(const std::vector<sp<IBinder>>& val);
 
     // Write an Enum vector with underlying type int8_t.
@@ -194,21 +194,21 @@
     template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
     status_t            writeEnumVector(const std::optional<std::vector<T>>& val);
     template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
-    status_t            writeEnumVector(const std::unique_ptr<std::vector<T>>& val);
+    status_t            writeEnumVector(const std::unique_ptr<std::vector<T>>& val) __attribute__((deprecated("use std::optional version instead")));
     // Write an Enum vector with underlying type != int8_t.
     template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
     status_t            writeEnumVector(const std::vector<T>& val);
     template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
     status_t            writeEnumVector(const std::optional<std::vector<T>>& val);
     template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
-    status_t            writeEnumVector(const std::unique_ptr<std::vector<T>>& val);
+    status_t            writeEnumVector(const std::unique_ptr<std::vector<T>>& val) __attribute__((deprecated("use std::optional version instead")));
 
     template<typename T>
     status_t            writeParcelableVector(const std::optional<std::vector<std::optional<T>>>& val);
     template<typename T>
-    status_t            writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val);
+    status_t            writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val) __attribute__((deprecated("use std::optional version instead")));
     template<typename T>
-    status_t            writeParcelableVector(const std::shared_ptr<std::vector<std::unique_ptr<T>>>& val);
+    status_t            writeParcelableVector(const std::shared_ptr<std::vector<std::unique_ptr<T>>>& val) __attribute__((deprecated("use std::optional version instead")));
     template<typename T>
     status_t            writeParcelableVector(const std::shared_ptr<std::vector<std::optional<T>>>& val);
     template<typename T>
@@ -217,7 +217,7 @@
     template<typename T>
     status_t            writeNullableParcelable(const std::optional<T>& parcelable);
     template<typename T>
-    status_t            writeNullableParcelable(const std::unique_ptr<T>& parcelable);
+    status_t            writeNullableParcelable(const std::unique_ptr<T>& parcelable) __attribute__((deprecated("use std::optional version instead")));
 
     status_t            writeParcelable(const Parcelable& parcelable);
 
@@ -232,7 +232,7 @@
     template<typename T>
     status_t            writeVectorSize(const std::optional<std::vector<T>>& val);
     template<typename T>
-    status_t            writeVectorSize(const std::unique_ptr<std::vector<T>>& val);
+    status_t            writeVectorSize(const std::unique_ptr<std::vector<T>>& val) __attribute__((deprecated("use std::optional version instead")));
 
     // Place a native_handle into the parcel (the native_handle's file-
     // descriptors are dup'ed, so it is safe to delete the native_handle
@@ -269,7 +269,7 @@
     status_t            writeUniqueFileDescriptorVector(
                             const std::optional<std::vector<base::unique_fd>>& val);
     status_t            writeUniqueFileDescriptorVector(
-                            const std::unique_ptr<std::vector<base::unique_fd>>& val);
+                            const std::unique_ptr<std::vector<base::unique_fd>>& val) __attribute__((deprecated("use std::optional version instead")));
     status_t            writeUniqueFileDescriptorVector(
                             const std::vector<base::unique_fd>& val);
 
@@ -317,7 +317,7 @@
     // Read a UTF16 encoded string, convert to UTF8
     status_t            readUtf8FromUtf16(std::string* str) const;
     status_t            readUtf8FromUtf16(std::optional<std::string>* str) const;
-    status_t            readUtf8FromUtf16(std::unique_ptr<std::string>* str) const;
+    status_t            readUtf8FromUtf16(std::unique_ptr<std::string>* str) const __attribute__((deprecated("use std::optional version instead")));
 
     const char*         readCString() const;
     String8             readString8() const;
@@ -326,7 +326,7 @@
     String16            readString16() const;
     status_t            readString16(String16* pArg) const;
     status_t            readString16(std::optional<String16>* pArg) const;
-    status_t            readString16(std::unique_ptr<String16>* pArg) const;
+    status_t            readString16(std::unique_ptr<String16>* pArg) const __attribute__((deprecated("use std::optional version instead")));
     const char16_t*     readString16Inplace(size_t* outLen) const;
     sp<IBinder>         readStrongBinder() const;
     status_t            readStrongBinder(sp<IBinder>* val) const;
@@ -337,14 +337,14 @@
     template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
     status_t            readEnumVector(std::vector<T>* val) const;
     template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
-    status_t            readEnumVector(std::unique_ptr<std::vector<T>>* val) const;
+    status_t            readEnumVector(std::unique_ptr<std::vector<T>>* val) const __attribute__((deprecated("use std::optional version instead")));
     template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
     status_t            readEnumVector(std::optional<std::vector<T>>* val) const;
     // Read an Enum vector with underlying type != int8_t.
     template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
     status_t            readEnumVector(std::vector<T>* val) const;
     template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
-    status_t            readEnumVector(std::unique_ptr<std::vector<T>>* val) const;
+    status_t            readEnumVector(std::unique_ptr<std::vector<T>>* val) const __attribute__((deprecated("use std::optional version instead")));
     template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
     status_t            readEnumVector(std::optional<std::vector<T>>* val) const;
 
@@ -353,7 +353,7 @@
                             std::optional<std::vector<std::optional<T>>>* val) const;
     template<typename T>
     status_t            readParcelableVector(
-                            std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const;
+                            std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const __attribute__((deprecated("use std::optional version instead")));
     template<typename T>
     status_t            readParcelableVector(std::vector<T>* val) const;
 
@@ -362,7 +362,7 @@
     template<typename T>
     status_t            readParcelable(std::optional<T>* parcelable) const;
     template<typename T>
-    status_t            readParcelable(std::unique_ptr<T>* parcelable) const;
+    status_t            readParcelable(std::unique_ptr<T>* parcelable) const __attribute__((deprecated("use std::optional version instead")));
 
     template<typename T>
     status_t            readStrongBinder(sp<T>* val) const;
@@ -371,45 +371,45 @@
     status_t            readNullableStrongBinder(sp<T>* val) const;
 
     status_t            readStrongBinderVector(std::optional<std::vector<sp<IBinder>>>* val) const;
-    status_t            readStrongBinderVector(std::unique_ptr<std::vector<sp<IBinder>>>* val) const;
+    status_t            readStrongBinderVector(std::unique_ptr<std::vector<sp<IBinder>>>* val) const __attribute__((deprecated("use std::optional version instead")));
     status_t            readStrongBinderVector(std::vector<sp<IBinder>>* val) const;
 
     status_t            readByteVector(std::optional<std::vector<int8_t>>* val) const;
-    status_t            readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const;
+    status_t            readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const __attribute__((deprecated("use std::optional version instead")));
     status_t            readByteVector(std::vector<int8_t>* val) const;
     status_t            readByteVector(std::optional<std::vector<uint8_t>>* val) const;
-    status_t            readByteVector(std::unique_ptr<std::vector<uint8_t>>* val) const;
+    status_t            readByteVector(std::unique_ptr<std::vector<uint8_t>>* val) const __attribute__((deprecated("use std::optional version instead")));
     status_t            readByteVector(std::vector<uint8_t>* val) const;
     status_t            readInt32Vector(std::optional<std::vector<int32_t>>* val) const;
-    status_t            readInt32Vector(std::unique_ptr<std::vector<int32_t>>* val) const;
+    status_t            readInt32Vector(std::unique_ptr<std::vector<int32_t>>* val) const __attribute__((deprecated("use std::optional version instead")));
     status_t            readInt32Vector(std::vector<int32_t>* val) const;
     status_t            readInt64Vector(std::optional<std::vector<int64_t>>* val) const;
-    status_t            readInt64Vector(std::unique_ptr<std::vector<int64_t>>* val) const;
+    status_t            readInt64Vector(std::unique_ptr<std::vector<int64_t>>* val) const __attribute__((deprecated("use std::optional version instead")));
     status_t            readInt64Vector(std::vector<int64_t>* val) const;
     status_t            readUint64Vector(std::optional<std::vector<uint64_t>>* val) const;
-    status_t            readUint64Vector(std::unique_ptr<std::vector<uint64_t>>* val) const;
+    status_t            readUint64Vector(std::unique_ptr<std::vector<uint64_t>>* val) const __attribute__((deprecated("use std::optional version instead")));
     status_t            readUint64Vector(std::vector<uint64_t>* val) const;
     status_t            readFloatVector(std::optional<std::vector<float>>* val) const;
-    status_t            readFloatVector(std::unique_ptr<std::vector<float>>* val) const;
+    status_t            readFloatVector(std::unique_ptr<std::vector<float>>* val) const __attribute__((deprecated("use std::optional version instead")));
     status_t            readFloatVector(std::vector<float>* val) const;
     status_t            readDoubleVector(std::optional<std::vector<double>>* val) const;
-    status_t            readDoubleVector(std::unique_ptr<std::vector<double>>* val) const;
+    status_t            readDoubleVector(std::unique_ptr<std::vector<double>>* val) const __attribute__((deprecated("use std::optional version instead")));
     status_t            readDoubleVector(std::vector<double>* val) const;
     status_t            readBoolVector(std::optional<std::vector<bool>>* val) const;
-    status_t            readBoolVector(std::unique_ptr<std::vector<bool>>* val) const;
+    status_t            readBoolVector(std::unique_ptr<std::vector<bool>>* val) const __attribute__((deprecated("use std::optional version instead")));
     status_t            readBoolVector(std::vector<bool>* val) const;
     status_t            readCharVector(std::optional<std::vector<char16_t>>* val) const;
-    status_t            readCharVector(std::unique_ptr<std::vector<char16_t>>* val) const;
+    status_t            readCharVector(std::unique_ptr<std::vector<char16_t>>* val) const __attribute__((deprecated("use std::optional version instead")));
     status_t            readCharVector(std::vector<char16_t>* val) const;
     status_t            readString16Vector(
                             std::optional<std::vector<std::optional<String16>>>* val) const;
     status_t            readString16Vector(
-                            std::unique_ptr<std::vector<std::unique_ptr<String16>>>* val) const;
+                            std::unique_ptr<std::vector<std::unique_ptr<String16>>>* val) const __attribute__((deprecated("use std::optional version instead")));
     status_t            readString16Vector(std::vector<String16>* val) const;
     status_t            readUtf8VectorFromUtf16Vector(
                             std::optional<std::vector<std::optional<std::string>>>* val) const;
     status_t            readUtf8VectorFromUtf16Vector(
-                            std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const;
+                            std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const __attribute__((deprecated("use std::optional version instead")));
     status_t            readUtf8VectorFromUtf16Vector(std::vector<std::string>* val) const;
 
     template<typename T>
@@ -423,7 +423,7 @@
     template<typename T>
     status_t            resizeOutVector(std::optional<std::vector<T>>* val) const;
     template<typename T>
-    status_t            resizeOutVector(std::unique_ptr<std::vector<T>>* val) const;
+    status_t            resizeOutVector(std::unique_ptr<std::vector<T>>* val) const __attribute__((deprecated("use std::optional version instead")));
     template<typename T>
     status_t            reserveOutVector(std::vector<T>* val, size_t* size) const;
     template<typename T>
@@ -431,7 +431,7 @@
                                          size_t* size) const;
     template<typename T>
     status_t            reserveOutVector(std::unique_ptr<std::vector<T>>* val,
-                                         size_t* size) const;
+                                         size_t* size) const __attribute__((deprecated("use std::optional version instead")));
 
     // Like Parcel.java's readExceptionCode().  Reads the first int32
     // off of a Parcel's header, returning 0 or the negative error
@@ -467,7 +467,7 @@
     status_t            readUniqueFileDescriptorVector(
                             std::optional<std::vector<base::unique_fd>>* val) const;
     status_t            readUniqueFileDescriptorVector(
-                            std::unique_ptr<std::vector<base::unique_fd>>* val) const;
+                            std::unique_ptr<std::vector<base::unique_fd>>* val) const __attribute__((deprecated("use std::optional version instead")));
     status_t            readUniqueFileDescriptorVector(
                             std::vector<base::unique_fd>* val) const;
 
@@ -561,7 +561,7 @@
                                                 status_t(Parcel::*read_func)(T*) const) const;
     template<typename T>
     status_t            readNullableTypedVector(std::unique_ptr<std::vector<T>>* val,
-                                                status_t(Parcel::*read_func)(T*) const) const;
+                                                status_t(Parcel::*read_func)(T*) const) const __attribute__((deprecated("use std::optional version instead")));
     template<typename T>
     status_t            readTypedVector(std::vector<T>* val,
                                         status_t(Parcel::*read_func)(T*) const) const;
@@ -573,13 +573,13 @@
                                                  status_t(Parcel::*write_func)(const T&));
     template<typename T>
     status_t            writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
-                                                 status_t(Parcel::*write_func)(const T&));
+                                                 status_t(Parcel::*write_func)(const T&)) __attribute__((deprecated("use std::optional version instead")));
     template<typename T>
     status_t            writeNullableTypedVector(const std::optional<std::vector<T>>& val,
                                                  status_t(Parcel::*write_func)(T));
     template<typename T>
     status_t            writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
-                                                 status_t(Parcel::*write_func)(T));
+                                                 status_t(Parcel::*write_func)(T)) __attribute__((deprecated("use std::optional version instead")));
     template<typename T>
     status_t            writeTypedVector(const std::vector<T>& val,
                                          status_t(Parcel::*write_func)(const T&));
diff --git a/libs/binder/include/binder/ParcelableHolder.h b/libs/binder/include/binder/ParcelableHolder.h
index 4ea3dd3..ce5027e 100644
--- a/libs/binder/include/binder/ParcelableHolder.h
+++ b/libs/binder/include/binder/ParcelableHolder.h
@@ -30,7 +30,7 @@
 class ParcelableHolder : public android::Parcelable {
 public:
     ParcelableHolder() = delete;
-    explicit ParcelableHolder(Stability stability) : mStability(stability){};
+    explicit ParcelableHolder(Stability stability) : mStability(stability){}
     virtual ~ParcelableHolder() = default;
     ParcelableHolder(const ParcelableHolder& other) {
         mParcelable = other.mParcelable;
@@ -40,7 +40,7 @@
             mParcelPtr->appendFrom(other.mParcelPtr.get(), 0, other.mParcelPtr->dataSize());
         }
         mStability = other.mStability;
-    };
+    }
 
     status_t writeToParcel(Parcel* parcel) const override;
     status_t readFromParcel(const Parcel* parcel) override;
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 5e2e1bd..350c658 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -307,7 +307,8 @@
     : onCreate(onCreate),
       onDestroy(onDestroy),
       onTransact(onTransact),
-      mInterfaceDescriptor(interfaceDescriptor) {}
+      mInterfaceDescriptor(interfaceDescriptor),
+      mWideInterfaceDescriptor(interfaceDescriptor) {}
 
 AIBinder_Class* AIBinder_Class_define(const char* interfaceDescriptor,
                                       AIBinder_Class_onCreate onCreate,
@@ -335,6 +336,12 @@
     clazz->handleShellCommand = handleShellCommand;
 }
 
+const char* AIBinder_Class_getDescriptor(const AIBinder_Class* clazz) {
+    CHECK(clazz != nullptr) << "getDescriptor requires non-null clazz";
+
+    return clazz->getInterfaceDescriptorUtf8();
+}
+
 void AIBinder_DeathRecipient::TransferDeathRecipient::binderDied(const wp<IBinder>& who) {
     CHECK(who == mWho);
 
diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h
index 6236e81..6824306 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -112,7 +112,8 @@
     AIBinder_Class(const char* interfaceDescriptor, AIBinder_Class_onCreate onCreate,
                    AIBinder_Class_onDestroy onDestroy, AIBinder_Class_onTransact onTransact);
 
-    const ::android::String16& getInterfaceDescriptor() const { return mInterfaceDescriptor; }
+    const ::android::String16& getInterfaceDescriptor() const { return mWideInterfaceDescriptor; }
+    const char* getInterfaceDescriptorUtf8() const { return mInterfaceDescriptor.c_str(); }
 
     // required to be non-null, implemented for every class
     const AIBinder_Class_onCreate onCreate = nullptr;
@@ -124,9 +125,11 @@
     AIBinder_handleShellCommand handleShellCommand = nullptr;
 
    private:
+    // Copy of the raw char string for when we don't have to return UTF-16
+    const std::string mInterfaceDescriptor;
     // This must be a String16 since BBinder virtual getInterfaceDescriptor returns a reference to
     // one.
-    const ::android::String16 mInterfaceDescriptor;
+    const ::android::String16 mWideInterfaceDescriptor;
 };
 
 // Ownership is like this (when linked to death):
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
index dfcf4dc..4858514 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
@@ -82,13 +82,13 @@
     }
 
     template <typename T>
-    bool setParcelable(T* p) {
-        if (p && this->mStability > T::_aidl_stability) {
+    bool setParcelable(const T& p) {
+        if (this->mStability > T::_aidl_stability) {
             return false;
         }
         AParcel_reset(mParcel.get());
         AParcel_writeString(mParcel.get(), T::descriptor, strlen(T::descriptor));
-        p->writeToParcel(mParcel.get());
+        p.writeToParcel(mParcel.get());
         return true;
     }
 
@@ -112,6 +112,8 @@
         return std::move(ret);
     }
 
+    void reset() { AParcel_reset(mParcel.get()); }
+
    private:
     mutable ndk::ScopedAParcel mParcel;
     parcelable_stability_t mStability;
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index ce3d1db..5e1ed46 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -642,6 +642,23 @@
 
 #endif  //__ANDROID_API__ >= 30
 
+#if __ANDROID_API__ >= 31
+
+/**
+ * Retrieve the class descriptor for the class.
+ *
+ * Available since API level 31.
+ *
+ * \param clazz the class to fetch the descriptor from
+ *
+ * \return the class descriptor string. This pointer will never be null; a
+ * descriptor is required to define a class. The pointer is owned by the class
+ * and will remain valid as long as the class does.
+ */
+const char* AIBinder_Class_getDescriptor(const AIBinder_Class* clazz) __INTRODUCED_IN(31);
+
+#endif  //__ANDROID_API__ >= 31
+
 __END_DECLS
 
 /** @} */
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 6962f86..e233ffd 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -121,6 +121,7 @@
     AServiceManager_registerLazyService; # llndk
     AServiceManager_waitForService; # apex llndk
 
+    AIBinder_Class_getDescriptor;
     AParcel_appendFrom;
     AParcel_create;
     AParcel_getDataSize;
diff --git a/libs/binder/ndk/tests/iface.cpp b/libs/binder/ndk/tests/iface.cpp
index a588985..2afe5d2 100644
--- a/libs/binder/ndk/tests/iface.cpp
+++ b/libs/binder/ndk/tests/iface.cpp
@@ -25,7 +25,7 @@
 
 const char* IFoo::kSomeInstanceName = "libbinder_ndk-test-IFoo";
 const char* IFoo::kInstanceNameToDieFor = "libbinder_ndk-test-IFoo-to-die";
-const char* kIFooDescriptor = "my-special-IFoo-class";
+const char* IFoo::kIFooDescriptor = "my-special-IFoo-class";
 
 struct IFoo_Class_Data {
     sp<IFoo> foo;
diff --git a/libs/binder/ndk/tests/include/iface/iface.h b/libs/binder/ndk/tests/include/iface/iface.h
index d9dd64b..7408d0c 100644
--- a/libs/binder/ndk/tests/include/iface/iface.h
+++ b/libs/binder/ndk/tests/include/iface/iface.h
@@ -27,6 +27,7 @@
    public:
     static const char* kSomeInstanceName;
     static const char* kInstanceNameToDieFor;
+    static const char* kIFooDescriptor;
 
     static AIBinder_Class* kClass;
 
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index f84d9d3..b7df115 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -39,6 +39,7 @@
 #include <condition_variable>
 #include <iostream>
 #include <mutex>
+#include "android/binder_ibinder.h"
 
 using namespace android;
 
@@ -543,6 +544,10 @@
     EXPECT_EQ("CMD", shellCmdToString(testService, {"C", "M", "D"}));
 }
 
+TEST(NdkBinder, GetClassInterfaceDescriptor) {
+    ASSERT_STREQ(IFoo::kIFooDescriptor, AIBinder_Class_getDescriptor(IFoo::kClass));
+}
+
 int main(int argc, char* argv[]) {
     ::testing::InitGoogleTest(&argc, argv);
 
diff --git a/libs/binder/parcel_fuzzer/binder.cpp b/libs/binder/parcel_fuzzer/binder.cpp
index 96cd215..624def1 100644
--- a/libs/binder/parcel_fuzzer/binder.cpp
+++ b/libs/binder/parcel_fuzzer/binder.cpp
@@ -86,6 +86,8 @@
     PARCEL_READ_WITH_STATUS(T, FUN), \
     PARCEL_READ_NO_STATUS(T, FUN)
 
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
 // clang-format off
 std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS {
     PARCEL_READ_NO_STATUS(size_t, dataSize),
@@ -296,3 +298,4 @@
     PARCEL_READ_WITH_STATUS(android::os::PersistableBundle, readParcelable),
 };
 // clang-format on
+#pragma clang diagnostic pop
diff --git a/libs/binder/tests/binderSafeInterfaceTest.cpp b/libs/binder/tests/binderSafeInterfaceTest.cpp
index 09f58cc..2f9d85e 100644
--- a/libs/binder/tests/binderSafeInterfaceTest.cpp
+++ b/libs/binder/tests/binderSafeInterfaceTest.cpp
@@ -184,7 +184,7 @@
 
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wexit-time-destructors"
-IMPLEMENT_META_INTERFACE(Callback, "android.gfx.tests.ICallback");
+IMPLEMENT_META_INTERFACE(Callback, "android.gfx.tests.ICallback")
 #pragma clang diagnostic pop
 
 class BnCallback : public SafeBnInterface<ICallback> {
@@ -373,7 +373,7 @@
 
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wexit-time-destructors"
-IMPLEMENT_META_INTERFACE(SafeInterfaceTest, "android.gfx.tests.ISafeInterfaceTest");
+IMPLEMENT_META_INTERFACE(SafeInterfaceTest, "android.gfx.tests.ISafeInterfaceTest")
 
 static sp<IBinder::DeathRecipient> getDeathRecipient() {
     static sp<IBinder::DeathRecipient> recipient = new ExitOnDeath;
diff --git a/libs/binder/tests/schd-dbg.cpp b/libs/binder/tests/schd-dbg.cpp
index ab4c56a..56d958c 100644
--- a/libs/binder/tests/schd-dbg.cpp
+++ b/libs/binder/tests/schd-dbg.cpp
@@ -245,7 +245,7 @@
     double best = (double)m_best / 1.0E6;
     double worst = (double)m_worst / 1.0E6;
     double average = (double)m_total_time / m_transactions / 1.0E6;
-    // FIXME: libjson?
+    // TODO: libjson?
     int W = DUMP_PRESICION + 2;
     cout << setprecision(DUMP_PRESICION) << "{ \"avg\":" << setw(W) << left
          << average << ",\"wst\":" << setw(W) << left << worst
@@ -376,7 +376,7 @@
   if (is_client(num)) {
     int no_trans = iterations * 2;
     double sync_ratio = (1.0 - (double)no_sync / no_trans);
-    // FIXME: libjson?
+    // TODO: libjson?
     cout << "\"P" << (num - server_count) << "\":{\"SYNC\":\""
          << ((sync_ratio > GOOD_SYNC_MIN) ? "GOOD" : "POOR") << "\","
          << "\"S\":" << (no_trans - no_sync) << ",\"I\":" << no_trans << ","
@@ -466,7 +466,7 @@
   }
   vector<Pipe> pipes;
   thread_dump("main");
-  // FIXME: libjson?
+  // TODO: libjson?
   cout << "{" << endl;
   cout << "\"cfg\":{\"pair\":" << (no_process / 2)
        << ",\"iterations\":" << iterations << ",\"deadline_us\":" << deadline_us
@@ -495,7 +495,7 @@
     // detected in the child process
     no_inherent += status;
   }
-  // FIXME: libjson?
+  // TODO: libjson?
   cout << "\"inheritance\": " << (no_inherent == 0 ? "\"PASS\"" : "\"FAIL\"")
        << endl;
   cout << "}" << endl;
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index fb32382..eb8e57a 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -5,6 +5,7 @@
         address: true,
     },
     srcs: [
+        "SmallMap_test.cpp",
         "SmallVector_test.cpp",
         "StaticVector_test.cpp",
     ],
diff --git a/libs/ftl/SmallMap_test.cpp b/libs/ftl/SmallMap_test.cpp
new file mode 100644
index 0000000..fa00c06
--- /dev/null
+++ b/libs/ftl/SmallMap_test.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ftl/SmallMap.h>
+#include <gtest/gtest.h>
+
+#include <cctype>
+
+namespace android::test {
+
+using ftl::SmallMap;
+
+// Keep in sync with example usage in header file.
+TEST(SmallMap, Example) {
+    ftl::SmallMap<int, std::string, 3> map;
+    EXPECT_TRUE(map.empty());
+    EXPECT_FALSE(map.dynamic());
+
+    map = ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
+    EXPECT_EQ(map.size(), 3u);
+    EXPECT_FALSE(map.dynamic());
+
+    EXPECT_TRUE(map.contains(123));
+
+    EXPECT_EQ(map.find(42, [](const std::string& s) { return s.size(); }), 3u);
+
+    const auto opt = map.find(-1);
+    ASSERT_TRUE(opt);
+
+    std::string& ref = *opt;
+    EXPECT_TRUE(ref.empty());
+    ref = "xyz";
+
+    EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "xyz")(42, "???")(123, "abc")));
+}
+
+TEST(SmallMap, Construct) {
+    {
+        // Default constructor.
+        SmallMap<int, std::string, 2> map;
+
+        EXPECT_TRUE(map.empty());
+        EXPECT_FALSE(map.dynamic());
+    }
+    {
+        // In-place constructor with same types.
+        SmallMap<int, std::string, 5> map =
+                ftl::init::map<int, std::string>(123, "abc")(456, "def")(789, "ghi");
+
+        EXPECT_EQ(map.size(), 3u);
+        EXPECT_EQ(map.max_size(), 5u);
+        EXPECT_FALSE(map.dynamic());
+
+        EXPECT_EQ(map, SmallMap(ftl::init::map(123, "abc")(456, "def")(789, "ghi")));
+    }
+    {
+        // In-place constructor with different types.
+        SmallMap<int, std::string, 5> map =
+                ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
+
+        EXPECT_EQ(map.size(), 3u);
+        EXPECT_EQ(map.max_size(), 5u);
+        EXPECT_FALSE(map.dynamic());
+
+        EXPECT_EQ(map, SmallMap(ftl::init::map(42, "???")(123, "abc")(-1, "\0\0\0")));
+    }
+    {
+        // In-place constructor with implicit size.
+        SmallMap map = ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
+
+        static_assert(std::is_same_v<decltype(map), SmallMap<int, std::string, 3>>);
+        EXPECT_EQ(map.size(), 3u);
+        EXPECT_EQ(map.max_size(), 3u);
+        EXPECT_FALSE(map.dynamic());
+
+        EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "\0\0\0")(42, "???")(123, "abc")));
+    }
+}
+
+TEST(SmallMap, Find) {
+    {
+        // Constant reference.
+        const ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
+
+        const auto opt = map.find('b');
+        EXPECT_EQ(opt, 'B');
+
+        const char d = 'D';
+        const auto ref = map.find('d').value_or(std::cref(d));
+        EXPECT_EQ(ref.get(), 'D');
+    }
+    {
+        // Mutable reference.
+        ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
+
+        const auto opt = map.find('c');
+        EXPECT_EQ(opt, 'C');
+
+        char d = 'd';
+        const auto ref = map.find('d').value_or(std::ref(d));
+        ref.get() = 'D';
+        EXPECT_EQ(d, 'D');
+    }
+    {
+        // Constant unary operation.
+        const ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
+        EXPECT_EQ(map.find('c', [](char c) { return std::toupper(c); }), 'Z');
+    }
+    {
+        // Mutable unary operation.
+        ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
+        EXPECT_TRUE(map.find('c', [](char& c) { c = std::toupper(c); }));
+
+        EXPECT_EQ(map, SmallMap(ftl::init::map('c', 'Z')('b', 'y')('a', 'x')));
+    }
+}
+
+} // namespace android::test
diff --git a/libs/ftl/SmallVector_test.cpp b/libs/ftl/SmallVector_test.cpp
index c41e622..d0c2858 100644
--- a/libs/ftl/SmallVector_test.cpp
+++ b/libs/ftl/SmallVector_test.cpp
@@ -52,6 +52,14 @@
     vector = ftl::SmallVector(array);
     EXPECT_EQ(vector, (ftl::SmallVector{'h', 'i', '\0'}));
     EXPECT_FALSE(vector.dynamic());
+
+    ftl::SmallVector strings = ftl::init::list<std::string>("abc")("123456", 3u)(3u, '?');
+    ASSERT_EQ(strings.size(), 3u);
+    EXPECT_FALSE(strings.dynamic());
+
+    EXPECT_EQ(strings[0], "abc");
+    EXPECT_EQ(strings[1], "123");
+    EXPECT_EQ(strings[2], "???");
 }
 
 TEST(SmallVector, Construct) {
@@ -99,7 +107,8 @@
     }
     {
         // In-place constructor with same types.
-        SmallVector vector(std::in_place_type<std::string>, "red", "velvet", "cake");
+        SmallVector vector =
+                ftl::init::list<std::string>("redolent", 3u)("velveteen", 6u)("cakewalk", 4u);
 
         static_assert(std::is_same_v<decltype(vector), SmallVector<std::string, 3>>);
         EXPECT_EQ(vector, (SmallVector{"red"s, "velvet"s, "cake"s}));
@@ -110,9 +119,10 @@
         const auto copy = "red"s;
         auto move = "velvet"s;
         std::initializer_list<char> list = {'c', 'a', 'k', 'e'};
-        SmallVector vector(std::in_place_type<std::string>, copy.c_str(), std::move(move), list);
+        SmallVector vector = ftl::init::list<std::string>(copy.c_str())(std::move(move))(list);
 
         static_assert(std::is_same_v<decltype(vector), SmallVector<std::string, 3>>);
+        EXPECT_TRUE(move.empty());
         EXPECT_EQ(vector, (SmallVector{"red"s, "velvet"s, "cake"s}));
         EXPECT_FALSE(vector.dynamic());
     }
@@ -206,9 +216,8 @@
 }
 
 TEST(SmallVector, MovableElement) {
-    // Construct std::string elements in-place from C-style strings. Without std::in_place_type, the
-    // element type would be deduced from the first element, i.e. const char*.
-    SmallVector strings(std::in_place_type<std::string>, "", "", "", "cake", "velvet", "red", "");
+    // Construct std::string elements in place from per-element arguments.
+    SmallVector strings = ftl::init::list<std::string>()()()("cake")("velvet")("red")();
     strings.pop_back();
 
     EXPECT_EQ(strings.max_size(), 7u);
@@ -221,6 +230,7 @@
         ASSERT_FALSE(it == strings.end());
         EXPECT_EQ(*it, "cake");
 
+        // Construct std::string from first 4 characters of string literal.
         strings.unstable_erase(it);
         EXPECT_EQ(strings.emplace_back("cakewalk", 4u), "cake"s);
     }
@@ -260,7 +270,7 @@
         bool operator==(const Word& other) const { return other.str == str; }
     };
 
-    SmallVector words(std::in_place_type<Word>, "colored", "velour");
+    SmallVector words = ftl::init::list<Word>("colored")("velour");
 
     // The replaced element can be referenced by the replacement.
     {
@@ -301,7 +311,7 @@
 }
 
 TEST(SmallVector, Sort) {
-    SmallVector strings(std::in_place_type<std::string>, "pie", "quince", "tart", "red", "velvet");
+    SmallVector strings = ftl::init::list<std::string>("pie")("quince")("tart")("red")("velvet");
     strings.push_back("cake"s);
 
     auto sorted = std::move(strings);
diff --git a/libs/ftl/StaticVector_test.cpp b/libs/ftl/StaticVector_test.cpp
index dd5ce35..db42d23 100644
--- a/libs/ftl/StaticVector_test.cpp
+++ b/libs/ftl/StaticVector_test.cpp
@@ -51,6 +51,13 @@
     const char array[] = "hi";
     vector = ftl::StaticVector(array);
     EXPECT_EQ(vector, (ftl::StaticVector{'h', 'i', '\0'}));
+
+    ftl::StaticVector strings = ftl::init::list<std::string>("abc")("123456", 3u)(3u, '?');
+    ASSERT_EQ(strings.size(), 3u);
+
+    EXPECT_EQ(strings[0], "abc");
+    EXPECT_EQ(strings[1], "123");
+    EXPECT_EQ(strings[2], "???");
 }
 
 TEST(StaticVector, Construct) {
@@ -91,7 +98,8 @@
     }
     {
         // In-place constructor with same types.
-        StaticVector vector(std::in_place_type<std::string>, "red", "velvet", "cake");
+        StaticVector vector =
+                ftl::init::list<std::string>("redolent", 3u)("velveteen", 6u)("cakewalk", 4u);
 
         static_assert(std::is_same_v<decltype(vector), StaticVector<std::string, 3>>);
         EXPECT_EQ(vector, (StaticVector{"red"s, "velvet"s, "cake"s}));
@@ -101,9 +109,10 @@
         const auto copy = "red"s;
         auto move = "velvet"s;
         std::initializer_list<char> list = {'c', 'a', 'k', 'e'};
-        StaticVector vector(std::in_place_type<std::string>, copy.c_str(), std::move(move), list);
+        StaticVector vector = ftl::init::list<std::string>(copy.c_str())(std::move(move))(list);
 
         static_assert(std::is_same_v<decltype(vector), StaticVector<std::string, 3>>);
+        EXPECT_TRUE(move.empty());
         EXPECT_EQ(vector, (StaticVector{"red"s, "velvet"s, "cake"s}));
     }
     {
@@ -204,9 +213,8 @@
 }
 
 TEST(StaticVector, MovableElement) {
-    // Construct std::string elements in-place from C-style strings. Without std::in_place_type, the
-    // element type would be deduced from the first element, i.e. const char*.
-    StaticVector strings(std::in_place_type<std::string>, "", "", "", "cake", "velvet", "red", "");
+    // Construct std::string elements in place from per-element arguments.
+    StaticVector strings = ftl::init::list<std::string>()()()("cake")("velvet")("red")();
     strings.pop_back();
 
     EXPECT_EQ(strings.max_size(), 7u);
@@ -221,7 +229,7 @@
 
         strings.unstable_erase(it);
 
-        // Construct std::string from first 4 characters of C-style string.
+        // Construct std::string from first 4 characters of string literal.
         it = strings.emplace_back("cakewalk", 4u);
         ASSERT_NE(it, strings.end());
         EXPECT_EQ(*it, "cake"s);
@@ -250,7 +258,7 @@
         const std::string str;
     };
 
-    StaticVector words(std::in_place_type<Word>, "red", "velour", "cake");
+    StaticVector words = ftl::init::list<Word>("red")("velour")("cake");
 
     // The replaced element can be referenced by the replacement.
     const auto it = words.begin() + 1;
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index a0e9cbf..af9ef06 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -237,6 +237,10 @@
         "libnativebase_headers",
     ],
 
+    include_dirs: [
+        "frameworks/native/include",
+    ],
+
     export_shared_lib_headers: [
         "libEGL",
         "libnativewindow",
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index abfee61..c6c9a8f 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -33,10 +33,10 @@
 // using just a few large reads.
 static const size_t EVENT_BUFFER_SIZE = 100;
 
-DisplayEventDispatcher::DisplayEventDispatcher(const sp<Looper>& looper,
-                                               ISurfaceComposer::VsyncSource vsyncSource,
-                                               ISurfaceComposer::ConfigChanged configChanged)
-      : mLooper(looper), mReceiver(vsyncSource, configChanged), mWaitingForVsync(false) {
+DisplayEventDispatcher::DisplayEventDispatcher(
+        const sp<Looper>& looper, ISurfaceComposer::VsyncSource vsyncSource,
+        ISurfaceComposer::EventRegistrationFlags eventRegistration)
+      : mLooper(looper), mReceiver(vsyncSource, eventRegistration), mWaitingForVsync(false) {
     ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this);
 }
 
@@ -139,6 +139,7 @@
     ssize_t n;
     while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
         ALOGV("dispatcher %p ~ Read %d events.", this, int(n));
+        mFrameRateOverrides.reserve(n);
         for (ssize_t i = 0; i < n; i++) {
             const DisplayEventReceiver::Event& ev = buf[i];
             switch (ev.header.type) {
@@ -162,6 +163,13 @@
                 case DisplayEventReceiver::DISPLAY_EVENT_NULL:
                     dispatchNullEvent(ev.header.timestamp, ev.header.displayId);
                     break;
+                case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE:
+                    mFrameRateOverrides.emplace_back(ev.frameRateOverride);
+                    break;
+                case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH:
+                    dispatchFrameRateOverrides(ev.header.timestamp, ev.header.displayId,
+                                               std::move(mFrameRateOverrides));
+                    break;
                 default:
                     ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type);
                     break;
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp
index f2b0962..03b33c7 100644
--- a/libs/gui/DisplayEventReceiver.cpp
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -32,11 +32,12 @@
 
 // ---------------------------------------------------------------------------
 
-DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource,
-                                           ISurfaceComposer::ConfigChanged configChanged) {
+DisplayEventReceiver::DisplayEventReceiver(
+        ISurfaceComposer::VsyncSource vsyncSource,
+        ISurfaceComposer::EventRegistrationFlags eventRegistration) {
     sp<ISurfaceComposer> sf(ComposerService::getComposerService());
     if (sf != nullptr) {
-        mEventConnection = sf->createDisplayEventConnection(vsyncSource, configChanged);
+        mEventConnection = sf->createDisplayEventConnection(vsyncSource, eventRegistration);
         if (mEventConnection != nullptr) {
             mDataChannel = std::make_unique<gui::BitTube>();
             mEventConnection->stealReceiveChannel(mDataChannel.get());
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index a9fe5bf..e46a415 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -223,8 +223,8 @@
         return NO_ERROR;
     }
 
-    virtual sp<IDisplayEventConnection> createDisplayEventConnection(VsyncSource vsyncSource,
-                                                                     ConfigChanged configChanged) {
+    virtual sp<IDisplayEventConnection> createDisplayEventConnection(
+            VsyncSource vsyncSource, EventRegistrationFlags eventRegistration) {
         Parcel data, reply;
         sp<IDisplayEventConnection> result;
         int err = data.writeInterfaceToken(
@@ -233,7 +233,7 @@
             return result;
         }
         data.writeInt32(static_cast<int32_t>(vsyncSource));
-        data.writeInt32(static_cast<int32_t>(configChanged));
+        data.writeUint32(eventRegistration.get());
         err = remote()->transact(
                 BnSurfaceComposer::CREATE_DISPLAY_EVENT_CONNECTION,
                 data, &reply);
@@ -1373,10 +1373,11 @@
         case CREATE_DISPLAY_EVENT_CONNECTION: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             auto vsyncSource = static_cast<ISurfaceComposer::VsyncSource>(data.readInt32());
-            auto configChanged = static_cast<ISurfaceComposer::ConfigChanged>(data.readInt32());
+            EventRegistrationFlags eventRegistration =
+                    static_cast<EventRegistration>(data.readUint32());
 
             sp<IDisplayEventConnection> connection(
-                    createDisplayEventConnection(vsyncSource, configChanged));
+                    createDisplayEventConnection(vsyncSource, eventRegistration));
             reply->writeStrongBinder(IInterface::asBinder(connection));
             return NO_ERROR;
         }
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index c65618b..5587acf 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -19,6 +19,7 @@
 #include <utils/Looper.h>
 
 namespace android {
+using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
 
 struct VsyncEventData {
     // The Vsync Id corresponsing to this vsync event. This will be used to
@@ -36,8 +37,7 @@
     explicit DisplayEventDispatcher(
             const sp<Looper>& looper,
             ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp,
-            ISurfaceComposer::ConfigChanged configChanged =
-                    ISurfaceComposer::eConfigChangedSuppress);
+            ISurfaceComposer::EventRegistrationFlags eventRegistration = {});
 
     status_t initialize();
     void dispose();
@@ -54,6 +54,8 @@
     DisplayEventReceiver mReceiver;
     bool mWaitingForVsync;
 
+    std::vector<FrameRateOverride> mFrameRateOverrides;
+
     virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
                                VsyncEventData vsyncEventData) = 0;
     virtual void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId,
@@ -64,6 +66,9 @@
     // can be properly poked.
     virtual void dispatchNullEvent(nsecs_t timestamp, PhysicalDisplayId displayId) = 0;
 
+    virtual void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId,
+                                            std::vector<FrameRateOverride> overrides) = 0;
+
     bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId,
                               uint32_t* outCount, VsyncEventData* outVsyncEventData);
 };
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index df3118f..3191fc9 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -54,6 +54,8 @@
         DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'),
         DISPLAY_EVENT_CONFIG_CHANGED = fourcc('c', 'o', 'n', 'f'),
         DISPLAY_EVENT_NULL = fourcc('n', 'u', 'l', 'l'),
+        DISPLAY_EVENT_FRAME_RATE_OVERRIDE = fourcc('r', 'a', 't', 'e'),
+        DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH = fourcc('f', 'l', 's', 'h'),
     };
 
     struct Event {
@@ -85,11 +87,17 @@
             nsecs_t vsyncPeriod __attribute__((aligned(8)));
         };
 
+        struct FrameRateOverride {
+            uid_t uid __attribute__((aligned(8)));
+            float frameRateHz __attribute__((aligned(8)));
+        };
+
         Header header;
         union {
             VSync vsync;
             Hotplug hotplug;
             Config config;
+            FrameRateOverride frameRateOverride;
         };
     };
 
@@ -98,13 +106,12 @@
      * DisplayEventReceiver creates and registers an event connection with
      * SurfaceFlinger. VSync events are disabled by default. Call setVSyncRate
      * or requestNextVsync to receive them.
-     * To receive Config Changed events specify this in the constructor.
-     * Other events start being delivered immediately.
+     * To receive ConfigChanged and/or FrameRateOverrides events specify this in
+     * the constructor. Other events start being delivered immediately.
      */
     explicit DisplayEventReceiver(
             ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp,
-            ISurfaceComposer::ConfigChanged configChanged =
-                    ISurfaceComposer::eConfigChangedSuppress);
+            ISurfaceComposer::EventRegistrationFlags eventRegistration = {});
 
     /*
      * ~DisplayEventReceiver severs the connection with SurfaceFlinger, new events
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 9e96b79..86e3a25 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -26,6 +26,8 @@
 #include <gui/IScreenCaptureListener.h>
 #include <gui/ITransactionCompletedListener.h>
 
+#include <input/Flags.h>
+
 #include <math/vec4.h>
 
 #include <ui/ConfigStoreTypes.h>
@@ -107,7 +109,12 @@
         eVsyncSourceSurfaceFlinger = 1
     };
 
-    enum ConfigChanged { eConfigChangedSuppress = 0, eConfigChangedDispatch = 1 };
+    enum class EventRegistration {
+        configChanged = 1 << 0,
+        frameRateOverride = 1 << 1,
+    };
+
+    using EventRegistrationFlags = Flags<EventRegistration>;
 
     // Needs to be in sync with android.graphics.FrameInfo.INVALID_VSYNC_ID in java
     static constexpr int64_t INVALID_VSYNC_ID = -1;
@@ -120,7 +127,7 @@
     /* return an IDisplayEventConnection */
     virtual sp<IDisplayEventConnection> createDisplayEventConnection(
             VsyncSource vsyncSource = eVsyncSourceApp,
-            ConfigChanged configChanged = eConfigChangedSuppress) = 0;
+            EventRegistrationFlags eventRegistration = {}) = 0;
 
     /* create a virtual display
      * requires ACCESS_SURFACE_FLINGER permission.
diff --git a/libs/gui/tests/DisplayEventStructLayout_test.cpp b/libs/gui/tests/DisplayEventStructLayout_test.cpp
index 7210910..c5093e2 100644
--- a/libs/gui/tests/DisplayEventStructLayout_test.cpp
+++ b/libs/gui/tests/DisplayEventStructLayout_test.cpp
@@ -40,6 +40,9 @@
 
     CHECK_OFFSET(DisplayEventReceiver::Event::Config, configId, 0);
     CHECK_OFFSET(DisplayEventReceiver::Event::Config, vsyncPeriod, 8);
+
+    CHECK_OFFSET(DisplayEventReceiver::Event::FrameRateOverride, uid, 0);
+    CHECK_OFFSET(DisplayEventReceiver::Event::FrameRateOverride, frameRateHz, 8);
 }
 
 } // namespace android::test
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 2392ae5..ce3afa2 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -687,7 +687,7 @@
 
     sp<ISurfaceComposerClient> createConnection() override { return nullptr; }
     sp<IDisplayEventConnection> createDisplayEventConnection(
-            ISurfaceComposer::VsyncSource, ISurfaceComposer::ConfigChanged) override {
+            ISurfaceComposer::VsyncSource, ISurfaceComposer::EventRegistrationFlags) override {
         return nullptr;
     }
     sp<IBinder> createDisplay(const String8& /*displayName*/,
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 9e515cd..6d91916 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -141,6 +141,8 @@
     void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t configId,
                                nsecs_t vsyncPeriod) override;
     void dispatchNullEvent(nsecs_t, PhysicalDisplayId) override;
+    void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId,
+                                    std::vector<FrameRateOverride> overrides) override;
 
     void scheduleCallbacks();
 
@@ -175,8 +177,7 @@
 }
 
 Choreographer::Choreographer(const sp<Looper>& looper)
-      : DisplayEventDispatcher(looper, ISurfaceComposer::VsyncSource::eVsyncSourceApp,
-                               ISurfaceComposer::ConfigChanged::eConfigChangedSuppress),
+      : DisplayEventDispatcher(looper, ISurfaceComposer::VsyncSource::eVsyncSourceApp),
         mLooper(looper),
         mThreadId(std::this_thread::get_id()) {
     std::lock_guard<std::mutex> _l(gChoreographers.lock);
@@ -381,15 +382,13 @@
             this, to_string(displayId).c_str(), toString(connected));
 }
 
-// TODO(b/74619554): The PhysicalDisplayId is ignored because currently
-// Choreographer only supports dispatching VSYNC events for the internal
-// display, so as such Choreographer does not support the notion of multiple
-// displays. When multi-display choreographer is properly supported, then
-// PhysicalDisplayId should no longer be ignored.
-void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId displayId, int32_t configId,
-                                          nsecs_t) {
-    ALOGV("choreographer %p ~ received config change event (displayId=%s, configId=%d).",
-          this, to_string(displayId).c_str(), configId);
+void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId, int32_t, nsecs_t) {
+    LOG_ALWAYS_FATAL("dispatchConfigChanged was called but was never registered");
+}
+
+void Choreographer::dispatchFrameRateOverrides(nsecs_t, PhysicalDisplayId,
+                                               std::vector<FrameRateOverride>) {
+    LOG_ALWAYS_FATAL("dispatchFrameRateOverrides was called but was never registered");
 }
 
 void Choreographer::dispatchNullEvent(nsecs_t, PhysicalDisplayId) {
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index cd7f37b..b878150 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -31,6 +31,9 @@
         "libui",
         "libutils",
     ],
+    include_dirs: [
+        "external/skia/src/gpu",
+    ],
     whole_static_libs: ["libskia"],
     local_include_dirs: ["include"],
     export_include_dirs: ["include"],
@@ -75,6 +78,7 @@
 filegroup {
     name: "librenderengine_skia_sources",
     srcs: [
+        "skia/AutoBackendTexture.cpp",
         "skia/SkiaRenderEngine.cpp",
         "skia/SkiaGLRenderEngine.cpp",
         "skia/filters/BlurFilter.cpp",
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index be83ebc..279e648 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -15,6 +15,7 @@
  */
 
 //#define LOG_NDEBUG 0
+#include "EGL/egl.h"
 #undef LOG_TAG
 #define LOG_TAG "RenderEngine"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
@@ -446,19 +447,37 @@
                                           mPlaceholderBuffer, attributes);
     ALOGE_IF(mPlaceholderImage == EGL_NO_IMAGE_KHR, "Failed to create placeholder image: %#x",
              eglGetError());
+
+    mShadowTexture = std::make_unique<GLShadowTexture>();
 }
 
 GLESRenderEngine::~GLESRenderEngine() {
     // Destroy the image manager first.
     mImageManager = nullptr;
+    mShadowTexture = nullptr;
     cleanFramebufferCache();
+    ProgramCache::getInstance().purgeCaches();
     std::lock_guard<std::mutex> lock(mRenderingMutex);
+    glDisableVertexAttribArray(Program::position);
     unbindFrameBuffer(mDrawingBuffer.get());
     mDrawingBuffer = nullptr;
     eglDestroyImageKHR(mEGLDisplay, mPlaceholderImage);
     mImageCache.clear();
+    if (mStubSurface != EGL_NO_SURFACE) {
+        eglDestroySurface(mEGLDisplay, mStubSurface);
+    }
+    if (mProtectedStubSurface != EGL_NO_SURFACE) {
+        eglDestroySurface(mEGLDisplay, mProtectedStubSurface);
+    }
+    if (mEGLContext != EGL_NO_CONTEXT) {
+        eglDestroyContext(mEGLDisplay, mEGLContext);
+    }
+    if (mProtectedEGLContext != EGL_NO_CONTEXT) {
+        eglDestroyContext(mEGLDisplay, mProtectedEGLContext);
+    }
     eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
     eglTerminate(mEGLDisplay);
+    eglReleaseThread();
 }
 
 std::unique_ptr<Framebuffer> GLESRenderEngine::createFramebuffer() {
@@ -1800,7 +1819,7 @@
 
     mState.cornerRadius = 0.0f;
     mState.drawShadows = true;
-    setupLayerTexturing(mShadowTexture.getTexture());
+    setupLayerTexturing(mShadowTexture->getTexture());
     drawMesh(mesh);
     mState.drawShadows = false;
 }
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index c0449a1..92e1529 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -193,7 +193,7 @@
     GLuint mVpWidth;
     GLuint mVpHeight;
     Description mState;
-    GLShadowTexture mShadowTexture;
+    std::unique_ptr<GLShadowTexture> mShadowTexture = nullptr;
 
     mat4 mSrgbToXyz;
     mat4 mDisplayP3ToXyz;
@@ -294,7 +294,7 @@
     friend class BlurFilter;
     friend class GenericProgram;
     std::unique_ptr<FlushTracer> mFlushTracer;
-    std::unique_ptr<ImageManager> mImageManager = std::make_unique<ImageManager>(this);
+    std::unique_ptr<ImageManager> mImageManager;
 };
 
 } // namespace gl
diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp
index 383486b..58d6caa 100644
--- a/libs/renderengine/gl/GLFramebuffer.cpp
+++ b/libs/renderengine/gl/GLFramebuffer.cpp
@@ -38,6 +38,7 @@
 }
 
 GLFramebuffer::~GLFramebuffer() {
+    setNativeWindowBuffer(nullptr, false, false);
     glDeleteFramebuffers(1, &mFramebufferName);
     glDeleteTextures(1, &mTextureName);
 }
diff --git a/libs/renderengine/gl/Program.cpp b/libs/renderengine/gl/Program.cpp
index a172c56..26f6166 100644
--- a/libs/renderengine/gl/Program.cpp
+++ b/libs/renderengine/gl/Program.cpp
@@ -82,6 +82,14 @@
     }
 }
 
+Program::~Program() {
+    glDetachShader(mProgram, mVertexShader);
+    glDetachShader(mProgram, mFragmentShader);
+    glDeleteShader(mVertexShader);
+    glDeleteShader(mFragmentShader);
+    glDeleteProgram(mProgram);
+}
+
 bool Program::isValid() const {
     return mInitialized;
 }
diff --git a/libs/renderengine/gl/Program.h b/libs/renderengine/gl/Program.h
index 4292645..41f1bf8 100644
--- a/libs/renderengine/gl/Program.h
+++ b/libs/renderengine/gl/Program.h
@@ -54,7 +54,7 @@
     };
 
     Program(const ProgramCache::Key& needs, const char* vertex, const char* fragment);
-    ~Program() = default;
+    ~Program();
 
     /* whether this object is usable */
     bool isValid() const;
diff --git a/libs/renderengine/gl/ProgramCache.h b/libs/renderengine/gl/ProgramCache.h
index 37bb651..535d21c 100644
--- a/libs/renderengine/gl/ProgramCache.h
+++ b/libs/renderengine/gl/ProgramCache.h
@@ -203,6 +203,8 @@
     // if none can be found.
     void useProgram(const EGLContext context, const Description& description);
 
+    void purgeCaches() { mCaches.clear(); }
+
 private:
     // compute a cache Key from a Description
     static Key computeKey(const Description& description);
diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp
new file mode 100644
index 0000000..d126c27
--- /dev/null
+++ b/libs/renderengine/skia/AutoBackendTexture.cpp
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AutoBackendTexture.h"
+
+#undef LOG_TAG
+#define LOG_TAG "RenderEngine"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <utils/Trace.h>
+
+#include "log/log_main.h"
+#include "utils/Trace.h"
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+// Converts an android dataspace to a supported SkColorSpace
+// Supported dataspaces are
+// 1. sRGB
+// 2. Display P3
+// 3. BT2020 PQ
+// 4. BT2020 HLG
+// Unknown primaries are mapped to BT709, and unknown transfer functions
+// are mapped to sRGB.
+static sk_sp<SkColorSpace> toSkColorSpace(ui::Dataspace dataspace) {
+    skcms_Matrix3x3 gamut;
+    switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
+        case HAL_DATASPACE_STANDARD_BT709:
+            gamut = SkNamedGamut::kSRGB;
+            break;
+        case HAL_DATASPACE_STANDARD_BT2020:
+            gamut = SkNamedGamut::kRec2020;
+            break;
+        case HAL_DATASPACE_STANDARD_DCI_P3:
+            gamut = SkNamedGamut::kDisplayP3;
+            break;
+        default:
+            ALOGV("Unsupported Gamut: %d, defaulting to sRGB", dataspace);
+            gamut = SkNamedGamut::kSRGB;
+            break;
+    }
+
+    switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
+        case HAL_DATASPACE_TRANSFER_LINEAR:
+            return SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear, gamut);
+        case HAL_DATASPACE_TRANSFER_SRGB:
+            return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut);
+        case HAL_DATASPACE_TRANSFER_ST2084:
+            return SkColorSpace::MakeRGB(SkNamedTransferFn::kPQ, gamut);
+        case HAL_DATASPACE_TRANSFER_HLG:
+            return SkColorSpace::MakeRGB(SkNamedTransferFn::kHLG, gamut);
+        default:
+            ALOGV("Unsupported Gamma: %d, defaulting to sRGB transfer", dataspace);
+            return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut);
+    }
+}
+
+AutoBackendTexture::AutoBackendTexture(GrDirectContext* context, AHardwareBuffer* buffer,
+                                       bool isRender) {
+    AHardwareBuffer_Desc desc;
+    AHardwareBuffer_describe(buffer, &desc);
+    bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT);
+    GrBackendFormat backendFormat =
+            GrAHardwareBufferUtils::GetBackendFormat(context, buffer, desc.format, false);
+    mBackendTexture =
+            GrAHardwareBufferUtils::MakeBackendTexture(context, buffer, desc.width, desc.height,
+                                                       &mDeleteProc, &mUpdateProc, &mImageCtx,
+                                                       createProtectedImage, backendFormat,
+                                                       isRender);
+    mColorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(desc.format);
+}
+
+void AutoBackendTexture::unref(bool releaseLocalResources) {
+    if (releaseLocalResources) {
+        mSurface = nullptr;
+        mImage = nullptr;
+    }
+
+    mUsageCount--;
+    if (mUsageCount <= 0) {
+        if (mBackendTexture.isValid()) {
+            mDeleteProc(mImageCtx);
+            mBackendTexture = {};
+        }
+        delete this;
+    }
+}
+
+// releaseSurfaceProc is invoked by SkSurface, when the texture is no longer in use.
+// "releaseContext" contains an "AutoBackendTexture*".
+void AutoBackendTexture::releaseSurfaceProc(SkSurface::ReleaseContext releaseContext) {
+    AutoBackendTexture* textureRelease = reinterpret_cast<AutoBackendTexture*>(releaseContext);
+    textureRelease->unref(false);
+}
+
+// releaseImageProc is invoked by SkImage, when the texture is no longer in use.
+// "releaseContext" contains an "AutoBackendTexture*".
+void AutoBackendTexture::releaseImageProc(SkImage::ReleaseContext releaseContext) {
+    AutoBackendTexture* textureRelease = reinterpret_cast<AutoBackendTexture*>(releaseContext);
+    textureRelease->unref(false);
+}
+
+sk_sp<SkImage> AutoBackendTexture::makeImage(ui::Dataspace dataspace, SkAlphaType alphaType,
+                                             GrDirectContext* context) {
+    ATRACE_CALL();
+
+    if (mBackendTexture.isValid()) {
+        mUpdateProc(mImageCtx, context);
+    }
+
+    sk_sp<SkImage> image =
+            SkImage::MakeFromTexture(context, mBackendTexture, kTopLeft_GrSurfaceOrigin, mColorType,
+                                     alphaType, toSkColorSpace(dataspace), releaseImageProc, this);
+    if (image.get()) {
+        // The following ref will be counteracted by releaseProc, when SkImage is discarded.
+        ref();
+    }
+
+    mImage = image;
+    mDataspace = dataspace;
+    LOG_ALWAYS_FATAL_IF(mImage == nullptr, "Unable to generate SkImage from buffer");
+    return mImage;
+}
+
+sk_sp<SkSurface> AutoBackendTexture::getOrCreateSurface(ui::Dataspace dataspace,
+                                                        GrDirectContext* context) {
+    ATRACE_CALL();
+    if (!mSurface.get() || mDataspace != dataspace) {
+        sk_sp<SkSurface> surface =
+                SkSurface::MakeFromBackendTexture(context, mBackendTexture,
+                                                  kTopLeft_GrSurfaceOrigin, 0, mColorType,
+                                                  toSkColorSpace(dataspace), nullptr,
+                                                  releaseSurfaceProc, this);
+        if (surface.get()) {
+            // The following ref will be counteracted by releaseProc, when SkSurface is discarded.
+            ref();
+        }
+        mSurface = surface;
+    }
+
+    mDataspace = dataspace;
+    LOG_ALWAYS_FATAL_IF(mSurface == nullptr, "Unable to generate SkSurface");
+    return mSurface;
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/skia/AutoBackendTexture.h b/libs/renderengine/skia/AutoBackendTexture.h
new file mode 100644
index 0000000..30f4b77
--- /dev/null
+++ b/libs/renderengine/skia/AutoBackendTexture.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <GrAHardwareBufferUtils.h>
+#include <GrDirectContext.h>
+#include <SkImage.h>
+#include <SkSurface.h>
+#include <sys/types.h>
+
+#include "android-base/macros.h"
+#include "ui/GraphicTypes.h"
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+/**
+ * AutoBackendTexture manages GPU image  lifetime. It is a ref-counted object
+ * that keeps GPU resources alive until the last SkImage or SkSurface object using them is
+ * destroyed.
+ */
+class AutoBackendTexture {
+public:
+    // Local reference that supports RAII-style management of an AutoBackendTexture
+    // AutoBackendTexture by itself can't be managed in a similar fashion because
+    // of shared ownership with Skia objects, so we wrap it here instead.
+    class LocalRef {
+    public:
+        LocalRef() {}
+
+        ~LocalRef() {
+            // Destroying the texture is the same as setting it to null
+            setTexture(nullptr);
+        }
+
+        // Sets the texture to locally ref-track.
+        void setTexture(AutoBackendTexture* texture) {
+            if (mTexture != nullptr) {
+                mTexture->unref(true);
+            }
+
+            mTexture = texture;
+            if (mTexture != nullptr) {
+                mTexture->ref();
+            }
+        }
+
+        AutoBackendTexture* getTexture() const { return mTexture; }
+
+        DISALLOW_COPY_AND_ASSIGN(LocalRef);
+
+    private:
+        AutoBackendTexture* mTexture = nullptr;
+    };
+
+    // Creates a GrBackendTexture whose contents come from the provided buffer.
+    AutoBackendTexture(GrDirectContext* context, AHardwareBuffer* buffer, bool isRender);
+
+    void ref() { mUsageCount++; }
+
+    // releaseLocalResources is true if the underlying SkImage and SkSurface
+    // should be deleted from local tracking.
+    void unref(bool releaseLocalResources);
+
+    // Makes a new SkImage from the texture content.
+    // As SkImages are immutable but buffer content is not, we create
+    // a new SkImage every time.
+    sk_sp<SkImage> makeImage(ui::Dataspace dataspace, SkAlphaType alphaType,
+                             GrDirectContext* context);
+
+    // Makes a new SkSurface from the texture content, if needed.
+    sk_sp<SkSurface> getOrCreateSurface(ui::Dataspace dataspace, GrDirectContext* context);
+
+private:
+    // The only way to invoke dtor is with unref, when mUsageCount is 0.
+    ~AutoBackendTexture() {}
+
+    GrBackendTexture mBackendTexture;
+    GrAHardwareBufferUtils::DeleteImageProc mDeleteProc;
+    GrAHardwareBufferUtils::UpdateImageProc mUpdateProc;
+    GrAHardwareBufferUtils::TexImageCtx mImageCtx;
+
+    static void releaseSurfaceProc(SkSurface::ReleaseContext releaseContext);
+    static void releaseImageProc(SkImage::ReleaseContext releaseContext);
+
+    int mUsageCount = 0;
+
+    sk_sp<SkImage> mImage = nullptr;
+    sk_sp<SkSurface> mSurface = nullptr;
+    ui::Dataspace mDataspace = ui::Dataspace::UNKNOWN;
+    SkColorType mColorType = kUnknown_SkColorType;
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 07be0b6..a0660fb 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -16,8 +16,10 @@
 
 //#define LOG_NDEBUG 0
 #include <cstdint>
+#include <memory>
 
 #include "SkImageInfo.h"
+#include "log/log_main.h"
 #include "system/graphics-base-v1.0.h"
 #undef LOG_TAG
 #define LOG_TAG "RenderEngine"
@@ -145,47 +147,6 @@
     return err;
 }
 
-// Converts an android dataspace to a supported SkColorSpace
-// Supported dataspaces are
-// 1. sRGB
-// 2. Display P3
-// 3. BT2020 PQ
-// 4. BT2020 HLG
-// Unknown primaries are mapped to BT709, and unknown transfer functions
-// are mapped to sRGB.
-static sk_sp<SkColorSpace> toColorSpace(ui::Dataspace dataspace) {
-    skcms_Matrix3x3 gamut;
-    switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
-        case HAL_DATASPACE_STANDARD_BT709:
-            gamut = SkNamedGamut::kSRGB;
-            break;
-        case HAL_DATASPACE_STANDARD_BT2020:
-            gamut = SkNamedGamut::kRec2020;
-            break;
-        case HAL_DATASPACE_STANDARD_DCI_P3:
-            gamut = SkNamedGamut::kDisplayP3;
-            break;
-        default:
-            ALOGV("Unsupported Gamut: %d, defaulting to sRGB", dataspace);
-            gamut = SkNamedGamut::kSRGB;
-            break;
-    }
-
-    switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
-        case HAL_DATASPACE_TRANSFER_LINEAR:
-            return SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear, gamut);
-        case HAL_DATASPACE_TRANSFER_SRGB:
-            return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut);
-        case HAL_DATASPACE_TRANSFER_ST2084:
-            return SkColorSpace::MakeRGB(SkNamedTransferFn::kPQ, gamut);
-        case HAL_DATASPACE_TRANSFER_HLG:
-            return SkColorSpace::MakeRGB(SkNamedTransferFn::kHLG, gamut);
-        default:
-            ALOGV("Unsupported Gamma: %d, defaulting to sRGB transfer", dataspace);
-            return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut);
-    }
-}
-
 std::unique_ptr<SkiaGLRenderEngine> SkiaGLRenderEngine::create(
         const RenderEngineCreationArgs& args) {
     // initialize EGL for the default display
@@ -455,9 +416,15 @@
             sourceTransfer != destTransfer;
 }
 
+static bool needsLinearEffect(const mat4& colorTransform, ui::Dataspace sourceDataspace,
+                              ui::Dataspace destinationDataspace) {
+    return colorTransform != mat4() || needsToneMapping(sourceDataspace, destinationDataspace);
+}
+
 void SkiaGLRenderEngine::unbindExternalTextureBuffer(uint64_t bufferId) {
     std::lock_guard<std::mutex> lock(mRenderingMutex);
-    mImageCache.erase(bufferId);
+    mTextureCache.erase(bufferId);
+    mProtectedTextureCache.erase(bufferId);
 }
 
 status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
@@ -486,36 +453,36 @@
     }
 
     auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
-    auto& cache = mInProtectedContext ? mProtectedSurfaceCache : mSurfaceCache;
+    auto& cache = mInProtectedContext ? mProtectedTextureCache : mTextureCache;
     AHardwareBuffer_Desc bufferDesc;
     AHardwareBuffer_describe(buffer->toAHardwareBuffer(), &bufferDesc);
     LOG_ALWAYS_FATAL_IF(!hasUsage(bufferDesc, AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE),
                         "missing usage");
 
-    sk_sp<SkSurface> surface;
+    std::shared_ptr<AutoBackendTexture::LocalRef> surfaceTextureRef = nullptr;
     if (useFramebufferCache) {
         auto iter = cache.find(buffer->getId());
         if (iter != cache.end()) {
             ALOGV("Cache hit!");
-            surface = iter->second;
+            surfaceTextureRef = iter->second;
         }
     }
-    if (!surface) {
-        surface = SkSurface::MakeFromAHardwareBuffer(grContext.get(), buffer->toAHardwareBuffer(),
-                                                     GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
-                                                     mUseColorManagement
-                                                             ? toColorSpace(display.outputDataspace)
-                                                             : SkColorSpace::MakeSRGB(),
-                                                     nullptr);
-        if (useFramebufferCache && surface) {
+
+    if (surfaceTextureRef == nullptr || surfaceTextureRef->getTexture() == nullptr) {
+        surfaceTextureRef = std::make_shared<AutoBackendTexture::LocalRef>();
+        surfaceTextureRef->setTexture(
+                new AutoBackendTexture(mGrContext.get(), buffer->toAHardwareBuffer(), true));
+        if (useFramebufferCache) {
             ALOGD("Adding to cache");
-            cache.insert({buffer->getId(), surface});
+            cache.insert({buffer->getId(), surfaceTextureRef});
         }
     }
-    if (!surface) {
-        ALOGE("Failed to make surface");
-        return BAD_VALUE;
-    }
+
+    sk_sp<SkSurface> surface =
+            surfaceTextureRef->getTexture()->getOrCreateSurface(mUseColorManagement
+                                                                        ? display.outputDataspace
+                                                                        : ui::Dataspace::SRGB,
+                                                                mGrContext.get());
 
     auto canvas = surface->getCanvas();
     // Clear the entire canvas with a transparent black to prevent ghost images.
@@ -586,28 +553,37 @@
             const auto& item = layer->source.buffer;
             const auto bufferWidth = item.buffer->getBounds().width();
             const auto bufferHeight = item.buffer->getBounds().height();
-            sk_sp<SkImage> image;
-            auto iter = mImageCache.find(item.buffer->getId());
-            if (iter != mImageCache.end()) {
-                image = iter->second;
+            std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef = nullptr;
+            auto iter = mTextureCache.find(item.buffer->getId());
+            if (iter != mTextureCache.end()) {
+                imageTextureRef = iter->second;
             } else {
-                image = SkImage::MakeFromAHardwareBuffer(
-                        item.buffer->toAHardwareBuffer(),
-                        item.isOpaque ? kOpaque_SkAlphaType
-                                      : (item.usePremultipliedAlpha ? kPremul_SkAlphaType
-                                                                    : kUnpremul_SkAlphaType),
-                        mUseColorManagement
-                                ? (needsToneMapping(layer->sourceDataspace, display.outputDataspace)
-                                           // If we need to map to linear space, then
-                                           // mark the source image with the same
-                                           // colorspace as the destination surface so
-                                           // that Skia's color management is a no-op.
-                                           ? toColorSpace(display.outputDataspace)
-                                           : toColorSpace(layer->sourceDataspace))
-                                : SkColorSpace::MakeSRGB());
-                mImageCache.insert({item.buffer->getId(), image});
+                imageTextureRef = std::make_shared<AutoBackendTexture::LocalRef>();
+                imageTextureRef->setTexture(new AutoBackendTexture(mGrContext.get(),
+                                                                   item.buffer->toAHardwareBuffer(),
+                                                                   false));
+                mTextureCache.insert({buffer->getId(), imageTextureRef});
             }
 
+            sk_sp<SkImage> image =
+                    imageTextureRef->getTexture()
+                            ->makeImage(mUseColorManagement
+                                                ? (needsLinearEffect(layer->colorTransform,
+                                                                     layer->sourceDataspace,
+                                                                     display.outputDataspace)
+                                                           // If we need to map to linear space,
+                                                           // then mark the source image with the
+                                                           // same colorspace as the destination
+                                                           // surface so that Skia's color
+                                                           // management is a no-op.
+                                                           ? display.outputDataspace
+                                                           : layer->sourceDataspace)
+                                                : ui::Dataspace::SRGB,
+                                        item.isOpaque ? kOpaque_SkAlphaType
+                                                      : (item.usePremultipliedAlpha
+                                                                 ? kPremul_SkAlphaType
+                                                                 : kUnpremul_SkAlphaType),
+                                        mGrContext.get());
             SkMatrix matrix;
             if (layer->geometry.roundedCornersRadius > 0) {
                 const auto roundedRect = getRoundedRect(layer);
@@ -652,22 +628,44 @@
 
             matrix.postConcat(texMatrix);
             matrix.postScale(rotatedBufferWidth, rotatedBufferHeight);
-            sk_sp<SkShader> shader = image->makeShader(matrix);
+            sk_sp<SkShader> shader;
+
+            if (layer->source.buffer.useTextureFiltering) {
+                shader = image->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
+                                           SkSamplingOptions(
+                                                   {SkFilterMode::kLinear, SkMipmapMode::kNone}),
+                                           &matrix);
+            } else {
+                shader = image->makeShader(matrix);
+            }
 
             if (mUseColorManagement &&
-                needsToneMapping(layer->sourceDataspace, display.outputDataspace)) {
+                needsLinearEffect(layer->colorTransform, layer->sourceDataspace,
+                                  display.outputDataspace)) {
                 LinearEffect effect = LinearEffect{.inputDataspace = layer->sourceDataspace,
                                                    .outputDataspace = display.outputDataspace,
                                                    .undoPremultipliedAlpha = !item.isOpaque &&
                                                            item.usePremultipliedAlpha};
-                sk_sp<SkRuntimeEffect> runtimeEffect = buildRuntimeEffect(effect);
+
+                auto effectIter = mRuntimeEffects.find(effect);
+                sk_sp<SkRuntimeEffect> runtimeEffect = nullptr;
+                if (effectIter == mRuntimeEffects.end()) {
+                    runtimeEffect = buildRuntimeEffect(effect);
+                    mRuntimeEffects.insert({effect, runtimeEffect});
+                } else {
+                    runtimeEffect = effectIter->second;
+                }
+
                 paint.setShader(createLinearEffectShader(shader, effect, runtimeEffect,
+                                                         layer->colorTransform,
                                                          display.maxLuminance,
                                                          layer->source.buffer.maxMasteringLuminance,
                                                          layer->source.buffer.maxContentLuminance));
             } else {
                 paint.setShader(shader);
             }
+            // Make sure to take into the account the alpha set on the layer.
+            paint.setAlphaf(layer->alpha);
         } else {
             ATRACE_NAME("DrawColor");
             const auto color = layer->source.solidColor;
@@ -910,10 +908,7 @@
     return eglCreatePbufferSurface(display, placeholderConfig, attributes.data());
 }
 
-void SkiaGLRenderEngine::cleanFramebufferCache() {
-    mSurfaceCache.clear();
-    mProtectedSurfaceCache.clear();
-}
+void SkiaGLRenderEngine::cleanFramebufferCache() {}
 
 } // namespace skia
 } // namespace renderengine
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 965cb41..f5eed1e 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -29,9 +29,13 @@
 #include <mutex>
 #include <unordered_map>
 
+#include "AutoBackendTexture.h"
 #include "EGL/egl.h"
+#include "SkImageInfo.h"
 #include "SkiaRenderEngine.h"
+#include "android-base/macros.h"
 #include "filters/BlurFilter.h"
+#include "skia/filters/LinearEffect.h"
 
 namespace android {
 namespace renderengine {
@@ -92,8 +96,12 @@
 
     const bool mUseColorManagement;
 
-    // Cache of GL images that we'll store per GraphicBuffer ID
-    std::unordered_map<uint64_t, sk_sp<SkImage>> mImageCache GUARDED_BY(mRenderingMutex);
+    // Cache of GL textures that we'll store per GraphicBuffer ID
+    std::unordered_map<uint64_t, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache
+            GUARDED_BY(mRenderingMutex);
+    std::unordered_map<uint64_t, std::shared_ptr<AutoBackendTexture::LocalRef>>
+            mProtectedTextureCache GUARDED_BY(mRenderingMutex);
+    std::unordered_map<LinearEffect, sk_sp<SkRuntimeEffect>, LinearEffectHasher> mRuntimeEffects;
     // Mutex guarding rendering operations, so that:
     // 1. GL operations aren't interleaved, and
     // 2. Internal state related to rendering that is potentially modified by
@@ -107,8 +115,6 @@
     // Same as above, but for protected content (eg. DRM)
     sk_sp<GrDirectContext> mProtectedGrContext;
 
-    std::unordered_map<uint64_t, sk_sp<SkSurface>> mSurfaceCache;
-    std::unordered_map<uint64_t, sk_sp<SkSurface>> mProtectedSurfaceCache;
     bool mInProtectedContext = false;
 };
 
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
index a514825..f19b64a 100644
--- a/libs/renderengine/skia/filters/BlurFilter.cpp
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
 #include "BlurFilter.h"
 #include <SkCanvas.h>
 #include <SkData.h>
@@ -38,11 +40,11 @@
         half4 main(float2 xy) {
             float2 scaled_xy = float2(xy.x * in_inverseScale, xy.y * in_inverseScale);
 
-            float4 c = float4(sample(input, scaled_xy));
-            c += float4(sample(input, scaled_xy + float2( in_blurOffset.x,  in_blurOffset.y)));
-            c += float4(sample(input, scaled_xy + float2( in_blurOffset.x, -in_blurOffset.y)));
-            c += float4(sample(input, scaled_xy + float2(-in_blurOffset.x,  in_blurOffset.y)));
-            c += float4(sample(input, scaled_xy + float2(-in_blurOffset.x, -in_blurOffset.y)));
+            half4 c = sample(input, scaled_xy);
+            c += sample(input, scaled_xy + float2( in_blurOffset.x,  in_blurOffset.y));
+            c += sample(input, scaled_xy + float2( in_blurOffset.x, -in_blurOffset.y));
+            c += sample(input, scaled_xy + float2(-in_blurOffset.x,  in_blurOffset.y));
+            c += sample(input, scaled_xy + float2(-in_blurOffset.x, -in_blurOffset.y));
 
             return half4(c.rgb * 0.2, 1.0);
         }
@@ -57,8 +59,6 @@
 
 sk_sp<SkSurface> BlurFilter::generate(SkCanvas* canvas, const sk_sp<SkSurface> input,
                                       const uint32_t blurRadius, SkRect rect) const {
-    ATRACE_CALL();
-
     // Kawase is an approximation of Gaussian, but it behaves differently from it.
     // A radius transformation is required for approximating them, and also to introduce
     // non-integer steps, necessary to smoothly interpolate large radii.
@@ -77,7 +77,7 @@
     const float stepY = radiusByPasses;
 
     // start by drawing and downscaling and doing the first blur pass
-    SkFilterOptions linear = {SkSamplingMode::kLinear, SkMipmapMode::kNone};
+    SkSamplingOptions linear(SkFilterMode::kLinear, SkMipmapMode::kNone);
     SkRuntimeShaderBuilder blurBuilder(mBlurEffect);
     blurBuilder.child("input") =
             input->makeImageSnapshot()->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear);
@@ -126,12 +126,16 @@
         }
         lastDrawTarget = readSurface;
     }
-    lastDrawTarget->flushAndSubmit();
+
+    {
+        ATRACE_NAME("Flush Offscreen Surfaces");
+        lastDrawTarget->flushAndSubmit();
+    }
     return lastDrawTarget;
 }
 
 SkMatrix BlurFilter::getShaderMatrix() const {
-    return SkMatrix::MakeScale(kInverseInputScale);
+    return SkMatrix::Scale(kInverseInputScale, kInverseInputScale);
 }
 
 } // namespace skia
diff --git a/libs/renderengine/skia/filters/LinearEffect.cpp b/libs/renderengine/skia/filters/LinearEffect.cpp
index 376abdf..7680649 100644
--- a/libs/renderengine/skia/filters/LinearEffect.cpp
+++ b/libs/renderengine/skia/filters/LinearEffect.cpp
@@ -16,12 +16,16 @@
 
 #include "LinearEffect.h"
 
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
 #include <SkString.h>
+#include <utils/Trace.h>
 
 #include <optional>
 
 #include "log/log.h"
 #include "math/mat4.h"
+#include "system/graphics-base-v1.0.h"
 #include "ui/ColorSpace.h"
 
 namespace android {
@@ -46,6 +50,30 @@
                 }
             )");
             break;
+        case HAL_DATASPACE_TRANSFER_HLG:
+            shader.append(R"(
+                float EOTF_channel(float channel) {
+                    const float a = 0.17883277;
+                    const float b = 0.28466892;
+                    const float c = 0.55991073;
+                    return channel <= 0.5 ? channel * channel / 3.0 :
+                            (exp((channel - c) / a) + b) / 12.0;
+                }
+
+                float3 EOTF(float3 color) {
+                    return float3(EOTF_channel(color.r), EOTF_channel(color.g),
+                            EOTF_channel(color.b));
+                }
+            )");
+            break;
+        case HAL_DATASPACE_TRANSFER_LINEAR:
+            shader.append(R"(
+                float3 EOTF(float3 color) {
+                    return color;
+                }
+            )");
+            break;
+        case HAL_DATASPACE_TRANSFER_SRGB:
         default:
             shader.append(R"(
 
@@ -79,13 +107,8 @@
     )");
 }
 
-static void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace,
-                         SkString& shader) {
-    shader.append(R"(
-            uniform float in_displayMaxLuminance;
-            uniform float in_inputMaxLuminance;
-            uniform float in_maxContentLuminance;
-        )");
+// Conversion from relative light to absolute light (maps from [0, 1] to [0, maxNits])
+static void generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace, SkString& shader) {
     switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
         case HAL_DATASPACE_TRANSFER_ST2084:
             shader.append(R"(
@@ -93,9 +116,50 @@
                         return xyz * 10000.0;
                     }
                 )");
+            break;
+        case HAL_DATASPACE_TRANSFER_HLG:
+            shader.append(R"(
+                    float3 ScaleLuminance(float3 xyz) {
+                        return xyz * 1000.0 * pow(xyz.y, 0.2);
+                    }
+                )");
+            break;
+        default:
+            shader.append(R"(
+                    float3 ScaleLuminance(float3 xyz) {
+                        return xyz * in_displayMaxLuminance;
+                    }
+                )");
+            break;
+    }
+}
 
+static void generateToneMapInterpolation(ui::Dataspace inputDataspace,
+                                         ui::Dataspace outputDataspace, SkString& shader) {
+    switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+        case HAL_DATASPACE_TRANSFER_ST2084:
+        case HAL_DATASPACE_TRANSFER_HLG:
             switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+                case HAL_DATASPACE_TRANSFER_ST2084:
+                    shader.append(R"(
+                            float3 ToneMap(float3 xyz) {
+                                return xyz;
+                            }
+                        )");
+                    break;
+                case HAL_DATASPACE_TRANSFER_HLG:
+                    // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, so
+                    // we'll clamp the luminance range in case we're mapping from PQ input to HLG
+                    // output.
+                    shader.append(R"(
+                            float3 ToneMap(float3 xyz) {
+                                return clamp(xyz, 0.0, 1000.0);
+                            }
+                        )");
+                    break;
                 default:
+                    // Here we're mapping from HDR to SDR content, so interpolate using a Hermitian
+                    // polynomial onto the smaller luminance range.
                     shader.append(R"(
                             float3 ToneMap(float3 xyz) {
                                 float maxInLumi = in_inputMaxLuminance;
@@ -155,18 +219,70 @@
             }
             break;
         default:
-            shader.append(R"(
-                    float3 ScaleLuminance(float3 xyz) {
-                        return xyz * in_displayMaxLuminance;
-                    }
+            switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+                case HAL_DATASPACE_TRANSFER_ST2084:
+                case HAL_DATASPACE_TRANSFER_HLG:
+                    // Map from SDR onto an HDR output buffer
+                    // Here we use a polynomial curve to map from [0, displayMaxLuminance] onto
+                    // [0, maxOutLumi] which is hard-coded to be 3000 nits.
+                    shader.append(R"(
+                            float3 ToneMap(float3 xyz) {
+                                const float maxOutLumi = 3000.0;
 
-                    float3 ToneMap(float3 xyz) {
-                        return xyz;
-                    }
-                )");
+                                const float x0 = 5.0;
+                                const float y0 = 2.5;
+                                float x1 = in_displayMaxLuminance * 0.7;
+                                float y1 = maxOutLumi * 0.15;
+                                float x2 = in_displayMaxLuminance * 0.9;
+                                float y2 = maxOutLumi * 0.45;
+                                float x3 = in_displayMaxLuminance;
+                                float y3 = maxOutLumi;
+
+                                float c1 = y1 / 3.0;
+                                float c2 = y2 / 2.0;
+                                float c3 = y3 / 1.5;
+
+                                float nits = xyz.y;
+
+                                if (nits <= x0) {
+                                    // scale [0.0, x0] to [0.0, y0] linearly
+                                    float slope = y0 / x0;
+                                    return xyz * slope;
+                                } else if (nits <= x1) {
+                                    // scale [x0, x1] to [y0, y1] using a curve
+                                    float t = (nits - x0) / (x1 - x0);
+                                    nits = (1.0 - t) * (1.0 - t) * y0 + 2.0 * (1.0 - t) * t * c1 + t * t * y1;
+                                } else if (nits <= x2) {
+                                    // scale [x1, x2] to [y1, y2] using a curve
+                                    float t = (nits - x1) / (x2 - x1);
+                                    nits = (1.0 - t) * (1.0 - t) * y1 + 2.0 * (1.0 - t) * t * c2 + t * t * y2;
+                                } else {
+                                    // scale [x2, x3] to [y2, y3] using a curve
+                                    float t = (nits - x2) / (x3 - x2);
+                                    nits = (1.0 - t) * (1.0 - t) * y2 + 2.0 * (1.0 - t) * t * c3 + t * t * y3;
+                                }
+
+                                // xyz.y is greater than x0 and is thus non-zero
+                                return xyz * (nits / xyz.y);
+                            }
+                        )");
+                    break;
+                default:
+                    // For completeness, this is tone-mapping from SDR to SDR, where this is just a
+                    // no-op.
+                    shader.append(R"(
+                            float3 ToneMap(float3 xyz) {
+                                return xyz;
+                            }
+                        )");
+                    break;
+            }
             break;
     }
+}
 
+// Normalizes from absolute light back to relative light (maps from [0, maxNits] back to [0, 1])
+static void generateLuminanceNormalizationForOOTF(ui::Dataspace outputDataspace, SkString& shader) {
     switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
         case HAL_DATASPACE_TRANSFER_ST2084:
             shader.append(R"(
@@ -175,6 +291,13 @@
                     }
                 )");
             break;
+        case HAL_DATASPACE_TRANSFER_HLG:
+            shader.append(R"(
+                    float3 NormalizeLuminance(float3 xyz) {
+                        return xyz / 1000.0 * pow(xyz.y / 1000.0, -0.2 / 1.2);
+                    }
+                )");
+            break;
         default:
             shader.append(R"(
                     float3 NormalizeLuminance(float3 xyz) {
@@ -183,6 +306,19 @@
                 )");
             break;
     }
+}
+
+static void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace,
+                         SkString& shader) {
+    // Input uniforms
+    shader.append(R"(
+            uniform float in_displayMaxLuminance;
+            uniform float in_inputMaxLuminance;
+        )");
+
+    generateLuminanceScalesForOOTF(inputDataspace, shader);
+    generateToneMapInterpolation(inputDataspace, outputDataspace, shader);
+    generateLuminanceNormalizationForOOTF(outputDataspace, shader);
 
     shader.append(R"(
             float3 OOTF(float3 xyz) {
@@ -209,6 +345,30 @@
                 }
             )");
             break;
+        case HAL_DATASPACE_TRANSFER_HLG:
+            shader.append(R"(
+                float OETF_channel(float channel) {
+                    const float a = 0.17883277;
+                    const float b = 0.28466892;
+                    const float c = 0.55991073;
+                    return channel <= 1.0 / 12.0 ? sqrt(3.0 * channel) :
+                            a * log(12.0 * channel - b) + c;
+                }
+
+                float3 OETF(float3 linear) {
+                    return float3(OETF_channel(linear.r), OETF_channel(linear.g),
+                            OETF_channel(linear.b));
+                }
+            )");
+            break;
+        case HAL_DATASPACE_TRANSFER_LINEAR:
+            shader.append(R"(
+                float3 OETF(float3 linear) {
+                    return linear;
+                }
+            )");
+            break;
+        case HAL_DATASPACE_TRANSFER_SRGB:
         default:
             shader.append(R"(
                 float OETF_sRGB(float linear) {
@@ -270,6 +430,7 @@
 }
 
 sk_sp<SkRuntimeEffect> buildRuntimeEffect(const LinearEffect& linearEffect) {
+    ATRACE_CALL();
     SkString shaderString;
     generateEOTF(linearEffect.inputDataspace, shaderString);
     generateXYZTransforms(shaderString);
@@ -286,8 +447,9 @@
 
 sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> shader, const LinearEffect& linearEffect,
                                          sk_sp<SkRuntimeEffect> runtimeEffect,
-                                         float maxDisplayLuminance, float maxMasteringLuminance,
-                                         float maxContentLuminance) {
+                                         const mat4& colorTransform, float maxDisplayLuminance,
+                                         float maxMasteringLuminance, float maxContentLuminance) {
+    ATRACE_CALL();
     SkRuntimeShaderBuilder effectBuilder(runtimeEffect);
 
     effectBuilder.child("input") = shader;
@@ -296,7 +458,7 @@
     ColorSpace outputColorSpace = toColorSpace(linearEffect.outputDataspace);
 
     effectBuilder.uniform("in_rgbToXyz") = mat4(inputColorSpace.getRGBtoXYZ());
-    effectBuilder.uniform("in_xyzToRgb") = mat4(outputColorSpace.getXYZtoRGB());
+    effectBuilder.uniform("in_xyzToRgb") = colorTransform * mat4(outputColorSpace.getXYZtoRGB());
     effectBuilder.uniform("in_displayMaxLuminance") = maxDisplayLuminance;
     effectBuilder.uniform("in_inputMaxLuminance") =
             std::min(maxMasteringLuminance, maxContentLuminance);
diff --git a/libs/renderengine/skia/filters/LinearEffect.h b/libs/renderengine/skia/filters/LinearEffect.h
index 2615669..20b8338 100644
--- a/libs/renderengine/skia/filters/LinearEffect.h
+++ b/libs/renderengine/skia/filters/LinearEffect.h
@@ -16,9 +16,10 @@
 
 #pragma once
 
+#include <math/mat4.h>
+
 #include <optional>
 
-#include "SkColorMatrix.h"
 #include "SkRuntimeEffect.h"
 #include "SkShader.h"
 #include "ui/GraphicTypes.h"
@@ -63,11 +64,31 @@
     const bool undoPremultipliedAlpha = false;
 };
 
+static inline bool operator==(const LinearEffect& lhs, const LinearEffect& rhs) {
+    return lhs.inputDataspace == rhs.inputDataspace && lhs.outputDataspace == rhs.outputDataspace &&
+            lhs.undoPremultipliedAlpha == rhs.undoPremultipliedAlpha;
+}
+
+struct LinearEffectHasher {
+    // Inspired by art/runtime/class_linker.cc
+    // Also this is what boost:hash_combine does
+    static size_t HashCombine(size_t seed, size_t val) {
+        return seed ^ (val + 0x9e3779b9 + (seed << 6) + (seed >> 2));
+    }
+    size_t operator()(const LinearEffect& le) const {
+        size_t result = std::hash<ui::Dataspace>{}(le.inputDataspace);
+        result = HashCombine(result, std::hash<ui::Dataspace>{}(le.outputDataspace));
+        return HashCombine(result, std::hash<bool>{}(le.undoPremultipliedAlpha));
+    }
+};
+
 sk_sp<SkRuntimeEffect> buildRuntimeEffect(const LinearEffect& linearEffect);
 
 // Generates a shader resulting from applying the a linear effect created from
-// LinearEffectARgs::buildEffect to an inputShader. We also provide additional HDR metadata upon
-// creating the shader:
+// LinearEffectArgs::buildEffect to an inputShader.
+// Optionally, a color transform may also be provided, which combines with the
+// matrix transforming from linear XYZ to linear RGB immediately before OETF.
+// We also provide additional HDR metadata upon creating the shader:
 // * The max display luminance is the max luminance of the physical display in nits
 // * The max mastering luminance is provided as the max luminance from the SMPTE 2086
 // standard.
@@ -76,8 +97,8 @@
 sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> inputShader,
                                          const LinearEffect& linearEffect,
                                          sk_sp<SkRuntimeEffect> runtimeEffect,
-                                         float maxDisplayLuminance, float maxMasteringLuminance,
-                                         float maxContentLuminance);
+                                         const mat4& colorTransform, float maxDisplayLuminance,
+                                         float maxMasteringLuminance, float maxContentLuminance);
 } // namespace skia
 } // namespace renderengine
 } // namespace android
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index d20fcc4..0e11c99 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+#undef LOG_TAG
+#define LOG_TAG "RenderEngineTest"
+
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
@@ -37,8 +40,19 @@
 
 namespace android {
 
-struct RenderEngineTest : public ::testing::Test {
-    static void SetUpTestSuite() {
+class RenderEngineFactory {
+public:
+    virtual ~RenderEngineFactory() = default;
+
+    virtual std::string name() = 0;
+    virtual std::unique_ptr<renderengine::gl::GLESRenderEngine> createRenderEngine() = 0;
+};
+
+class GLESRenderEngineFactory : public RenderEngineFactory {
+public:
+    std::string name() override { return "GLESRenderEngineFactory"; }
+
+    std::unique_ptr<renderengine::gl::GLESRenderEngine> createRenderEngine() override {
         renderengine::RenderEngineCreationArgs reCreationArgs =
                 renderengine::RenderEngineCreationArgs::Builder()
                         .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
@@ -50,20 +64,32 @@
                         .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
                         .setRenderEngineType(renderengine::RenderEngine::RenderEngineType::GLES)
                         .build();
-        sRE = renderengine::gl::GLESRenderEngine::create(reCreationArgs);
-
-        reCreationArgs.useColorManagement = true;
-        sRECM = renderengine::gl::GLESRenderEngine::create(reCreationArgs);
+        return renderengine::gl::GLESRenderEngine::create(reCreationArgs);
     }
+};
 
-    static void TearDownTestSuite() {
-        // The ordering here is important - sCurrentBuffer must live longer
-        // than RenderEngine to avoid a null reference on tear-down.
-        sRE = nullptr;
-        sRECM = nullptr;
-        sCurrentBuffer = nullptr;
+class GLESCMRenderEngineFactory : public RenderEngineFactory {
+public:
+    std::string name() override { return "GLESCMRenderEngineFactory"; }
+
+    std::unique_ptr<renderengine::gl::GLESRenderEngine> createRenderEngine() override {
+        renderengine::RenderEngineCreationArgs reCreationArgs =
+                renderengine::RenderEngineCreationArgs::Builder()
+                        .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
+                        .setImageCacheSize(1)
+                        .setEnableProtectedContext(false)
+                        .setPrecacheToneMapperShaderOnly(false)
+                        .setSupportsBackgroundBlur(true)
+                        .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
+                        .setRenderEngineType(renderengine::RenderEngine::RenderEngineType::GLES)
+                        .setUseColorManagerment(true)
+                        .build();
+        return renderengine::gl::GLESRenderEngine::create(reCreationArgs);
     }
+};
 
+class RenderEngineTest : public ::testing::TestWithParam<std::shared_ptr<RenderEngineFactory>> {
+public:
     static sp<GraphicBuffer> allocateDefaultBuffer() {
         return new GraphicBuffer(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT,
                                  HAL_PIXEL_FORMAT_RGBA_8888, 1,
@@ -80,19 +106,24 @@
                                  "input");
     }
 
-    RenderEngineTest() { mBuffer = allocateDefaultBuffer(); }
+    RenderEngineTest() {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+        mBuffer = allocateDefaultBuffer();
+    }
 
     ~RenderEngineTest() {
         if (WRITE_BUFFER_TO_FILE_ON_FAILURE && ::testing::Test::HasFailure()) {
             writeBufferToFile("/data/texture_out_");
         }
         for (uint32_t texName : mTexNames) {
-            sRE->deleteTextures(1, &texName);
-            EXPECT_FALSE(sRE->isTextureNameKnownForTesting(texName));
+            mRE->deleteTextures(1, &texName);
+            EXPECT_FALSE(mRE->isTextureNameKnownForTesting(texName));
         }
-        for (uint32_t texName : mTexNamesCM) {
-            sRECM->deleteTextures(1, &texName);
-        }
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
     }
 
     void writeBufferToFile(const char* basename) {
@@ -261,12 +292,11 @@
 
     void invokeDraw(renderengine::DisplaySettings settings,
                     std::vector<const renderengine::LayerSettings*> layers,
-                    sp<GraphicBuffer> buffer, bool useColorManagement = false) {
+                    sp<GraphicBuffer> buffer) {
         base::unique_fd fence;
-        status_t status = useColorManagement
-                ? sRECM->drawLayers(settings, layers, buffer, true, base::unique_fd(), &fence)
-                : sRE->drawLayers(settings, layers, buffer, true, base::unique_fd(), &fence);
-        sCurrentBuffer = buffer;
+        status_t status =
+                mRE->drawLayers(settings, layers, buffer, true, base::unique_fd(), &fence);
+        mCurrentBuffer = buffer;
 
         int fd = fence.release();
         if (fd >= 0) {
@@ -276,11 +306,7 @@
 
         ASSERT_EQ(NO_ERROR, status);
         if (layers.size() > 0) {
-            if (useColorManagement) {
-                ASSERT_TRUE(sRECM->isFramebufferImageCachedForTesting(buffer->getId()));
-            } else {
-                ASSERT_TRUE(sRE->isFramebufferImageCachedForTesting(buffer->getId()));
-            }
+            ASSERT_TRUE(mRE->isFramebufferImageCachedForTesting(buffer->getId()));
         }
     }
 
@@ -335,15 +361,12 @@
     void fillBufferLayerTransform();
 
     template <typename SourceVariant>
-    void fillBufferWithColorTransform(bool useColorManagement = false);
+    void fillBufferWithColorTransform();
 
     template <typename SourceVariant>
     void fillBufferColorTransform();
 
     template <typename SourceVariant>
-    void fillBufferColorTransformCM();
-
-    template <typename SourceVariant>
     void fillRedBufferWithRoundedCorners();
 
     template <typename SourceVariant>
@@ -378,38 +401,30 @@
                     const renderengine::ShadowSettings& shadow, const ubyte4& casterColor,
                     const ubyte4& backgroundColor);
 
-    // Keep around the same renderengine object to save on initialization time.
-    // For now, exercise the GL backend directly so that some caching specifics
-    // can be tested without changing the interface.
-    static std::unique_ptr<renderengine::gl::GLESRenderEngine> sRE;
-    // renderengine object with Color Management enabled
-    static std::unique_ptr<renderengine::gl::GLESRenderEngine> sRECM;
+    std::unique_ptr<renderengine::gl::GLESRenderEngine> mRE;
+
     // Dumb hack to avoid NPE in the EGL driver: the GraphicBuffer needs to
     // be freed *after* RenderEngine is destroyed, so that the EGL image is
     // destroyed first.
-    static sp<GraphicBuffer> sCurrentBuffer;
+    sp<GraphicBuffer> mCurrentBuffer;
 
     sp<GraphicBuffer> mBuffer;
 
     std::vector<uint32_t> mTexNames;
-    std::vector<uint32_t> mTexNamesCM;
 };
 
-std::unique_ptr<renderengine::gl::GLESRenderEngine> RenderEngineTest::sRE = nullptr;
-std::unique_ptr<renderengine::gl::GLESRenderEngine> RenderEngineTest::sRECM = nullptr;
-
-sp<GraphicBuffer> RenderEngineTest::sCurrentBuffer = nullptr;
-
 struct ColorSourceVariant {
     static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b,
-                          RenderEngineTest* /*fixture*/, bool /*useColorManagement*/ = false) {
+                          RenderEngineTest* /*fixture*/) {
         layer.source.solidColor = half3(r, g, b);
+        layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     }
 };
 
 struct RelaxOpaqueBufferVariant {
     static void setOpaqueBit(renderengine::LayerSettings& layer) {
         layer.source.buffer.isOpaque = false;
+        layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     }
 
     static uint8_t getAlphaChannel() { return 255; }
@@ -418,6 +433,7 @@
 struct ForceOpaqueBufferVariant {
     static void setOpaqueBit(renderengine::LayerSettings& layer) {
         layer.source.buffer.isOpaque = true;
+        layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     }
 
     static uint8_t getAlphaChannel() {
@@ -430,16 +446,11 @@
 template <typename OpaquenessVariant>
 struct BufferSourceVariant {
     static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b,
-                          RenderEngineTest* fixture, bool useColorManagement = false) {
+                          RenderEngineTest* fixture) {
         sp<GraphicBuffer> buf = RenderEngineTest::allocateSourceBuffer(1, 1);
         uint32_t texName;
-        if (useColorManagement) {
-            fixture->sRECM->genTextures(1, &texName);
-            fixture->mTexNamesCM.push_back(texName);
-        } else {
-            fixture->sRE->genTextures(1, &texName);
-            fixture->mTexNames.push_back(texName);
-        }
+        fixture->mRE->genTextures(1, &texName);
+        fixture->mTexNames.push_back(texName);
 
         uint8_t* pixels;
         buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
@@ -460,6 +471,7 @@
 
         layer.source.buffer.buffer = buf;
         layer.source.buffer.textureName = texName;
+        layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
         OpaquenessVariant::setOpaqueBit(layer);
     }
 };
@@ -469,10 +481,12 @@
     renderengine::DisplaySettings settings;
     settings.physicalDisplay = fullscreenRect();
     settings.clip = fullscreenRect();
+    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
 
     std::vector<const renderengine::LayerSettings*> layers;
 
     renderengine::LayerSettings layer;
+    layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     layer.geometry.boundaries = fullscreenRect().toFloatRect();
     SourceVariant::fillColor(layer, r, g, b, this);
     layer.alpha = a;
@@ -509,12 +523,14 @@
 template <typename SourceVariant>
 void RenderEngineTest::fillRedOffsetBuffer() {
     renderengine::DisplaySettings settings;
+    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     settings.physicalDisplay = offsetRect();
     settings.clip = offsetRectAtZero();
 
     std::vector<const renderengine::LayerSettings*> layers;
 
     renderengine::LayerSettings layer;
+    layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     layer.geometry.boundaries = offsetRectAtZero().toFloatRect();
     SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
     layer.alpha = 1.0f;
@@ -540,6 +556,7 @@
 template <typename SourceVariant>
 void RenderEngineTest::fillBufferCheckers(uint32_t orientationFlag) {
     renderengine::DisplaySettings settings;
+    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     settings.physicalDisplay = fullscreenRect();
     // Here logical space is 2x2
     settings.clip = Rect(2, 2);
@@ -548,18 +565,21 @@
     std::vector<const renderengine::LayerSettings*> layers;
 
     renderengine::LayerSettings layerOne;
+    layerOne.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     Rect rectOne(0, 0, 1, 1);
     layerOne.geometry.boundaries = rectOne.toFloatRect();
     SourceVariant::fillColor(layerOne, 1.0f, 0.0f, 0.0f, this);
     layerOne.alpha = 1.0f;
 
     renderengine::LayerSettings layerTwo;
+    layerTwo.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     Rect rectTwo(0, 1, 1, 2);
     layerTwo.geometry.boundaries = rectTwo.toFloatRect();
     SourceVariant::fillColor(layerTwo, 0.0f, 1.0f, 0.0f, this);
     layerTwo.alpha = 1.0f;
 
     renderengine::LayerSettings layerThree;
+    layerThree.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     Rect rectThree(1, 0, 2, 1);
     layerThree.geometry.boundaries = rectThree.toFloatRect();
     SourceVariant::fillColor(layerThree, 0.0f, 0.0f, 1.0f, this);
@@ -642,10 +662,12 @@
     settings.physicalDisplay = fullscreenRect();
     // Here logical space is 2x2
     settings.clip = Rect(2, 2);
+    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
 
     std::vector<const renderengine::LayerSettings*> layers;
 
     renderengine::LayerSettings layer;
+    layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     layer.geometry.boundaries = Rect(1, 1).toFloatRect();
     // Translate one pixel diagonally
     layer.geometry.positionTransform = mat4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1);
@@ -669,16 +691,18 @@
 }
 
 template <typename SourceVariant>
-void RenderEngineTest::fillBufferWithColorTransform(bool useColorManagement) {
+void RenderEngineTest::fillBufferWithColorTransform() {
     renderengine::DisplaySettings settings;
     settings.physicalDisplay = fullscreenRect();
     settings.clip = Rect(1, 1);
+    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
 
     std::vector<const renderengine::LayerSettings*> layers;
 
     renderengine::LayerSettings layer;
+    layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     layer.geometry.boundaries = Rect(1, 1).toFloatRect();
-    SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this, useColorManagement);
+    SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this);
     layer.alpha = 1.0f;
 
     // construct a fake color matrix
@@ -692,7 +716,7 @@
 
     layers.push_back(&layer);
 
-    invokeDraw(settings, layers, mBuffer, useColorManagement);
+    invokeDraw(settings, layers, mBuffer);
 }
 
 template <typename SourceVariant>
@@ -702,20 +726,16 @@
 }
 
 template <typename SourceVariant>
-void RenderEngineTest::fillBufferColorTransformCM() {
-    fillBufferWithColorTransform<SourceVariant>(true);
-    expectBufferColor(fullscreenRect(), 126, 0, 0, 255, 1);
-}
-
-template <typename SourceVariant>
 void RenderEngineTest::fillRedBufferWithRoundedCorners() {
     renderengine::DisplaySettings settings;
     settings.physicalDisplay = fullscreenRect();
     settings.clip = fullscreenRect();
+    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
 
     std::vector<const renderengine::LayerSettings*> layers;
 
     renderengine::LayerSettings layer;
+    layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     layer.geometry.boundaries = fullscreenRect().toFloatRect();
     layer.geometry.roundedCornersRadius = 5.0f;
     layer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
@@ -756,18 +776,21 @@
     auto center = DEFAULT_DISPLAY_WIDTH / 2;
 
     renderengine::DisplaySettings settings;
+    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     settings.physicalDisplay = fullscreenRect();
     settings.clip = fullscreenRect();
 
     std::vector<const renderengine::LayerSettings*> layers;
 
     renderengine::LayerSettings backgroundLayer;
+    backgroundLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     backgroundLayer.geometry.boundaries = fullscreenRect().toFloatRect();
     SourceVariant::fillColor(backgroundLayer, 0.0f, 1.0f, 0.0f, this);
     backgroundLayer.alpha = 1.0f;
     layers.push_back(&backgroundLayer);
 
     renderengine::LayerSettings leftLayer;
+    leftLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     leftLayer.geometry.boundaries =
             Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT).toFloatRect();
     SourceVariant::fillColor(leftLayer, 1.0f, 0.0f, 0.0f, this);
@@ -775,6 +798,7 @@
     layers.push_back(&leftLayer);
 
     renderengine::LayerSettings blurLayer;
+    blurLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     blurLayer.geometry.boundaries = fullscreenRect().toFloatRect();
     blurLayer.backgroundBlurRadius = blurRadius;
     blurLayer.alpha = 0;
@@ -793,10 +817,12 @@
     renderengine::DisplaySettings settings;
     settings.physicalDisplay = fullscreenRect();
     settings.clip = fullscreenRect();
+    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
 
     std::vector<const renderengine::LayerSettings*> layersFirst;
 
     renderengine::LayerSettings layerOne;
+    layerOne.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     layerOne.geometry.boundaries =
             FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH / 3.0, DEFAULT_DISPLAY_HEIGHT / 3.0);
     SourceVariant::fillColor(layerOne, 1.0f, 0.0f, 0.0f, this);
@@ -811,6 +837,7 @@
 
     std::vector<const renderengine::LayerSettings*> layersSecond;
     renderengine::LayerSettings layerTwo;
+    layerTwo.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     layerTwo.geometry.boundaries =
             FloatRect(DEFAULT_DISPLAY_WIDTH / 3.0, DEFAULT_DISPLAY_HEIGHT / 3.0,
                       DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT);
@@ -830,15 +857,17 @@
     renderengine::DisplaySettings settings;
     settings.physicalDisplay = fullscreenRect();
     settings.clip = Rect(1, 1);
+    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
 
     std::vector<const renderengine::LayerSettings*> layers;
 
     renderengine::LayerSettings layer;
+    layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     // Here will allocate a checker board texture, but transform texture
     // coordinates so that only the upper left is applied.
     sp<GraphicBuffer> buf = allocateSourceBuffer(2, 2);
     uint32_t texName;
-    RenderEngineTest::sRE->genTextures(1, &texName);
+    RenderEngineTest::mRE->genTextures(1, &texName);
     this->mTexNames.push_back(texName);
 
     uint8_t* pixels;
@@ -887,7 +916,7 @@
     renderengine::LayerSettings layer;
     sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
     uint32_t texName;
-    RenderEngineTest::sRE->genTextures(1, &texName);
+    RenderEngineTest::mRE->genTextures(1, &texName);
     this->mTexNames.push_back(texName);
 
     uint8_t* pixels;
@@ -926,7 +955,7 @@
     renderengine::LayerSettings layer;
     sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
     uint32_t texName;
-    RenderEngineTest::sRE->genTextures(1, &texName);
+    RenderEngineTest::mRE->genTextures(1, &texName);
     this->mTexNames.push_back(texName);
 
     uint8_t* pixels;
@@ -981,6 +1010,7 @@
                                   const renderengine::ShadowSettings& shadow,
                                   const ubyte4& casterColor, const ubyte4& backgroundColor) {
     renderengine::DisplaySettings settings;
+    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     settings.physicalDisplay = fullscreenRect();
     settings.clip = fullscreenRect();
 
@@ -988,6 +1018,7 @@
 
     // add background layer
     renderengine::LayerSettings bgLayer;
+    bgLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     bgLayer.geometry.boundaries = fullscreenRect().toFloatRect();
     ColorSourceVariant::fillColor(bgLayer, backgroundColor.r / 255.0f, backgroundColor.g / 255.0f,
                                   backgroundColor.b / 255.0f, this);
@@ -996,6 +1027,7 @@
 
     // add shadow layer
     renderengine::LayerSettings shadowLayer;
+    shadowLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     shadowLayer.geometry.boundaries = castingLayer.geometry.boundaries;
     shadowLayer.alpha = castingLayer.alpha;
     shadowLayer.shadow = shadow;
@@ -1003,6 +1035,7 @@
 
     // add layer casting the shadow
     renderengine::LayerSettings layer = castingLayer;
+    layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     SourceVariant::fillColor(layer, casterColor.r / 255.0f, casterColor.g / 255.0f,
                              casterColor.b / 255.0f, this);
     layers.push_back(&layer);
@@ -1010,25 +1043,39 @@
     invokeDraw(settings, layers, mBuffer);
 }
 
-TEST_F(RenderEngineTest, drawLayers_noLayersToDraw) {
+INSTANTIATE_TEST_SUITE_P(PerRenderEngineType, RenderEngineTest,
+                         testing::Values(std::make_shared<GLESRenderEngineFactory>(),
+                                         std::make_shared<GLESCMRenderEngineFactory>()));
+
+TEST_P(RenderEngineTest, drawLayers_noLayersToDraw) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
     drawEmptyLayers();
 }
 
-TEST_F(RenderEngineTest, drawLayers_nullOutputBuffer) {
+TEST_P(RenderEngineTest, drawLayers_nullOutputBuffer) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     renderengine::DisplaySettings settings;
+    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     std::vector<const renderengine::LayerSettings*> layers;
     renderengine::LayerSettings layer;
     layer.geometry.boundaries = fullscreenRect().toFloatRect();
     BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
     layers.push_back(&layer);
     base::unique_fd fence;
-    status_t status = sRE->drawLayers(settings, layers, nullptr, true, base::unique_fd(), &fence);
+    status_t status = mRE->drawLayers(settings, layers, nullptr, true, base::unique_fd(), &fence);
 
     ASSERT_EQ(BAD_VALUE, status);
 }
 
-TEST_F(RenderEngineTest, drawLayers_nullOutputFence) {
+TEST_P(RenderEngineTest, drawLayers_nullOutputFence) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     renderengine::DisplaySettings settings;
+    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     settings.physicalDisplay = fullscreenRect();
     settings.clip = fullscreenRect();
 
@@ -1039,14 +1086,18 @@
     layer.alpha = 1.0;
     layers.push_back(&layer);
 
-    status_t status = sRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), nullptr);
-    sCurrentBuffer = mBuffer;
+    status_t status = mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), nullptr);
+    mCurrentBuffer = mBuffer;
     ASSERT_EQ(NO_ERROR, status);
     expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
 }
 
-TEST_F(RenderEngineTest, drawLayers_doesNotCacheFramebuffer) {
+TEST_P(RenderEngineTest, drawLayers_doesNotCacheFramebuffer) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     renderengine::DisplaySettings settings;
+    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     settings.physicalDisplay = fullscreenRect();
     settings.clip = fullscreenRect();
 
@@ -1057,211 +1108,341 @@
     layer.alpha = 1.0;
     layers.push_back(&layer);
 
-    status_t status = sRE->drawLayers(settings, layers, mBuffer, false, base::unique_fd(), nullptr);
-    sCurrentBuffer = mBuffer;
+    status_t status = mRE->drawLayers(settings, layers, mBuffer, false, base::unique_fd(), nullptr);
+    mCurrentBuffer = mBuffer;
     ASSERT_EQ(NO_ERROR, status);
-    ASSERT_FALSE(sRE->isFramebufferImageCachedForTesting(mBuffer->getId()));
+    ASSERT_FALSE(mRE->isFramebufferImageCachedForTesting(mBuffer->getId()));
     expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_colorSource) {
+TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_colorSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillRedBuffer<ColorSourceVariant>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillGreenBuffer_colorSource) {
+TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_colorSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillGreenBuffer<ColorSourceVariant>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBlueBuffer_colorSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_colorSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillBlueBuffer<ColorSourceVariant>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillRedTransparentBuffer_colorSource) {
+TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_colorSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillRedTransparentBuffer<ColorSourceVariant>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_colorSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_colorSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillBufferPhysicalOffset<ColorSourceVariant>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_colorSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_colorSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillBufferCheckersRotate0<ColorSourceVariant>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_colorSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_colorSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillBufferCheckersRotate90<ColorSourceVariant>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_colorSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_colorSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillBufferCheckersRotate180<ColorSourceVariant>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_colorSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_colorSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillBufferCheckersRotate270<ColorSourceVariant>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBufferLayerTransform_colorSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_colorSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillBufferLayerTransform<ColorSourceVariant>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillBufferColorTransform<ColorSourceVariant>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransformCM_colorSource) {
-    fillBufferColorTransformCM<ColorSourceVariant>();
-}
+TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
 
-TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) {
     fillBufferWithRoundedCorners<ColorSourceVariant>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_colorSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_colorSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillBufferAndBlurBackground<ColorSourceVariant>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_overlayCorners_colorSource) {
+TEST_P(RenderEngineTest, drawLayers_overlayCorners_colorSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     overlayCorners<ColorSourceVariant>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_opaqueBufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_opaqueBufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillRedBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillGreenBuffer_opaqueBufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_opaqueBufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillGreenBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBlueBuffer_opaqueBufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_opaqueBufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillBlueBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillRedTransparentBuffer_opaqueBufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_opaqueBufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillRedTransparentBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_opaqueBufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_opaqueBufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillBufferPhysicalOffset<BufferSourceVariant<ForceOpaqueBufferVariant>>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_opaqueBufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_opaqueBufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillBufferCheckersRotate0<BufferSourceVariant<ForceOpaqueBufferVariant>>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_opaqueBufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_opaqueBufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillBufferCheckersRotate90<BufferSourceVariant<ForceOpaqueBufferVariant>>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_opaqueBufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_opaqueBufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillBufferCheckersRotate180<BufferSourceVariant<ForceOpaqueBufferVariant>>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_opaqueBufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_opaqueBufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillBufferCheckersRotate270<BufferSourceVariant<ForceOpaqueBufferVariant>>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBufferLayerTransform_opaqueBufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_opaqueBufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillBufferLayerTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillBufferColorTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransformCM_opaqueBufferSource) {
-    fillBufferColorTransformCM<BufferSourceVariant<ForceOpaqueBufferVariant>>();
-}
+TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
 
-TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) {
     fillBufferWithRoundedCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_opaqueBufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_opaqueBufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillBufferAndBlurBackground<BufferSourceVariant<ForceOpaqueBufferVariant>>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_overlayCorners_opaqueBufferSource) {
+TEST_P(RenderEngineTest, drawLayers_overlayCorners_opaqueBufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     overlayCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_bufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_bufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillRedBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillGreenBuffer_bufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_bufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillGreenBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBlueBuffer_bufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_bufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillBlueBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillRedTransparentBuffer_bufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_bufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillRedTransparentBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_bufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_bufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillBufferPhysicalOffset<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_bufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_bufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillBufferCheckersRotate0<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_bufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_bufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillBufferCheckersRotate90<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_bufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_bufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillBufferCheckersRotate180<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_bufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_bufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillBufferCheckersRotate270<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBufferLayerTransform_bufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_bufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillBufferLayerTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillBufferColorTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransformCM_bufferSource) {
-    fillBufferColorTransformCM<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
-}
+TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
 
-TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) {
     fillBufferWithRoundedCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_bufferSource) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_bufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillBufferAndBlurBackground<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_overlayCorners_bufferSource) {
+TEST_P(RenderEngineTest, drawLayers_overlayCorners_bufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     overlayCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBufferTextureTransform) {
+TEST_P(RenderEngineTest, drawLayers_fillBufferTextureTransform) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillBufferTextureTransform();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBuffer_premultipliesAlpha) {
+TEST_P(RenderEngineTest, drawLayers_fillBuffer_premultipliesAlpha) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillBufferWithPremultiplyAlpha();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillBuffer_withoutPremultiplyingAlpha) {
+TEST_P(RenderEngineTest, drawLayers_fillBuffer_withoutPremultiplyingAlpha) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     fillBufferWithoutPremultiplyAlpha();
 }
 
-TEST_F(RenderEngineTest, drawLayers_clearRegion) {
+TEST_P(RenderEngineTest, drawLayers_clearRegion) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     clearRegion();
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillsBufferAndCachesImages) {
+TEST_P(RenderEngineTest, drawLayers_fillsBufferAndCachesImages) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     renderengine::DisplaySettings settings;
+    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     settings.physicalDisplay = fullscreenRect();
     settings.clip = fullscreenRect();
 
@@ -1274,21 +1455,24 @@
     layers.push_back(&layer);
     invokeDraw(settings, layers, mBuffer);
     uint64_t bufferId = layer.source.buffer.buffer->getId();
-    EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
+    EXPECT_TRUE(mRE->isImageCachedForTesting(bufferId));
     std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier =
-            sRE->unbindExternalTextureBufferForTesting(bufferId);
+            mRE->unbindExternalTextureBufferForTesting(bufferId);
     std::lock_guard<std::mutex> lock(barrier->mutex);
     ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5),
                                             [&]() REQUIRES(barrier->mutex) {
                                                 return barrier->isOpen;
                                             }));
-    EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
+    EXPECT_FALSE(mRE->isImageCachedForTesting(bufferId));
     EXPECT_EQ(NO_ERROR, barrier->result);
 }
 
-TEST_F(RenderEngineTest, cacheExternalBuffer_withNullBuffer) {
+TEST_P(RenderEngineTest, cacheExternalBuffer_withNullBuffer) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier =
-            sRE->cacheExternalTextureBufferForTesting(nullptr);
+            mRE->cacheExternalTextureBufferForTesting(nullptr);
     std::lock_guard<std::mutex> lock(barrier->mutex);
     ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5),
                                             [&]() REQUIRES(barrier->mutex) {
@@ -1298,11 +1482,14 @@
     EXPECT_EQ(BAD_VALUE, barrier->result);
 }
 
-TEST_F(RenderEngineTest, cacheExternalBuffer_cachesImages) {
+TEST_P(RenderEngineTest, cacheExternalBuffer_cachesImages) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
     uint64_t bufferId = buf->getId();
     std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier =
-            sRE->cacheExternalTextureBufferForTesting(buf);
+            mRE->cacheExternalTextureBufferForTesting(buf);
     {
         std::lock_guard<std::mutex> lock(barrier->mutex);
         ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5),
@@ -1311,8 +1498,8 @@
                                                 }));
         EXPECT_EQ(NO_ERROR, barrier->result);
     }
-    EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
-    barrier = sRE->unbindExternalTextureBufferForTesting(bufferId);
+    EXPECT_TRUE(mRE->isImageCachedForTesting(bufferId));
+    barrier = mRE->unbindExternalTextureBufferForTesting(bufferId);
     {
         std::lock_guard<std::mutex> lock(barrier->mutex);
         ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5),
@@ -1321,10 +1508,13 @@
                                                 }));
         EXPECT_EQ(NO_ERROR, barrier->result);
     }
-    EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
+    EXPECT_FALSE(mRE->isImageCachedForTesting(bufferId));
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) {
+TEST_P(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     const ubyte4 casterColor(255, 0, 0, 255);
     const ubyte4 backgroundColor(255, 255, 255, 255);
     const float shadowLength = 5.0f;
@@ -1341,13 +1531,17 @@
     expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) {
+TEST_P(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     const ubyte4 casterColor(255, 0, 0, 255);
     const ubyte4 backgroundColor(255, 255, 255, 255);
     const float shadowLength = 5.0f;
     Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
     casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
     renderengine::LayerSettings castingLayer;
+    castingLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     castingLayer.geometry.boundaries = casterBounds.toFloatRect();
     castingLayer.alpha = 1.0f;
     renderengine::ShadowSettings settings =
@@ -1358,13 +1552,17 @@
     expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) {
+TEST_P(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     const ubyte4 casterColor(255, 0, 0, 255);
     const ubyte4 backgroundColor(255, 255, 255, 255);
     const float shadowLength = 5.0f;
     Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
     casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
     renderengine::LayerSettings castingLayer;
+    castingLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     castingLayer.geometry.boundaries = casterBounds.toFloatRect();
     castingLayer.alpha = 1.0f;
     renderengine::ShadowSettings settings =
@@ -1376,7 +1574,10 @@
     expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) {
+TEST_P(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     const ubyte4 casterColor(255, 0, 0, 255);
     const ubyte4 backgroundColor(255, 255, 255, 255);
     const float shadowLength = 5.0f;
@@ -1396,7 +1597,10 @@
     expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
 }
 
-TEST_F(RenderEngineTest, drawLayers_fillShadow_translucentCasterWithAlpha) {
+TEST_P(RenderEngineTest, drawLayers_fillShadow_translucentCasterWithAlpha) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     const ubyte4 casterColor(255, 0, 0, 255);
     const ubyte4 backgroundColor(255, 255, 255, 255);
     const float shadowLength = 5.0f;
@@ -1421,10 +1625,14 @@
                       backgroundColor.a);
 }
 
-TEST_F(RenderEngineTest, cleanupPostRender_cleansUpOnce) {
+TEST_P(RenderEngineTest, cleanupPostRender_cleansUpOnce) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     renderengine::DisplaySettings settings;
     settings.physicalDisplay = fullscreenRect();
     settings.clip = fullscreenRect();
+    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
 
     std::vector<const renderengine::LayerSettings*> layers;
     renderengine::LayerSettings layer;
@@ -1434,23 +1642,27 @@
     layers.push_back(&layer);
 
     base::unique_fd fenceOne;
-    sRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fenceOne);
+    mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fenceOne);
     base::unique_fd fenceTwo;
-    sRE->drawLayers(settings, layers, mBuffer, true, std::move(fenceOne), &fenceTwo);
+    mRE->drawLayers(settings, layers, mBuffer, true, std::move(fenceOne), &fenceTwo);
 
     const int fd = fenceTwo.get();
     if (fd >= 0) {
         sync_wait(fd, -1);
     }
     // Only cleanup the first time.
-    EXPECT_TRUE(sRE->cleanupPostRender(
+    EXPECT_TRUE(mRE->cleanupPostRender(
             renderengine::RenderEngine::CleanupMode::CLEAN_OUTPUT_RESOURCES));
-    EXPECT_FALSE(sRE->cleanupPostRender(
+    EXPECT_FALSE(mRE->cleanupPostRender(
             renderengine::RenderEngine::CleanupMode::CLEAN_OUTPUT_RESOURCES));
 }
 
-TEST_F(RenderEngineTest, cleanupPostRender_whenCleaningAll_replacesTextureMemory) {
+TEST_P(RenderEngineTest, cleanupPostRender_whenCleaningAll_replacesTextureMemory) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
     renderengine::DisplaySettings settings;
+    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     settings.physicalDisplay = fullscreenRect();
     settings.clip = fullscreenRect();
 
@@ -1462,7 +1674,7 @@
     layers.push_back(&layer);
 
     base::unique_fd fence;
-    sRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fence);
+    mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fence);
 
     const int fd = fence.get();
     if (fd >= 0) {
@@ -1471,15 +1683,15 @@
 
     uint64_t bufferId = layer.source.buffer.buffer->getId();
     uint32_t texName = layer.source.buffer.textureName;
-    EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
-    EXPECT_EQ(bufferId, sRE->getBufferIdForTextureNameForTesting(texName));
+    EXPECT_TRUE(mRE->isImageCachedForTesting(bufferId));
+    EXPECT_EQ(bufferId, mRE->getBufferIdForTextureNameForTesting(texName));
 
-    EXPECT_TRUE(sRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL));
+    EXPECT_TRUE(mRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL));
 
     // Now check that our view of memory is good.
-    EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
-    EXPECT_EQ(std::nullopt, sRE->getBufferIdForTextureNameForTesting(bufferId));
-    EXPECT_TRUE(sRE->isTextureNameKnownForTesting(texName));
+    EXPECT_FALSE(mRE->isImageCachedForTesting(bufferId));
+    EXPECT_EQ(std::nullopt, mRE->getBufferIdForTextureNameForTesting(bufferId));
+    EXPECT_TRUE(mRE->isTextureNameKnownForTesting(texName));
 }
 
 } // namespace android
diff --git a/libs/vibrator/Android.bp b/libs/vibrator/Android.bp
index 1681fe2..49bc6bf 100644
--- a/libs/vibrator/Android.bp
+++ b/libs/vibrator/Android.bp
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-cc_library_shared {
+cc_library {
     name: "libvibrator",
     vendor_available: true,
     double_loadable: true,
@@ -47,4 +47,11 @@
     ],
 
     export_include_dirs: ["include"],
+
+    host_supported: true,
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
 }
diff --git a/libs/vibrator/fuzzer/Android.bp b/libs/vibrator/fuzzer/Android.bp
new file mode 100644
index 0000000..8020151
--- /dev/null
+++ b/libs/vibrator/fuzzer/Android.bp
@@ -0,0 +1,43 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *****************************************************************************
+ */
+
+cc_fuzz {
+    name: "vibrator_fuzzer",
+
+    host_supported: true,
+
+    srcs: [
+        "vibrator_fuzzer.cpp",
+    ],
+
+    static_libs: [
+        "liblog",
+        "libvibrator",
+    ],
+
+    shared_libs: [
+        "libbinder",
+        "libbase",
+        "libutils",
+    ],
+
+    fuzz_config: {
+        componentid: 155276,
+    },
+}
diff --git a/libs/vibrator/fuzzer/README.md b/libs/vibrator/fuzzer/README.md
new file mode 100644
index 0000000..43eb2d2
--- /dev/null
+++ b/libs/vibrator/fuzzer/README.md
@@ -0,0 +1,65 @@
+# Fuzzer for libvibrator
+
+## Plugin Design Considerations
+This fuzzer fuzzes native code present in libvibrator and does not cover the Java implementation ExternalVibration
+The fuzzer plugin is designed based on the understanding of the
+library and tries to achieve the following:
+
+##### Maximize code coverage
+The configuration parameters are not hardcoded, but instead selected based on
+incoming data. This ensures more code paths are reached by the fuzzer.
+
+libvibrator supports the following parameters:
+1. Uid (parameter name: `uid`)
+2. Package Name (parameter name: `pkg`)
+3. Audio Content Type (parameter name: `content_type`)
+4. Audio Usage (parameter name: `usage`)
+5. Audio Source (parameter name: `source`)
+6. Audio flags (parameter name: `flags`)
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+| `uid` | `INT32_MIN` to `INT32_MAX` | Value obtained from FuzzedDataProvider |
+| `pkg`   | Any std::string value | Value obtained from FuzzedDataProvider |
+| `content_type`   | 0.`AUDIO_CONTENT_TYPE_UNKNOWN` 1.`AUDIO_CONTENT_TYPE_SPEECH` 2.`AUDIO_CONTENT_TYPE_MUSIC` 3.`AUDIO_CONTENT_TYPE_MOVIE` 4.`AUDIO_CONTENT_TYPE_SONIFICATION`| Value obtained from FuzzedDataProvider in the range 0 to 4|
+| `usage`   | 0.`AUDIO_USAGE_UNKNOWN` 1.`AUDIO_USAGE_MEDIA` 2.`AUDIO_USAGE_VOICE_COMMUNICATION` 3.`AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING` 4.`AUDIO_USAGE_ALARM` 5.`AUDIO_USAGE_NOTIFICATION` 6.`AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE`  7.`AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST` 8.`AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT` 9.`AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED` 10.`AUDIO_USAGE_NOTIFICATION_EVENT` 11.`AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY` 12.`AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE` 13.`AUDIO_USAGE_ASSISTANCE_SONIFICATION` 14.`AUDIO_USAGE_GAME` 15.`AUDIO_USAGE_VIRTUAL_SOURCE` 16.`AUDIO_USAGE_ASSISTANT` 17.`AUDIO_USAGE_CALL_ASSISTANT` 18.`AUDIO_USAGE_EMERGENCY` 19.`AUDIO_USAGE_SAFETY` 20.`AUDIO_USAGE_VEHICLE_STATUS` 21.`AUDIO_USAGE_ANNOUNCEMENT`| Value obtained from FuzzedDataProvider in the range 0 to 21|
+| `source`   |  0.`AUDIO_SOURCE_DEFAULT` 1.`AUDIO_SOURCE_MIC` 2.`AUDIO_SOURCE_VOICE_UPLINK` 3.`AUDIO_SOURCE_VOICE_DOWNLINK` 4.`AUDIO_SOURCE_VOICE_CALL` 5.`AUDIO_SOURCE_CAMCORDER` 6.`AUDIO_SOURCE_VOICE_RECOGNITION` 7.`AUDIO_SOURCE_VOICE_COMMUNICATION` 8.`AUDIO_SOURCE_REMOTE_SUBMIX` 9.`AUDIO_SOURCE_UNPROCESSED` 10.`AUDIO_SOURCE_VOICE_PERFORMANCE` 11.`AUDIO_SOURCE_ECHO_REFERENCE` 12.`AUDIO_SOURCE_FM_TUNER` | Value obtained from FuzzedDataProvider in the range 0 to 12 |
+| `flags`   | `UINT32_MIN` to `UINT32_MAX` | Value obtained from FuzzedDataProvider |
+
+This also ensures that the plugin is always deterministic for any given input.
+
+##### Maximize utilization of input data
+The plugin tolerates any kind of input (empty, huge,
+malformed, etc) and doesn't `exit()` on any input and thereby increasing the
+chance of identifying vulnerabilities.
+
+## Build
+
+This describes steps to build vibrator_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+  $ mm -j$(nproc) vibrator_fuzzer
+```
+
+#### Steps to run
+Create a directory CORPUS_DIR and copy some files to that folder
+Push this directory to device.
+
+To run on device
+```
+  $ adb sync data
+  $ adb shell /data/fuzz/arm64/vibrator_fuzzer/vibrator_fuzzer CORPUS_DIR
+```
+
+To run on host
+```
+  $ $ANDROID_HOST_OUT/fuzz/x86_64/vibrator_fuzzer/vibrator_fuzzer CORPUS_DIR
+```
+
+## References:
+ * http://llvm.org/docs/LibFuzzer.html
+ * https://github.com/google/oss-fuzz
diff --git a/libs/vibrator/fuzzer/vibrator_fuzzer.cpp b/libs/vibrator/fuzzer/vibrator_fuzzer.cpp
new file mode 100644
index 0000000..68b3ca6
--- /dev/null
+++ b/libs/vibrator/fuzzer/vibrator_fuzzer.cpp
@@ -0,0 +1,130 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *****************************************************************************
+ */
+
+#include <binder/Parcel.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <vibrator/ExternalVibration.h>
+
+using namespace android;
+
+constexpr size_t MAX_STRING_LENGTH = 100;
+constexpr audio_content_type_t AUDIO_CONTENT_TYPE[] = {AUDIO_CONTENT_TYPE_UNKNOWN,
+                                                       AUDIO_CONTENT_TYPE_SPEECH,
+                                                       AUDIO_CONTENT_TYPE_MUSIC,
+                                                       AUDIO_CONTENT_TYPE_MOVIE,
+                                                       AUDIO_CONTENT_TYPE_SONIFICATION};
+constexpr audio_usage_t AUDIO_USAGE[] = {
+        AUDIO_USAGE_UNKNOWN,
+        AUDIO_USAGE_MEDIA,
+        AUDIO_USAGE_VOICE_COMMUNICATION,
+        AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING,
+        AUDIO_USAGE_ALARM,
+        AUDIO_USAGE_NOTIFICATION,
+        AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE,
+        AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST,
+        AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT,
+        AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED,
+        AUDIO_USAGE_NOTIFICATION_EVENT,
+        AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY,
+        AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE,
+        AUDIO_USAGE_ASSISTANCE_SONIFICATION,
+        AUDIO_USAGE_GAME,
+        AUDIO_USAGE_VIRTUAL_SOURCE,
+        AUDIO_USAGE_ASSISTANT,
+        AUDIO_USAGE_CALL_ASSISTANT,
+        AUDIO_USAGE_EMERGENCY,
+        AUDIO_USAGE_SAFETY,
+        AUDIO_USAGE_VEHICLE_STATUS,
+        AUDIO_USAGE_ANNOUNCEMENT,
+};
+constexpr audio_source_t AUDIO_SOURCE[] = {
+        AUDIO_SOURCE_DEFAULT,           AUDIO_SOURCE_MIC,
+        AUDIO_SOURCE_VOICE_UPLINK,      AUDIO_SOURCE_VOICE_DOWNLINK,
+        AUDIO_SOURCE_VOICE_CALL,        AUDIO_SOURCE_CAMCORDER,
+        AUDIO_SOURCE_VOICE_RECOGNITION, AUDIO_SOURCE_VOICE_COMMUNICATION,
+        AUDIO_SOURCE_REMOTE_SUBMIX,     AUDIO_SOURCE_UNPROCESSED,
+        AUDIO_SOURCE_VOICE_PERFORMANCE, AUDIO_SOURCE_ECHO_REFERENCE,
+        AUDIO_SOURCE_FM_TUNER,
+};
+constexpr size_t NUM_AUDIO_CONTENT_TYPE = std::size(AUDIO_CONTENT_TYPE);
+constexpr size_t NUM_AUDIO_USAGE = std::size(AUDIO_USAGE);
+constexpr size_t NUM_AUDIO_SOURCE = std::size(AUDIO_SOURCE);
+
+class TestVibrationController : public os::IExternalVibrationController {
+public:
+    explicit TestVibrationController() {}
+    IBinder *onAsBinder() override { return nullptr; }
+    binder::Status mute(/*out*/ bool *ret) override {
+        *ret = false;
+        return binder::Status::ok();
+    };
+    binder::Status unmute(/*out*/ bool *ret) override {
+        *ret = false;
+        return binder::Status::ok();
+    };
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+    if (size < 1) {
+        return 0;
+    }
+    FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+    // Initialize the parameters using FuzzedDataProvider
+    int32_t uid = fdp.ConsumeIntegral<int32_t>();
+    std::string pkg = fdp.ConsumeRandomLengthString(MAX_STRING_LENGTH);
+    audio_attributes_t attributes;
+    attributes.content_type =
+            AUDIO_CONTENT_TYPE[fdp.ConsumeIntegralInRange<uint32_t>(0, NUM_AUDIO_CONTENT_TYPE - 1)];
+    attributes.usage = AUDIO_USAGE[fdp.ConsumeIntegralInRange<uint32_t>(0, NUM_AUDIO_USAGE - 1)];
+    attributes.source = AUDIO_SOURCE[fdp.ConsumeIntegralInRange<uint32_t>(0, NUM_AUDIO_SOURCE - 1)];
+    attributes.flags = static_cast<audio_flags_mask_t>(fdp.ConsumeIntegral<uint32_t>());
+
+    // Create an instance of TestVibrationController
+    sp<TestVibrationController> vibrationController = new TestVibrationController();
+    if (!vibrationController) {
+        return 0;
+    }
+
+    // Set all the parameters in the constructor call
+    sp<os::ExternalVibration> extVibration =
+            new os::ExternalVibration(uid, pkg, attributes, vibrationController);
+    if (!extVibration) {
+        return 0;
+    }
+
+    // Get all the parameters that were previously set
+    extVibration->getUid();
+    extVibration->getPackage();
+    extVibration->getAudioAttributes();
+    extVibration->getController();
+
+    // Set the parameters in a Parcel object and send it to libvibrator
+    // This parcel shall be read by libvibrator
+    Parcel parcel;
+    parcel.writeInt32(uid);
+    parcel.writeString16(String16(pkg.c_str()));
+    parcel.writeStrongBinder(IInterface::asBinder(vibrationController));
+    parcel.setDataPosition(0);
+    extVibration->readFromParcel(&parcel);
+
+    // Send a Parcel to libvibrator
+    // Parameters shall be written to this parcel by libvibrator
+    extVibration->writeToParcel(&parcel);
+    return 0;
+}
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 17f37c3..b620e2d 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -745,12 +745,17 @@
         mOrientedRanges.clear();
     }
 
-    // Create pointer controller if needed.
+    // Create pointer controller if needed, and keep it around if Pointer Capture is enabled to
+    // preserve the cursor position.
     if (mDeviceMode == DeviceMode::POINTER ||
-        (mDeviceMode == DeviceMode::DIRECT && mConfig.showTouches)) {
+        (mDeviceMode == DeviceMode::DIRECT && mConfig.showTouches) ||
+        (mParameters.deviceType == Parameters::DeviceType::POINTER && mConfig.pointerCapture)) {
         if (mPointerController == nullptr) {
             mPointerController = getContext()->getPointerController(getDeviceId());
         }
+        if (mConfig.pointerCapture) {
+            mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
+        }
     } else {
         mPointerController.reset();
     }
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index bf5be47..b63e8c8 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -45,6 +45,9 @@
 #include "FrameTimeline.h"
 #include "HwcStrongTypes.h"
 
+#undef LOG_TAG
+#define LOG_TAG "EventThread"
+
 using namespace std::chrono_literals;
 
 namespace android {
@@ -123,14 +126,36 @@
     return event;
 }
 
+DisplayEventReceiver::Event makeFrameRateOverrideEvent(PhysicalDisplayId displayId,
+                                                       FrameRateOverride frameRateOverride) {
+    return DisplayEventReceiver::Event{
+            .header =
+                    DisplayEventReceiver::Event::Header{
+                            .type = DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE,
+                            .displayId = displayId,
+                            .timestamp = systemTime(),
+                    },
+            .frameRateOverride = frameRateOverride,
+    };
+}
+
+DisplayEventReceiver::Event makeFrameRateOverrideFlushEvent(PhysicalDisplayId displayId) {
+    return DisplayEventReceiver::Event{
+            .header = DisplayEventReceiver::Event::Header{
+                    .type = DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH,
+                    .displayId = displayId,
+                    .timestamp = systemTime(),
+            }};
+}
+
 } // namespace
 
-EventThreadConnection::EventThreadConnection(EventThread* eventThread, uid_t callingUid,
-                                             ResyncCallback resyncCallback,
-                                             ISurfaceComposer::ConfigChanged configChanged)
+EventThreadConnection::EventThreadConnection(
+        EventThread* eventThread, uid_t callingUid, ResyncCallback resyncCallback,
+        ISurfaceComposer::EventRegistrationFlags eventRegistration)
       : resyncCallback(std::move(resyncCallback)),
-        mConfigChanged(configChanged),
         mOwnerUid(callingUid),
+        mEventRegistration(eventRegistration),
         mEventThread(eventThread),
         mChannel(gui::BitTube::DefaultSize) {}
 
@@ -161,8 +186,25 @@
 }
 
 status_t EventThreadConnection::postEvent(const DisplayEventReceiver::Event& event) {
-    ssize_t size = DisplayEventReceiver::sendEvents(&mChannel, &event, 1);
-    return size < 0 ? status_t(size) : status_t(NO_ERROR);
+    constexpr auto toStatus = [](ssize_t size) {
+        return size < 0 ? status_t(size) : status_t(NO_ERROR);
+    };
+
+    if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE ||
+        event.header.type == DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH) {
+        mPendingEvents.emplace_back(event);
+        if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE) {
+            return status_t(NO_ERROR);
+        }
+
+        auto size = DisplayEventReceiver::sendEvents(&mChannel, mPendingEvents.data(),
+                                                     mPendingEvents.size());
+        mPendingEvents.clear();
+        return toStatus(size);
+    }
+
+    auto size = DisplayEventReceiver::sendEvents(&mChannel, &event, 1);
+    return toStatus(size);
 }
 
 // ---------------------------------------------------------------------------
@@ -220,10 +262,11 @@
 }
 
 sp<EventThreadConnection> EventThread::createEventConnection(
-        ResyncCallback resyncCallback, ISurfaceComposer::ConfigChanged configChanged) const {
+        ResyncCallback resyncCallback,
+        ISurfaceComposer::EventRegistrationFlags eventRegistration) const {
     return new EventThreadConnection(const_cast<EventThread*>(this),
                                      IPCThreadState::self()->getCallingUid(),
-                                     std::move(resyncCallback), configChanged);
+                                     std::move(resyncCallback), eventRegistration);
 }
 
 status_t EventThread::registerDisplayEventConnection(const sp<EventThreadConnection>& connection) {
@@ -333,6 +376,18 @@
     mCondition.notify_all();
 }
 
+void EventThread::onFrameRateOverridesChanged(PhysicalDisplayId displayId,
+                                              std::vector<FrameRateOverride> overrides) {
+    std::lock_guard<std::mutex> lock(mMutex);
+
+    for (auto frameRateOverride : overrides) {
+        mPendingEvents.push_back(makeFrameRateOverrideEvent(displayId, frameRateOverride));
+    }
+    mPendingEvents.push_back(makeFrameRateOverrideFlushEvent(displayId));
+
+    mCondition.notify_all();
+}
+
 size_t EventThread::getEventThreadConnectionCount() {
     std::lock_guard<std::mutex> lock(mMutex);
     return mDisplayEventConnections.size();
@@ -459,7 +514,8 @@
             return true;
 
         case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED: {
-            return connection->mConfigChanged == ISurfaceComposer::eConfigChangedDispatch;
+            return connection->mEventRegistration.test(
+                    ISurfaceComposer::EventRegistration::configChanged);
         }
 
         case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
@@ -488,6 +544,12 @@
                     return event.vsync.count % vsyncPeriod(connection->vsyncRequest) == 0;
             }
 
+        case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE:
+            [[fallthrough]];
+        case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH:
+            return connection->mEventRegistration.test(
+                    ISurfaceComposer::EventRegistration::frameRateOverride);
+
         default:
             return false;
     }
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 2e2d989..e75b718 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -48,6 +48,7 @@
 // ---------------------------------------------------------------------------
 
 using ResyncCallback = std::function<void()>;
+using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
 
 enum class VSyncRequest {
     None = -2,
@@ -82,7 +83,7 @@
 class EventThreadConnection : public BnDisplayEventConnection {
 public:
     EventThreadConnection(EventThread*, uid_t callingUid, ResyncCallback,
-                          ISurfaceComposer::ConfigChanged configChanged);
+                          ISurfaceComposer::EventRegistrationFlags eventRegistration = {});
     virtual ~EventThreadConnection();
 
     virtual status_t postEvent(const DisplayEventReceiver::Event& event);
@@ -95,15 +96,15 @@
     const ResyncCallback resyncCallback;
 
     VSyncRequest vsyncRequest = VSyncRequest::None;
-    const ISurfaceComposer::ConfigChanged mConfigChanged =
-            ISurfaceComposer::ConfigChanged::eConfigChangedSuppress;
-
     const uid_t mOwnerUid;
+    const ISurfaceComposer::EventRegistrationFlags mEventRegistration;
 
 private:
     virtual void onFirstRef();
     EventThread* const mEventThread;
     gui::BitTube mChannel;
+
+    std::vector<DisplayEventReceiver::Event> mPendingEvents;
 };
 
 class EventThread {
@@ -111,7 +112,8 @@
     virtual ~EventThread();
 
     virtual sp<EventThreadConnection> createEventConnection(
-            ResyncCallback, ISurfaceComposer::ConfigChanged configChanged) const = 0;
+            ResyncCallback,
+            ISurfaceComposer::EventRegistrationFlags eventRegistration = {}) const = 0;
 
     // called before the screen is turned off from main thread
     virtual void onScreenReleased() = 0;
@@ -125,6 +127,10 @@
     virtual void onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId,
                                  nsecs_t vsyncPeriod) = 0;
 
+    // called when SF updates the Frame Rate Override list
+    virtual void onFrameRateOverridesChanged(PhysicalDisplayId displayId,
+                                             std::vector<FrameRateOverride> overrides) = 0;
+
     virtual void dump(std::string& result) const = 0;
 
     virtual void setDuration(std::chrono::nanoseconds workDuration,
@@ -152,7 +158,8 @@
     ~EventThread();
 
     sp<EventThreadConnection> createEventConnection(
-            ResyncCallback, ISurfaceComposer::ConfigChanged configChanged) const override;
+            ResyncCallback,
+            ISurfaceComposer::EventRegistrationFlags eventRegistration = {}) const override;
 
     status_t registerDisplayEventConnection(const sp<EventThreadConnection>& connection) override;
     void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) override;
@@ -169,6 +176,9 @@
     void onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId,
                          nsecs_t vsyncPeriod) override;
 
+    void onFrameRateOverridesChanged(PhysicalDisplayId displayId,
+                                     std::vector<FrameRateOverride> overrides) override;
+
     void dump(std::string& result) const override;
 
     void setDuration(std::chrono::nanoseconds workDuration,
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 2559eb4..83fa20e 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -650,21 +650,20 @@
     return RefreshRateConfigs::KernelIdleTimerAction::TurnOn;
 }
 
-void RefreshRateConfigs::setPreferredRefreshRateForUid(uid_t uid, float refreshRateHz) {
-    if (refreshRateHz > 0 && refreshRateHz < 1) {
+void RefreshRateConfigs::setPreferredRefreshRateForUid(FrameRateOverride frameRateOverride) {
+    if (frameRateOverride.frameRateHz > 0 && frameRateOverride.frameRateHz < 1) {
         return;
     }
 
     std::lock_guard lock(mLock);
-    if (refreshRateHz != 0) {
-        mPreferredRefreshRateForUid[uid] = refreshRateHz;
+    if (frameRateOverride.frameRateHz != 0) {
+        mPreferredRefreshRateForUid[frameRateOverride.uid] = frameRateOverride.frameRateHz;
     } else {
-        mPreferredRefreshRateForUid.erase(uid);
+        mPreferredRefreshRateForUid.erase(frameRateOverride.uid);
     }
 }
 
 int RefreshRateConfigs::getRefreshRateDividerForUid(uid_t uid) const {
-    constexpr float kThreshold = 0.1f;
     std::lock_guard lock(mLock);
 
     const auto iter = mPreferredRefreshRateForUid.find(uid);
@@ -672,6 +671,9 @@
         return 1;
     }
 
+    // This calculation needs to be in sync with the java code
+    // in DisplayManagerService.getDisplayInfoForFrameRateOverride
+    constexpr float kThreshold = 0.1f;
     const auto refreshRateHz = iter->second;
     const auto numPeriods = mCurrentRefreshRate->getFps() / refreshRateHz;
     const auto numPeriodsRounded = std::round(numPeriods);
@@ -679,7 +681,19 @@
         return 1;
     }
 
-    return static_cast<int>(numPeriods);
+    return static_cast<int>(numPeriodsRounded);
+}
+
+std::vector<FrameRateOverride> RefreshRateConfigs::getFrameRateOverrides() {
+    std::lock_guard lock(mLock);
+    std::vector<FrameRateOverride> overrides;
+    overrides.reserve(mPreferredRefreshRateForUid.size());
+
+    for (const auto [uid, frameRate] : mPreferredRefreshRateForUid) {
+        overrides.emplace_back(FrameRateOverride{uid, frameRate});
+    }
+
+    return overrides;
 }
 
 void RefreshRateConfigs::dump(std::string& result) const {
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index a5fadbf..8ebbd74 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <android-base/stringprintf.h>
+#include <gui/DisplayEventReceiver.h>
 
 #include <algorithm>
 #include <numeric>
@@ -40,6 +41,8 @@
     return static_cast<RefreshRateConfigEvent>(static_cast<T>(lhs) | static_cast<T>(rhs));
 }
 
+using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
+
 /**
  * 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
@@ -313,14 +316,17 @@
     KernelIdleTimerAction getIdleTimerAction() const;
 
     // Stores the preferred refresh rate that an app should run at.
-    // refreshRate == 0 means no preference.
-    void setPreferredRefreshRateForUid(uid_t, float refreshRateHz) EXCLUDES(mLock);
+    // FrameRateOverride.refreshRateHz == 0 means no preference.
+    void setPreferredRefreshRateForUid(FrameRateOverride) EXCLUDES(mLock);
 
     // Returns a divider for the current refresh rate
     int getRefreshRateDividerForUid(uid_t) const EXCLUDES(mLock);
 
     void dump(std::string& result) const EXCLUDES(mLock);
 
+    // Returns the current frame rate overrides
+    std::vector<FrameRateOverride> getFrameRateOverrides() EXCLUDES(mLock);
+
 private:
     friend class RefreshRateConfigsTest;
 
@@ -378,6 +384,8 @@
     Policy mDisplayManagerPolicy GUARDED_BY(mLock);
     std::optional<Policy> mOverridePolicy GUARDED_BY(mLock);
 
+    // A mapping between a UID and a preferred refresh rate that this app would
+    // run at.
     std::unordered_map<uid_t, float> mPreferredRefreshRateForUid GUARDED_BY(mLock);
 
     // The min and max refresh rates supported by the device.
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index a9e08fc..52bf483 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -233,8 +233,7 @@
     const ConnectionHandle handle = ConnectionHandle{mNextConnectionHandleId++};
     ALOGV("Creating a connection handle with ID %" PRIuPTR, handle.id);
 
-    auto connection =
-            createConnectionInternal(eventThread.get(), ISurfaceComposer::eConfigChangedSuppress);
+    auto connection = createConnectionInternal(eventThread.get());
 
     std::lock_guard<std::mutex> lock(mConnectionsLock);
     mConnections.emplace(handle, Connection{connection, std::move(eventThread)});
@@ -242,15 +241,15 @@
 }
 
 sp<EventThreadConnection> Scheduler::createConnectionInternal(
-        EventThread* eventThread, ISurfaceComposer::ConfigChanged configChanged) {
-    return eventThread->createEventConnection([&] { resync(); }, configChanged);
+        EventThread* eventThread, ISurfaceComposer::EventRegistrationFlags eventRegistration) {
+    return eventThread->createEventConnection([&] { resync(); }, eventRegistration);
 }
 
 sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection(
-        ConnectionHandle handle, ISurfaceComposer::ConfigChanged configChanged) {
+        ConnectionHandle handle, ISurfaceComposer::EventRegistrationFlags eventRegistration) {
     std::lock_guard<std::mutex> lock(mConnectionsLock);
     RETURN_IF_INVALID_HANDLE(handle, nullptr);
-    return createConnectionInternal(mConnections[handle].thread.get(), configChanged);
+    return createConnectionInternal(mConnections[handle].thread.get(), eventRegistration);
 }
 
 sp<EventThreadConnection> Scheduler::getEventConnection(ConnectionHandle handle) {
@@ -291,6 +290,17 @@
     thread->onScreenReleased();
 }
 
+void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
+                                            std::vector<FrameRateOverride> overrides) {
+    android::EventThread* thread;
+    {
+        std::lock_guard<std::mutex> lock(mConnectionsLock);
+        RETURN_IF_INVALID_HANDLE(handle);
+        thread = mConnections[handle].thread.get();
+    }
+    thread->onFrameRateOverridesChanged(displayId, std::move(overrides));
+}
+
 void Scheduler::onPrimaryDisplayConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
                                               HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
     std::lock_guard<std::mutex> lock(mFeatureStateLock);
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 936d96a..f16e1f9 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -79,8 +79,8 @@
                                       std::chrono::nanoseconds readyDuration,
                                       impl::EventThread::InterceptVSyncsCallback);
 
-    sp<IDisplayEventConnection> createDisplayEventConnection(ConnectionHandle,
-                                                             ISurfaceComposer::ConfigChanged);
+    sp<IDisplayEventConnection> createDisplayEventConnection(
+            ConnectionHandle, ISurfaceComposer::EventRegistrationFlags eventRegistration = {});
 
     sp<EventThreadConnection> getEventConnection(ConnectionHandle);
 
@@ -93,6 +93,9 @@
     void onScreenAcquired(ConnectionHandle);
     void onScreenReleased(ConnectionHandle);
 
+    void onFrameRateOverridesChanged(ConnectionHandle, PhysicalDisplayId,
+                                     std::vector<FrameRateOverride>);
+
     // Modifies work duration in the event thread.
     void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration,
                      std::chrono::nanoseconds readyDuration);
@@ -200,8 +203,8 @@
 
     // Create a connection on the given EventThread.
     ConnectionHandle createConnection(std::unique_ptr<EventThread>);
-    sp<EventThreadConnection> createConnectionInternal(EventThread*,
-                                                       ISurfaceComposer::ConfigChanged);
+    sp<EventThreadConnection> createConnectionInternal(
+            EventThread*, ISurfaceComposer::EventRegistrationFlags eventRegistration = {});
 
     // Update feature state machine to given state when corresponding timer resets or expires.
     void kernelIdleTimerCallback(TimerState);
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index a28ed92..a6f9372 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -222,10 +222,10 @@
 
 /*
  * Returns whether a given vsync timestamp is in phase with a vsync divider.
- * For example, if the vsync timestamps are (0,16,32,48):
- * isVSyncInPhase(0, 2) = true
- * isVSyncInPhase(16, 2) = false
- * isVSyncInPhase(32, 2) = true
+ * For example, if the vsync timestamps are (16,32,48,64):
+ * isVSyncInPhase(16, 2) = true
+ * isVSyncInPhase(32, 2) = false
+ * isVSyncInPhase(48, 2) = true
  */
 bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, int divider) const {
     struct VsyncError {
@@ -235,11 +235,11 @@
         bool operator<(const VsyncError& other) const { return error < other.error; }
     };
 
-    std::lock_guard lock(mMutex);
-    if (divider <= 1) {
+    if (divider <= 1 || timePoint == 0) {
         return true;
     }
 
+    std::lock_guard lock(mMutex);
     const nsecs_t period = mRateMap[mIdealPeriod].slope;
     const nsecs_t justBeforeTimePoint = timePoint - period / 2;
     const nsecs_t dividedPeriod = mIdealPeriod / divider;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index dbfa881..576bd50 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1526,11 +1526,12 @@
 // ----------------------------------------------------------------------------
 
 sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
-        ISurfaceComposer::VsyncSource vsyncSource, ISurfaceComposer::ConfigChanged configChanged) {
+        ISurfaceComposer::VsyncSource vsyncSource,
+        ISurfaceComposer::EventRegistrationFlags eventRegistration) {
     const auto& handle =
             vsyncSource == eVsyncSourceSurfaceFlinger ? mSfConnectionHandle : mAppConnectionHandle;
 
-    return mScheduler->createDisplayEventConnection(handle, configChanged);
+    return mScheduler->createDisplayEventConnection(handle, eventRegistration);
 }
 
 void SurfaceFlinger::signalTransaction() {
@@ -4034,7 +4035,14 @@
                                                 sp<Layer>* outLayer) {
     LayerCreationArgs args(this, client, std::move(name), w, h, flags, std::move(metadata));
     args.textureName = getNewTexture();
-    sp<BufferStateLayer> layer = getFactory().createBufferStateLayer(args);
+    sp<BufferStateLayer> layer;
+    {
+        // TODO (b/173538294): Investigate why we need mStateLock here and above in
+        // createBufferQueue layer. Is it the renderengine::Image?
+        Mutex::Autolock lock(mStateLock);
+        layer = getFactory().createBufferStateLayer(args);
+
+    }
     *handle = layer->getHandle();
     *outLayer = layer;
 
@@ -5270,10 +5278,18 @@
                 return NO_ERROR;
             }
             case 1039: {
-                // The first parameter is the uid
-                n = data.readInt32();
-                const float refreshRateHz = data.readFloat();
-                mRefreshRateConfigs->setPreferredRefreshRateForUid(n, refreshRateHz);
+                PhysicalDisplayId displayId = [&]() {
+                    Mutex::Autolock lock(mStateLock);
+                    return getDefaultDisplayDeviceLocked()->getPhysicalId();
+                }();
+
+                auto inUid = static_cast<uid_t>(data.readInt32());
+                const auto refreshRate = data.readFloat();
+                mRefreshRateConfigs->setPreferredRefreshRateForUid(
+                        FrameRateOverride{inUid, refreshRate});
+                const auto mappings = mRefreshRateConfigs->getFrameRateOverrides();
+                mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId,
+                                                        std::move(mappings));
             }
                 return NO_ERROR;
         }
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 9666f14..a909ad8 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -530,8 +530,7 @@
     status_t getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) const override;
     sp<IDisplayEventConnection> createDisplayEventConnection(
             ISurfaceComposer::VsyncSource vsyncSource = eVsyncSourceApp,
-            ISurfaceComposer::ConfigChanged configChanged =
-                    ISurfaceComposer::eConfigChangedSuppress) override;
+            ISurfaceComposer::EventRegistrationFlags eventRegistration = {}) override;
     status_t captureDisplay(const DisplayCaptureArgs& args,
                             const sp<IScreenCaptureListener>& captureListener) override;
     status_t captureDisplay(uint64_t displayOrLayerStack,
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index 0a70f5c..31a5126 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -240,8 +240,9 @@
         mComposerClient = new SurfaceComposerClient;
         ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
 
-        mReceiver.reset(new DisplayEventReceiver(ISurfaceComposer::eVsyncSourceApp,
-                                                 ISurfaceComposer::eConfigChangedDispatch));
+        mReceiver.reset(
+                new DisplayEventReceiver(ISurfaceComposer::eVsyncSourceApp,
+                                         ISurfaceComposer::EventRegistration::configChanged));
         mLooper = new Looper(false);
         mLooper->addFd(mReceiver->getFd(), 0, ALOOPER_EVENT_INPUT, processDisplayEvents, this);
     }
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 0911712..be9d336 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -133,17 +133,13 @@
 
         EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
         EXPECT_CALL(*eventThread, createEventConnection(_, _))
-                .WillOnce(Return(
-                        new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
-                                                  ResyncCallback(),
-                                                  ISurfaceComposer::eConfigChangedSuppress)));
+                .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+                                                           ResyncCallback())));
 
         EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
         EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
-                .WillOnce(Return(
-                        new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
-                                                  ResyncCallback(),
-                                                  ISurfaceComposer::eConfigChangedSuppress)));
+                .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+                                                           ResyncCallback())));
 
         auto vsyncController = std::make_unique<mock::VsyncController>();
         auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index f0311bd..9069200 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -70,15 +70,13 @@
 void DisplayTransactionTest::injectMockScheduler() {
     EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_));
     EXPECT_CALL(*mEventThread, createEventConnection(_, _))
-            .WillOnce(Return(new EventThreadConnection(mEventThread, /*callingUid=*/0,
-                                                       ResyncCallback(),
-                                                       ISurfaceComposer::eConfigChangedSuppress)));
+            .WillOnce(Return(
+                    new EventThreadConnection(mEventThread, /*callingUid=*/0, ResyncCallback())));
 
     EXPECT_CALL(*mSFEventThread, registerDisplayEventConnection(_));
     EXPECT_CALL(*mSFEventThread, createEventConnection(_, _))
-            .WillOnce(Return(new EventThreadConnection(mSFEventThread, /*callingUid=*/0,
-                                                       ResyncCallback(),
-                                                       ISurfaceComposer::eConfigChangedSuppress)));
+            .WillOnce(Return(
+                    new EventThreadConnection(mSFEventThread, /*callingUid=*/0, ResyncCallback())));
 
     mFlinger.setupScheduler(std::unique_ptr<scheduler::VsyncController>(mVsyncController),
                             std::unique_ptr<scheduler::VSyncTracker>(mVSyncTracker),
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 3aafd45..ee56178 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -29,6 +29,7 @@
 using namespace std::chrono_literals;
 using namespace std::placeholders;
 
+using namespace android::flag_operators;
 using testing::_;
 using testing::Invoke;
 
@@ -61,9 +62,9 @@
     public:
         MockEventThreadConnection(impl::EventThread* eventThread, uid_t callingUid,
                                   ResyncCallback&& resyncCallback,
-                                  ISurfaceComposer::ConfigChanged configChanged)
+                                  ISurfaceComposer::EventRegistrationFlags eventRegistration)
               : EventThreadConnection(eventThread, callingUid, std::move(resyncCallback),
-                                      configChanged) {}
+                                      eventRegistration) {}
         MOCK_METHOD1(postEvent, status_t(const DisplayEventReceiver::Event& event));
     };
 
@@ -74,9 +75,10 @@
     ~EventThreadTest() override;
 
     void createThread(std::unique_ptr<VSyncSource>);
-    sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder,
-                                                   ISurfaceComposer::ConfigChanged configChanged,
-                                                   uid_t ownerUid = mConnectionUid);
+    sp<MockEventThreadConnection> createConnection(
+            ConnectionEventRecorder& recorder,
+            ISurfaceComposer::EventRegistrationFlags eventRegistration = {},
+            uid_t ownerUid = mConnectionUid);
 
     void expectVSyncSetEnabledCallReceived(bool expectedState);
     void expectVSyncSetDurationCallReceived(std::chrono::nanoseconds expectedDuration,
@@ -93,6 +95,8 @@
                                                       int32_t expectedConfigId,
                                                       nsecs_t expectedVsyncPeriod);
     void expectThrottleVsyncReceived(nsecs_t expectedTimestamp, uid_t);
+    void expectUidFrameRateMappingEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
+                                                            std::vector<FrameRateOverride>);
 
     AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder;
     AsyncCallRecorder<void (*)(VSyncSource::Callback*)> mVSyncSetCallbackCallRecorder;
@@ -133,10 +137,11 @@
 
     createThread(std::move(vsyncSource));
     mConnection = createConnection(mConnectionEventCallRecorder,
-                                   ISurfaceComposer::eConfigChangedDispatch);
-    mThrottledConnection =
-            createConnection(mThrottledConnectionEventCallRecorder,
-                             ISurfaceComposer::eConfigChangedDispatch, mThrottledConnectionUid);
+                                   ISurfaceComposer::EventRegistration::configChanged |
+                                           ISurfaceComposer::EventRegistration::frameRateOverride);
+    mThrottledConnection = createConnection(mThrottledConnectionEventCallRecorder,
+                                            ISurfaceComposer::EventRegistration::configChanged,
+                                            mThrottledConnectionUid);
 
     // A display must be connected for VSYNC events to be delivered.
     mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, true);
@@ -169,11 +174,11 @@
 }
 
 sp<EventThreadTest::MockEventThreadConnection> EventThreadTest::createConnection(
-        ConnectionEventRecorder& recorder, ISurfaceComposer::ConfigChanged configChanged,
-        uid_t ownerUid) {
+        ConnectionEventRecorder& recorder,
+        ISurfaceComposer::EventRegistrationFlags eventRegistration, uid_t ownerUid) {
     sp<MockEventThreadConnection> connection =
             new MockEventThreadConnection(mThread.get(), ownerUid,
-                                          mResyncCallRecorder.getInvocable(), configChanged);
+                                          mResyncCallRecorder.getInvocable(), eventRegistration);
     EXPECT_CALL(*connection, postEvent(_)).WillRepeatedly(Invoke(recorder.getInvocable()));
     return connection;
 }
@@ -254,6 +259,25 @@
     EXPECT_EQ(expectedVsyncPeriod, event.config.vsyncPeriod);
 }
 
+void EventThreadTest::expectUidFrameRateMappingEventReceivedByConnection(
+        PhysicalDisplayId expectedDisplayId, std::vector<FrameRateOverride> expectedOverrides) {
+    for (const auto [uid, frameRateHz] : expectedOverrides) {
+        auto args = mConnectionEventCallRecorder.waitForCall();
+        ASSERT_TRUE(args.has_value());
+        const auto& event = std::get<0>(args.value());
+        EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE, event.header.type);
+        EXPECT_EQ(expectedDisplayId, event.header.displayId);
+        EXPECT_EQ(uid, event.frameRateOverride.uid);
+        EXPECT_EQ(frameRateHz, event.frameRateOverride.frameRateHz);
+    }
+
+    auto args = mConnectionEventCallRecorder.waitForCall();
+    ASSERT_TRUE(args.has_value());
+    const auto& event = std::get<0>(args.value());
+    EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH, event.header.type);
+    EXPECT_EQ(expectedDisplayId, event.header.displayId);
+}
+
 namespace {
 
 /* ------------------------------------------------------------------------
@@ -313,9 +337,7 @@
 TEST_F(EventThreadTest, setVsyncRateZeroPostsNoVSyncEventsToThatConnection) {
     // Create a first connection, register it, and request a vsync rate of zero.
     ConnectionEventRecorder firstConnectionEventRecorder{0};
-    sp<MockEventThreadConnection> firstConnection =
-            createConnection(firstConnectionEventRecorder,
-                             ISurfaceComposer::eConfigChangedSuppress);
+    sp<MockEventThreadConnection> firstConnection = createConnection(firstConnectionEventRecorder);
     mThread->setVsyncRate(0, firstConnection);
 
     // By itself, this should not enable vsync events
@@ -325,8 +347,7 @@
     // However if there is another connection which wants events at a nonzero rate.....
     ConnectionEventRecorder secondConnectionEventRecorder{0};
     sp<MockEventThreadConnection> secondConnection =
-            createConnection(secondConnectionEventRecorder,
-                             ISurfaceComposer::eConfigChangedSuppress);
+            createConnection(secondConnectionEventRecorder);
     mThread->setVsyncRate(1, secondConnection);
 
     // EventThread should enable vsync callbacks.
@@ -418,9 +439,7 @@
 
 TEST_F(EventThreadTest, connectionsRemovedIfEventDeliveryError) {
     ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY};
-    sp<MockEventThreadConnection> errorConnection =
-            createConnection(errorConnectionEventRecorder,
-                             ISurfaceComposer::eConfigChangedSuppress);
+    sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
     mThread->setVsyncRate(1, errorConnection);
 
     // EventThread should enable vsync callbacks.
@@ -445,15 +464,12 @@
 TEST_F(EventThreadTest, tracksEventConnections) {
     EXPECT_EQ(2, mThread->getEventThreadConnectionCount());
     ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY};
-    sp<MockEventThreadConnection> errorConnection =
-            createConnection(errorConnectionEventRecorder,
-                             ISurfaceComposer::eConfigChangedSuppress);
+    sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
     mThread->setVsyncRate(1, errorConnection);
     EXPECT_EQ(3, mThread->getEventThreadConnectionCount());
     ConnectionEventRecorder secondConnectionEventRecorder{0};
     sp<MockEventThreadConnection> secondConnection =
-            createConnection(secondConnectionEventRecorder,
-                             ISurfaceComposer::eConfigChangedSuppress);
+            createConnection(secondConnectionEventRecorder);
     mThread->setVsyncRate(1, secondConnection);
     EXPECT_EQ(4, mThread->getEventThreadConnectionCount());
 
@@ -472,9 +488,7 @@
 
 TEST_F(EventThreadTest, eventsDroppedIfNonfatalEventDeliveryError) {
     ConnectionEventRecorder errorConnectionEventRecorder{WOULD_BLOCK};
-    sp<MockEventThreadConnection> errorConnection =
-            createConnection(errorConnectionEventRecorder,
-                             ISurfaceComposer::eConfigChangedSuppress);
+    sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
     mThread->setVsyncRate(1, errorConnection);
 
     // EventThread should enable vsync callbacks.
@@ -539,8 +553,7 @@
 TEST_F(EventThreadTest, suppressConfigChanged) {
     ConnectionEventRecorder suppressConnectionEventRecorder{0};
     sp<MockEventThreadConnection> suppressConnection =
-            createConnection(suppressConnectionEventRecorder,
-                             ISurfaceComposer::eConfigChangedSuppress);
+            createConnection(suppressConnectionEventRecorder);
 
     mThread->onConfigChanged(INTERNAL_DISPLAY_ID, HwcConfigIndexType(9), 16666666);
     expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 9, 16666666);
@@ -549,6 +562,35 @@
     ASSERT_FALSE(args.has_value());
 }
 
+TEST_F(EventThreadTest, postUidFrameRateMapping) {
+    const std::vector<FrameRateOverride> overrides = {
+            {.uid = 1, .frameRateHz = 20},
+            {.uid = 3, .frameRateHz = 40},
+            {.uid = 5, .frameRateHz = 60},
+    };
+
+    mThread->onFrameRateOverridesChanged(INTERNAL_DISPLAY_ID, overrides);
+    expectUidFrameRateMappingEventReceivedByConnection(INTERNAL_DISPLAY_ID, overrides);
+}
+
+TEST_F(EventThreadTest, suppressUidFrameRateMapping) {
+    const std::vector<FrameRateOverride> overrides = {
+            {.uid = 1, .frameRateHz = 20},
+            {.uid = 3, .frameRateHz = 40},
+            {.uid = 5, .frameRateHz = 60},
+    };
+
+    ConnectionEventRecorder suppressConnectionEventRecorder{0};
+    sp<MockEventThreadConnection> suppressConnection =
+            createConnection(suppressConnectionEventRecorder);
+
+    mThread->onFrameRateOverridesChanged(INTERNAL_DISPLAY_ID, overrides);
+    expectUidFrameRateMappingEventReceivedByConnection(INTERNAL_DISPLAY_ID, overrides);
+
+    auto args = suppressConnectionEventRecorder.waitForCall();
+    ASSERT_FALSE(args.has_value());
+}
+
 TEST_F(EventThreadTest, requestNextVsyncWithThrottleVsyncDoesntPostVSync) {
     // Signal that we want the next vsync event to be posted to the throttled connection
     mThread->requestNextVsync(mThrottledConnection);
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 82cf22c..f2b7191 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -1538,7 +1538,7 @@
             std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_30);
     const uid_t uid = 1234;
-    refreshRateConfigs->setPreferredRefreshRateForUid(uid, 30);
+    refreshRateConfigs->setPreferredRefreshRateForUid({uid, 30});
     EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDividerForUid(uid));
 
     refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_60);
@@ -1552,6 +1552,12 @@
 
     refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_120);
     EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+
+    refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
+    refreshRateConfigs->setPreferredRefreshRateForUid({uid, 22.5});
+    EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+    refreshRateConfigs->setPreferredRefreshRateForUid({uid, 22.6f});
+    EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDividerForUid(uid));
 }
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
index 8cd8372..c5deb7c 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
@@ -132,14 +132,12 @@
     EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
     EXPECT_CALL(*eventThread, createEventConnection(_, _))
             .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
-                                                       ResyncCallback(),
-                                                       ISurfaceComposer::eConfigChangedSuppress)));
+                                                       ResyncCallback())));
 
     EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
     EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
             .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
-                                                       ResyncCallback(),
-                                                       ISurfaceComposer::eConfigChangedSuppress)));
+                                                       ResyncCallback())));
 
     auto vsyncController = std::make_unique<mock::VsyncController>();
     auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 895c6b3..fb02a33 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -42,8 +42,7 @@
     class MockEventThreadConnection : public android::EventThreadConnection {
     public:
         explicit MockEventThreadConnection(EventThread* eventThread)
-              : EventThreadConnection(eventThread, /*callingUid=*/0, ResyncCallback(),
-                                      ISurfaceComposer::eConfigChangedSuppress) {}
+              : EventThreadConnection(eventThread, /*callingUid=*/0, ResyncCallback()) {}
         ~MockEventThreadConnection() = default;
 
         MOCK_METHOD1(stealReceiveChannel, status_t(gui::BitTube* outChannel));
@@ -97,9 +96,7 @@
 TEST_F(SchedulerTest, invalidConnectionHandle) {
     Scheduler::ConnectionHandle handle;
 
-    const sp<IDisplayEventConnection> connection =
-            mScheduler.createDisplayEventConnection(handle,
-                                                    ISurfaceComposer::eConfigChangedSuppress);
+    const sp<IDisplayEventConnection> connection = mScheduler.createDisplayEventConnection(handle);
 
     EXPECT_FALSE(connection);
     EXPECT_FALSE(mScheduler.getEventConnection(handle));
@@ -125,8 +122,7 @@
 
 TEST_F(SchedulerTest, validConnectionHandle) {
     const sp<IDisplayEventConnection> connection =
-            mScheduler.createDisplayEventConnection(mConnectionHandle,
-                                                    ISurfaceComposer::eConfigChangedSuppress);
+            mScheduler.createDisplayEventConnection(mConnectionHandle);
 
     ASSERT_EQ(mEventThreadConnection, connection);
     EXPECT_TRUE(mScheduler.getEventConnection(mConnectionHandle));
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index e25d501..5278641 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -172,14 +172,12 @@
     EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
     EXPECT_CALL(*eventThread, createEventConnection(_, _))
             .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
-                                                       ResyncCallback(),
-                                                       ISurfaceComposer::eConfigChangedSuppress)));
+                                                       ResyncCallback())));
 
     EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
     EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
             .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
-                                                       ResyncCallback(),
-                                                       ISurfaceComposer::eConfigChangedSuppress)));
+                                                       ResyncCallback())));
 
     auto vsyncController = std::make_unique<mock::VsyncController>();
     auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 68cf330..c36d994 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -65,17 +65,13 @@
 
         EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
         EXPECT_CALL(*eventThread, createEventConnection(_, _))
-                .WillOnce(Return(
-                        new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
-                                                  ResyncCallback(),
-                                                  ISurfaceComposer::eConfigChangedSuppress)));
+                .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+                                                           ResyncCallback())));
 
         EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
         EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
-                .WillOnce(Return(
-                        new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
-                                                  ResyncCallback(),
-                                                  ISurfaceComposer::eConfigChangedSuppress)));
+                .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+                                                           ResyncCallback())));
 
         EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
         EXPECT_CALL(*mVSyncTracker, currentPeriod())
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index b4594c1..650d52d 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -28,11 +28,14 @@
     ~EventThread() override;
 
     MOCK_CONST_METHOD2(createEventConnection,
-                       sp<EventThreadConnection>(ResyncCallback, ISurfaceComposer::ConfigChanged));
+                       sp<EventThreadConnection>(ResyncCallback,
+                                                 ISurfaceComposer::EventRegistrationFlags));
     MOCK_METHOD0(onScreenReleased, void());
     MOCK_METHOD0(onScreenAcquired, void());
     MOCK_METHOD2(onHotplugReceived, void(PhysicalDisplayId, bool));
     MOCK_METHOD3(onConfigChanged, void(PhysicalDisplayId, HwcConfigIndexType, nsecs_t));
+    MOCK_METHOD2(onFrameRateOverridesChanged,
+                 void(PhysicalDisplayId, std::vector<FrameRateOverride>));
     MOCK_CONST_METHOD1(dump, void(std::string&));
     MOCK_METHOD2(setDuration,
                  void(std::chrono::nanoseconds workDuration,