Merge "Setting queue metadata size via dvrSurface API"
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 301bb67..94c6821 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -219,6 +219,9 @@
 static const char* k_traceBufferSizePath =
     "buffer_size_kb";
 
+static const char* k_traceCmdlineSizePath =
+    "saved_cmdlines_size";
+
 static const char* k_tracingOverwriteEnablePath =
     "options/overwrite";
 
@@ -439,6 +442,12 @@
     return writeStr(k_traceBufferSizePath, str);
 }
 
+// Set the default size of cmdline hashtable
+static bool setCmdlineSize()
+{
+    return writeStr(k_traceCmdlineSizePath, "8192");
+}
+
 // Set the clock to the best available option while tracing. Use 'boot' if it's
 // available; otherwise, use 'mono'. If neither are available use 'global'.
 // Any write to the trace_clock sysfs file will reset the buffer, so only
@@ -756,6 +765,7 @@
     ok &= setCategoriesEnableFromFile(g_categoriesFile);
     ok &= setTraceOverwriteEnable(g_traceOverwrite);
     ok &= setTraceBufferSizeKB(g_traceBufferSizeKB);
+    ok &= setCmdlineSize();
     ok &= setClock();
     ok &= setPrintTgidEnableIfPresent(true);
     ok &= setKernelTraceFuncs(g_kernelTraceFuncs);
diff --git a/cmds/dumpstate/DumpstateInternal.h b/cmds/dumpstate/DumpstateInternal.h
index 2f7704d..10db5d6 100644
--- a/cmds/dumpstate/DumpstateInternal.h
+++ b/cmds/dumpstate/DumpstateInternal.h
@@ -32,6 +32,12 @@
     ALOGI(__VA_ARGS__);
 #endif
 
+#ifndef MYLOGW
+#define MYLOGW(...)               \
+    fprintf(stderr, __VA_ARGS__); \
+    ALOGW(__VA_ARGS__);
+#endif
+
 #ifndef MYLOGE
 #define MYLOGE(...)               \
     fprintf(stderr, __VA_ARGS__); \
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index f84d86d..745361c 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -21,13 +21,9 @@
 #include <fcntl.h>
 #include <libgen.h>
 #include <limits.h>
-#include <memory>
-#include <regex>
-#include <set>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <string>
 #include <string.h>
 #include <sys/prctl.h>
 #include <sys/resource.h>
@@ -35,6 +31,11 @@
 #include <sys/time.h>
 #include <sys/wait.h>
 #include <unistd.h>
+#include <memory>
+#include <regex>
+#include <set>
+#include <string>
+#include <vector>
 
 #include <android-base/file.h>
 #include <android-base/properties.h>
@@ -78,19 +79,29 @@
 #define LOGPERSIST_DATA_DIR "/data/misc/logd"
 #define PROFILE_DATA_DIR_CUR "/data/misc/profiles/cur"
 #define PROFILE_DATA_DIR_REF "/data/misc/profiles/ref"
-#define TOMBSTONE_DIR "/data/tombstones"
-#define TOMBSTONE_FILE_PREFIX TOMBSTONE_DIR "/tombstone_"
-/* Can accomodate a tombstone number up to 9999. */
-#define TOMBSTONE_MAX_LEN (sizeof(TOMBSTONE_FILE_PREFIX) + 4)
-#define NUM_TOMBSTONES  10
 #define WLUTIL "/vendor/xbin/wlutil"
 
-typedef struct {
-  char name[TOMBSTONE_MAX_LEN];
-  int fd;
-} tombstone_data_t;
+// TODO(narayan): Since this information has to be kept in sync
+// with tombstoned, we should just put it in a common header.
+//
+// File: system/core/debuggerd/tombstoned/tombstoned.cpp
+static const std::string TOMBSTONE_DIR = "/data/tombstones/";
+static const std::string TOMBSTONE_FILE_PREFIX = "tombstone_";
+static const std::string ANR_DIR = "/data/anr/";
+static const std::string ANR_FILE_PREFIX = "anr_";
 
-static tombstone_data_t tombstone_data[NUM_TOMBSTONES];
+struct DumpData {
+    std::string name;
+    int fd;
+    time_t mtime;
+};
+
+static bool operator<(const DumpData& d1, const DumpData& d2) {
+    return d1.mtime > d2.mtime;
+}
+
+static std::unique_ptr<std::vector<DumpData>> tombstone_data;
+static std::unique_ptr<std::vector<DumpData>> anr_data;
 
 // TODO: temporary variables and functions used during C++ refactoring
 static Dumpstate& ds = Dumpstate::GetInstance();
@@ -122,23 +133,79 @@
 
 static const CommandOptions AS_ROOT_20 = CommandOptions::WithTimeout(20).AsRoot().Build();
 
-/* gets the tombstone data, according to the bugreport type: if zipped, gets all tombstones;
- * otherwise, gets just those modified in the last half an hour. */
-static void get_tombstone_fds(tombstone_data_t data[NUM_TOMBSTONES]) {
-    time_t thirty_minutes_ago = ds.now_ - 60 * 30;
-    for (size_t i = 0; i < NUM_TOMBSTONES; i++) {
-        snprintf(data[i].name, sizeof(data[i].name), "%s%02zu", TOMBSTONE_FILE_PREFIX, i);
-        int fd = TEMP_FAILURE_RETRY(open(data[i].name,
-                                         O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
-        struct stat st;
-        if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode) && st.st_size > 0 &&
-            (ds.IsZipping() || st.st_mtime >= thirty_minutes_ago)) {
-            data[i].fd = fd;
-        } else {
-            close(fd);
-            data[i].fd = -1;
+/*
+ * Returns a vector of dump fds under |dir_path| with a given |file_prefix|.
+ * The returned vector is sorted by the mtimes of the dumps. If |limit_by_mtime|
+ * is set, the vector only contains files that were written in the last 30 minutes.
+ */
+static std::vector<DumpData>* GetDumpFds(const std::string& dir_path,
+                                         const std::string& file_prefix,
+                                         bool limit_by_mtime) {
+    const time_t thirty_minutes_ago = ds.now_ - 60 * 30;
+
+    std::unique_ptr<std::vector<DumpData>> dump_data(new std::vector<DumpData>());
+    std::unique_ptr<DIR, decltype(&closedir)> dump_dir(opendir(dir_path.c_str()), closedir);
+
+    struct dirent* entry = nullptr;
+    while ((entry = readdir(dump_dir.get()))) {
+        if (entry->d_type != DT_REG) {
+            continue;
         }
+
+        const std::string base_name(entry->d_name);
+        if (base_name.find(file_prefix) != 0) {
+            continue;
+        }
+
+        const std::string abs_path = dir_path + base_name;
+        android::base::unique_fd fd(
+            TEMP_FAILURE_RETRY(open(abs_path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK)));
+        if (fd == -1) {
+            MYLOGW("Unable to open dump file: %s %s\n", abs_path.c_str(), strerror(errno));
+            break;
+        }
+
+        struct stat st = {};
+        if (fstat(fd, &st) == -1) {
+            MYLOGW("Unable to stat dump file: %s %s\n", abs_path.c_str(), strerror(errno));
+            continue;
+        }
+
+        if (limit_by_mtime && st.st_mtime >= thirty_minutes_ago) {
+            MYLOGI("Excluding stale dump file: %s\n", abs_path.c_str());
+            continue;
+        }
+
+        DumpData data = {.name = abs_path, .fd = fd.release(), .mtime = st.st_mtime};
+
+        dump_data->push_back(data);
     }
+
+    std::sort(dump_data->begin(), dump_data->end());
+
+    return dump_data.release();
+}
+
+static bool AddDumps(const std::vector<DumpData>::const_iterator start,
+                     const std::vector<DumpData>::const_iterator end,
+                     const char* type_name, const bool add_to_zip) {
+    bool dumped = false;
+    for (auto it = start; it != end; ++it) {
+        const std::string& name = it->name;
+        const int fd = it->fd;
+        dumped = true;
+        if (ds.IsZipping() && add_to_zip) {
+            if (!ds.AddZipEntryFromFd(ZIP_ROOT_DIR + name, fd)) {
+                MYLOGE("Unable to add %s %s to zip file\n", name.c_str(), type_name);
+            }
+        } else {
+            dump_file_from_fd(type_name, name.c_str(), fd);
+        }
+
+        close(fd);
+    }
+
+    return dumped;
 }
 
 // for_each_pid() callback to get mount info about a process.
@@ -860,11 +927,10 @@
     RunCommand("IP6TABLES RAW", {"ip6tables", "-t", "raw", "-L", "-nvx"});
 }
 
-static void AddAnrTraceFiles() {
-    bool add_to_zip = ds.IsZipping() && ds.version_ == VERSION_SPLIT_ANR;
+static void AddGlobalAnrTraceFile(const bool add_to_zip, const std::string& anr_traces_file,
+                                  const std::string& anr_traces_dir) {
     std::string dump_traces_dir;
 
-    /* show the traces we collected in main(), if that was done */
     if (dump_traces_path != nullptr) {
         if (add_to_zip) {
             dump_traces_dir = dirname(dump_traces_path);
@@ -877,8 +943,6 @@
         }
     }
 
-    std::string anr_traces_path = android::base::GetProperty("dalvik.vm.stack-trace-file", "");
-    std::string anr_traces_dir = dirname(anr_traces_path.c_str());
 
     // Make sure directory is not added twice.
     // TODO: this is an overzealous check because it's relying on dump_traces_path - which is
@@ -888,54 +952,110 @@
     // be revisited.
     bool already_dumped = anr_traces_dir == dump_traces_dir;
 
-    MYLOGD("AddAnrTraceFiles(): dump_traces_dir=%s, anr_traces_dir=%s, already_dumped=%d\n",
+    MYLOGD("AddGlobalAnrTraceFile(): dump_traces_dir=%s, anr_traces_dir=%s, already_dumped=%d\n",
            dump_traces_dir.c_str(), anr_traces_dir.c_str(), already_dumped);
 
-    if (anr_traces_path.empty()) {
-        printf("*** NO VM TRACES FILE DEFINED (dalvik.vm.stack-trace-file)\n\n");
+    int fd = TEMP_FAILURE_RETRY(
+        open(anr_traces_file.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
+    if (fd < 0) {
+        printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anr_traces_file.c_str(), strerror(errno));
     } else {
-        int fd = TEMP_FAILURE_RETRY(
-            open(anr_traces_path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
-        if (fd < 0) {
-            printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anr_traces_path.c_str(),
-                   strerror(errno));
-        } else {
-            if (add_to_zip) {
-                if (!already_dumped) {
-                    MYLOGD("Adding dalvik ANR traces (directory %s) to the zip file\n",
-                           anr_traces_dir.c_str());
-                    ds.AddDir(anr_traces_dir, true);
-                    already_dumped = true;
-                }
-            } else {
-                MYLOGD("Dumping last ANR traces (%s) to the main bugreport entry\n",
-                       anr_traces_path.c_str());
-                dump_file_from_fd("VM TRACES AT LAST ANR", anr_traces_path.c_str(), fd);
+        if (add_to_zip) {
+            if (!already_dumped) {
+                MYLOGD("Adding dalvik ANR traces (directory %s) to the zip file\n",
+                       anr_traces_dir.c_str());
+                ds.AddDir(anr_traces_dir, true);
             }
+        } else {
+            MYLOGD("Dumping last ANR traces (%s) to the main bugreport entry\n",
+                   anr_traces_file.c_str());
+            dump_file_from_fd("VM TRACES AT LAST ANR", anr_traces_file.c_str(), fd);
+        }
+    }
+}
+
+static void AddAnrTraceDir(const bool add_to_zip, const std::string& anr_traces_dir) {
+    MYLOGD("AddAnrTraceDir(): dump_traces_file=%s, anr_traces_dir=%s\n", dump_traces_path,
+           anr_traces_dir.c_str());
+
+    // If we're here, dump_traces_path will always be a temporary file
+    // (created with mkostemp or similar) that contains dumps taken earlier
+    // on in the process.
+    if (dump_traces_path != nullptr) {
+        if (add_to_zip) {
+            ds.AddZipEntry(ZIP_ROOT_DIR + anr_traces_dir + "/traces-just-now.txt", dump_traces_path);
+        } else {
+            MYLOGD("Dumping current ANR traces (%s) to the main bugreport entry\n",
+                   dump_traces_path);
+            ds.DumpFile("VM TRACES JUST NOW", dump_traces_path);
+        }
+
+        const int ret = unlink(dump_traces_path);
+        if (ret == -1) {
+            MYLOGW("Error unlinking temporary trace path %s: %s\n", dump_traces_path,
+                   strerror(errno));
         }
     }
 
-    if (add_to_zip && already_dumped) {
-        MYLOGD("Already dumped directory %s to the zip file\n", anr_traces_dir.c_str());
+    // Add a specific message for the first ANR Dump.
+    if (anr_data->size() > 0) {
+        AddDumps(anr_data->begin(), anr_data->begin() + 1,
+                 "VM TRACES AT LAST ANR", add_to_zip);
+
+        if (anr_data->size() > 1) {
+            AddDumps(anr_data->begin() + 1, anr_data->end(),
+                     "HISTORICAL ANR", add_to_zip);
+        }
+    } else {
+        printf("*** NO ANRs to dump in %s\n\n", ANR_DIR.c_str());
+    }
+}
+
+static void AddAnrTraceFiles() {
+    const bool add_to_zip = ds.IsZipping() && ds.version_ == VERSION_SPLIT_ANR;
+
+    std::string anr_traces_file;
+    std::string anr_traces_dir;
+    bool is_global_trace_file = true;
+
+    // First check whether the stack-trace-dir property is set. When it's set,
+    // each ANR trace will be written to a separate file and not to a global
+    // stack trace file.
+    anr_traces_dir = android::base::GetProperty("dalvik.vm.stack-trace-dir", "");
+    if (anr_traces_dir.empty()) {
+        anr_traces_file = android::base::GetProperty("dalvik.vm.stack-trace-file", "");
+        if (!anr_traces_file.empty()) {
+            anr_traces_dir = dirname(anr_traces_file.c_str());
+        }
+    } else {
+        is_global_trace_file = false;
+    }
+
+    // We have neither configured a global trace file nor a trace directory,
+    // there will be nothing to dump.
+    if (anr_traces_file.empty() && anr_traces_dir.empty()) {
+        printf("*** NO VM TRACES FILE DEFINED (dalvik.vm.stack-trace-file)\n\n");
         return;
     }
 
+    if (is_global_trace_file) {
+        AddGlobalAnrTraceFile(add_to_zip, anr_traces_file, anr_traces_dir);
+    } else {
+        AddAnrTraceDir(add_to_zip, anr_traces_dir);
+    }
+
     /* slow traces for slow operations */
     struct stat st;
-    if (!anr_traces_path.empty()) {
-        int tail = anr_traces_path.size() - 1;
-        while (tail > 0 && anr_traces_path.at(tail) != '/') {
-            tail--;
-        }
+    if (!anr_traces_dir.empty()) {
         int i = 0;
-        while (1) {
-            anr_traces_path = anr_traces_path.substr(0, tail + 1) +
-                              android::base::StringPrintf("slow%02d.txt", i);
-            if (stat(anr_traces_path.c_str(), &st)) {
+        while (true) {
+            const std::string slow_trace_path =
+                anr_traces_dir + android::base::StringPrintf("slow%02d.txt", i);
+            if (stat(slow_trace_path.c_str(), &st)) {
                 // No traces file at this index, done with the files.
                 break;
             }
-            ds.DumpFile("VM TRACES WHEN SLOW", anr_traces_path.c_str());
+            ds.DumpFile("VM TRACES WHEN SLOW", slow_trace_path.c_str());
             i++;
         }
     }
@@ -1010,25 +1130,12 @@
 
     AddAnrTraceFiles();
 
-    int dumped = 0;
-    for (size_t i = 0; i < NUM_TOMBSTONES; i++) {
-        if (tombstone_data[i].fd != -1) {
-            const char *name = tombstone_data[i].name;
-            int fd = tombstone_data[i].fd;
-            dumped = 1;
-            if (ds.IsZipping()) {
-                if (!ds.AddZipEntryFromFd(ZIP_ROOT_DIR + name, fd)) {
-                    MYLOGE("Unable to add tombstone %s to zip file\n", name);
-                }
-            } else {
-                dump_file_from_fd("TOMBSTONE", name, fd);
-            }
-            close(fd);
-            tombstone_data[i].fd = -1;
-        }
-    }
-    if (!dumped) {
-        printf("*** NO TOMBSTONES to dump in %s\n\n", TOMBSTONE_DIR);
+    // NOTE: tombstones are always added as separate entries in the zip archive
+    // and are not interspersed with the main report.
+    const bool tombstones_dumped = AddDumps(tombstone_data->begin(), tombstone_data->end(),
+                                            "TOMBSTONE", true /* add_to_zip */);
+    if (!tombstones_dumped) {
+        printf("*** NO TOMBSTONES to dump in %s\n\n", TOMBSTONE_DIR.c_str());
     }
 
     DumpFile("NETWORK DEV INFO", "/proc/net/dev");
@@ -1707,7 +1814,9 @@
         dump_traces_path = dump_traces();
 
         /* Run some operations that require root. */
-        get_tombstone_fds(tombstone_data);
+        tombstone_data.reset(GetDumpFds(TOMBSTONE_DIR, TOMBSTONE_FILE_PREFIX, !ds.IsZipping()));
+        anr_data.reset(GetDumpFds(ANR_DIR, ANR_FILE_PREFIX, !ds.IsZipping()));
+
         ds.AddDir(RECOVERY_DIR, true);
         ds.AddDir(RECOVERY_DATA_DIR, true);
         ds.AddDir(LOGPERSIST_DATA_DIR, false);
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp
index f649a5e..5698d1b 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -38,6 +38,7 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <memory>
 #include <set>
 #include <string>
 #include <vector>
@@ -46,6 +47,7 @@
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <android-base/unique_fd.h>
 #include <android/hidl/manager/1.0/IServiceManager.h>
 #include <cutils/properties.h>
 #include <cutils/sockets.h>
@@ -852,15 +854,143 @@
     return pids; // whether it was okay or not
 }
 
+const char* DumpTraces(const std::string& traces_path);
+const char* DumpTracesTombstoned(const std::string& traces_dir);
+
 /* dump Dalvik and native stack traces, return the trace file location (NULL if none) */
 const char *dump_traces() {
     DurationReporter duration_reporter("DUMP TRACES");
 
-    const char* result = nullptr;
+    const std::string traces_dir = android::base::GetProperty("dalvik.vm.stack-trace-dir", "");
+    if (!traces_dir.empty()) {
+        return DumpTracesTombstoned(traces_dir);
+    }
 
-    std::string traces_path = android::base::GetProperty("dalvik.vm.stack-trace-file", "");
-    if (traces_path.empty()) return nullptr;
+    const std::string traces_file = android::base::GetProperty("dalvik.vm.stack-trace-file", "");
+    if (!traces_file.empty()) {
+        return DumpTraces(traces_file);
+    }
 
+    return nullptr;
+}
+
+static bool IsZygote(int pid) {
+    static const std::string kZygotePrefix = "zygote";
+
+    std::string cmdline;
+    if (!android::base::ReadFileToString(android::base::StringPrintf("/proc/%d/cmdline", pid),
+                                         &cmdline)) {
+        return true;
+    }
+
+    return (cmdline.find(kZygotePrefix) == 0);
+}
+
+const char* DumpTracesTombstoned(const std::string& traces_dir) {
+    const std::string temp_file_pattern = traces_dir + "/dumptrace_XXXXXX";
+
+    const size_t buf_size = temp_file_pattern.length() + 1;
+    std::unique_ptr<char[]> file_name_buf(new char[buf_size]);
+    memcpy(file_name_buf.get(), temp_file_pattern.c_str(), buf_size);
+
+    // Create a new, empty file to receive all trace dumps.
+    //
+    // TODO: This can be simplified once we remove support for the old style
+    // dumps. We can have a file descriptor passed in to dump_traces instead
+    // of creating a file, closing it and then reopening it again.
+    android::base::unique_fd fd(mkostemp(file_name_buf.get(), O_APPEND | O_CLOEXEC));
+    if (fd < 0) {
+        MYLOGE("mkostemp on pattern %s: %s\n", file_name_buf.get(), strerror(errno));
+        return nullptr;
+    }
+
+    // Nobody should have access to this temporary file except dumpstate, but we
+    // temporarily grant 'read' to 'others' here because this file is created
+    // when tombstoned is still running as root, but dumped after dropping. This
+    // can go away once support for old style dumping has.
+    const int chmod_ret = fchmod(fd, 0666);
+    if (chmod_ret < 0) {
+        MYLOGE("fchmod on %s failed: %s\n", file_name_buf.get(), strerror(errno));
+        return nullptr;
+    }
+
+    std::unique_ptr<DIR, decltype(&closedir)> proc(opendir("/proc"), closedir);
+    if (proc.get() == nullptr) {
+        MYLOGE("opendir /proc failed: %s\n", strerror(errno));
+        return nullptr;
+    }
+
+    // Number of times process dumping has timed out. If we encounter too many
+    // failures, we'll give up.
+    int timeout_failures = 0;
+    bool dalvik_found = false;
+
+    const std::set<int> hal_pids = get_interesting_hal_pids();
+
+    struct dirent* d;
+    while ((d = readdir(proc.get()))) {
+        int pid = atoi(d->d_name);
+        if (pid <= 0) {
+            continue;
+        }
+
+        const std::string link_name = android::base::StringPrintf("/proc/%d/exe", pid);
+        std::string exe;
+        if (!android::base::Readlink(link_name, &exe)) {
+            continue;
+        }
+
+        bool is_java_process;
+        if (exe == "/system/bin/app_process32" || exe == "/system/bin/app_process64") {
+            // Don't bother dumping backtraces for the zygote.
+            if (IsZygote(pid)) {
+                continue;
+            }
+
+            dalvik_found = true;
+            is_java_process = true;
+        } else if (should_dump_native_traces(exe.c_str()) || hal_pids.find(pid) != hal_pids.end()) {
+            is_java_process = false;
+        } else {
+            // Probably a native process we don't care about, continue.
+            continue;
+        }
+
+        // If 3 backtrace dumps fail in a row, consider debuggerd dead.
+        if (timeout_failures == 3) {
+            dprintf(fd, "ERROR: Too many stack dump failures, exiting.\n");
+            break;
+        }
+
+        const uint64_t start = Nanotime();
+        const int ret = dump_backtrace_to_file_timeout(
+            pid, is_java_process ? kDebuggerdJavaBacktrace : kDebuggerdNativeBacktrace,
+            is_java_process ? 5 : 20, fd);
+
+        if (ret == -1) {
+            dprintf(fd, "dumping failed, likely due to a timeout\n");
+            timeout_failures++;
+            continue;
+        }
+
+        // We've successfully dumped stack traces, reset the failure count
+        // and write a summary of the elapsed time to the file and continue with the
+        // next process.
+        timeout_failures = 0;
+
+        dprintf(fd, "[dump %s stack %d: %.3fs elapsed]\n", is_java_process ? "dalvik" : "native",
+                pid, (float)(Nanotime() - start) / NANOS_PER_SEC);
+    }
+
+    if (!dalvik_found) {
+        MYLOGE("Warning: no Dalvik processes found to dump stacks\n");
+    }
+
+    return file_name_buf.release();
+}
+
+const char* DumpTraces(const std::string& traces_path) {
+    const char* result = NULL;
     /* move the old traces.txt (if any) out of the way temporarily */
     std::string anrtraces_path = traces_path + ".anr";
     if (rename(traces_path.c_str(), anrtraces_path.c_str()) && errno != ENOENT) {
@@ -973,7 +1103,8 @@
                 /* If 3 backtrace dumps fail in a row, consider debuggerd dead. */
                 if (timeout_failures == 3) {
                     dprintf(fd, "too many stack dump failures, skipping...\n");
-                } else if (dump_backtrace_to_file_timeout(pid, fd, 20) == -1) {
+                } else if (dump_backtrace_to_file_timeout(
+                        pid, kDebuggerdNativeBacktrace, 20, fd) == -1) {
                     dprintf(fd, "dumping failed, likely due to a timeout\n");
                     timeout_failures++;
                 } else {
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index a1fea8d..22ad718 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -915,15 +915,8 @@
     return res;
 }
 
-/* Try to ensure free_size bytes of storage are available.
- * Returns 0 on success.
- * This is rather simple-minded because doing a full LRU would
- * be potentially memory-intensive, and without atime it would
- * also require that apps constantly modify file metadata even
- * when just reading from the cache, which is pretty awful.
- */
 binder::Status InstalldNativeService::freeCache(const std::unique_ptr<std::string>& uuid,
-        int64_t freeStorageSize, int32_t flags) {
+        int64_t targetFreeBytes, int64_t cacheReservedBytes, int32_t flags) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
     std::lock_guard<std::recursive_mutex> lock(mLock);
@@ -938,11 +931,12 @@
         return error("Failed to determine free space for " + data_path);
     }
 
-    int64_t needed = freeStorageSize - free;
+    int64_t cleared = 0;
+    int64_t needed = targetFreeBytes - free;
     LOG(DEBUG) << "Device " << data_path << " has " << free << " free; requested "
-            << freeStorageSize << "; needed " << needed;
+            << targetFreeBytes << "; needed " << needed;
 
-    if (free >= freeStorageSize) {
+    if (free >= targetFreeBytes) {
         return ok();
     }
 
@@ -999,6 +993,7 @@
 
         // 2. Populate tracker stats and insert into priority queue
         ATRACE_BEGIN("populate");
+        int64_t cacheTotal = 0;
         auto cmp = [](std::shared_ptr<CacheTracker> left, std::shared_ptr<CacheTracker> right) {
             return (left->getCacheRatio() < right->getCacheRatio());
         };
@@ -1007,6 +1002,7 @@
         for (const auto& it : trackers) {
             it.second->loadStats();
             queue.push(it.second);
+            cacheTotal += it.second->cacheUsed;
         }
         ATRACE_END();
 
@@ -1023,6 +1019,12 @@
                 break;
             }
 
+            // Only keep clearing when we haven't pushed into reserved area
+            if (cacheReservedBytes > 0 && cleared >= (cacheTotal - cacheReservedBytes)) {
+                LOG(DEBUG) << "Refusing to clear cached data in reserved space";
+                break;
+            }
+
             // Find the best tracker to work with; this might involve swapping
             // if the active tracker is no longer the most over quota
             bool nextBetter = active && !queue.empty()
@@ -1052,13 +1054,14 @@
                 }
                 active->cacheUsed -= item->size;
                 needed -= item->size;
+                cleared += item->size;
             }
 
             // Verify that we're actually done before bailing, since sneaky
             // apps might be using hardlinks
             if (needed <= 0) {
                 free = data_disk_free(data_path);
-                needed = freeStorageSize - free;
+                needed = targetFreeBytes - free;
                 if (needed <= 0) {
                     break;
                 } else {
@@ -1073,11 +1076,11 @@
     }
 
     free = data_disk_free(data_path);
-    if (free >= freeStorageSize) {
+    if (free >= targetFreeBytes) {
         return ok();
     } else {
         return error(StringPrintf("Failed to free up %" PRId64 " on %s; final free space %" PRId64,
-                freeStorageSize, data_path.c_str(), free));
+                targetFreeBytes, data_path.c_str(), free));
     }
 }
 
@@ -2256,13 +2259,13 @@
 }
 
 binder::Status InstalldNativeService::deleteOdex(const std::string& apkPath,
-        const std::string& instructionSet, const std::string& outputPath) {
+        const std::string& instructionSet, const std::unique_ptr<std::string>& outputPath) {
     ENFORCE_UID(AID_SYSTEM);
     std::lock_guard<std::recursive_mutex> lock(mLock);
 
     const char* apk_path = apkPath.c_str();
     const char* instruction_set = instructionSet.c_str();
-    const char* oat_dir = outputPath.c_str();
+    const char* oat_dir = outputPath ? outputPath->c_str() : nullptr;
 
     bool res = delete_odex(apk_path, instruction_set, oat_dir);
     return res ? ok() : error();
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index a94223c..5756b82 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -101,8 +101,8 @@
     binder::Status removeIdmap(const std::string& overlayApkPath);
     binder::Status rmPackageDir(const std::string& packageDir);
     binder::Status markBootComplete(const std::string& instructionSet);
-    binder::Status freeCache(const std::unique_ptr<std::string>& uuid, int64_t freeStorageSize,
-            int32_t flags);
+    binder::Status freeCache(const std::unique_ptr<std::string>& uuid, int64_t targetFreeBytes,
+            int64_t cacheReservedBytes, int32_t flags);
     binder::Status linkNativeLibraryDirectory(const std::unique_ptr<std::string>& uuid,
             const std::string& packageName, const std::string& nativeLibPath32, int32_t userId);
     binder::Status createOatDir(const std::string& oatDir, const std::string& instructionSet);
@@ -111,7 +111,7 @@
     binder::Status moveAb(const std::string& apkPath, const std::string& instructionSet,
             const std::string& outputPath);
     binder::Status deleteOdex(const std::string& apkPath, const std::string& instructionSet,
-            const std::string& outputPath);
+            const std::unique_ptr<std::string>& outputPath);
     binder::Status reconcileSecondaryDexFile(const std::string& dexPath,
         const std::string& packageName, int32_t uid, const std::vector<std::string>& isa,
         const std::unique_ptr<std::string>& volumeUuid, int32_t storage_flag, bool* _aidl_return);
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index efcae4f..c8e76b0 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -66,7 +66,8 @@
     void removeIdmap(@utf8InCpp String overlayApkPath);
     void rmPackageDir(@utf8InCpp String packageDir);
     void markBootComplete(@utf8InCpp String instructionSet);
-    void freeCache(@nullable @utf8InCpp String uuid, long freeStorageSize, int flags);
+    void freeCache(@nullable @utf8InCpp String uuid, long targetFreeBytes,
+            long cacheReservedBytes, int flags);
     void linkNativeLibraryDirectory(@nullable @utf8InCpp String uuid,
             @utf8InCpp String packageName, @utf8InCpp String nativeLibPath32, int userId);
     void createOatDir(@utf8InCpp String oatDir, @utf8InCpp String instructionSet);
@@ -75,7 +76,7 @@
     void moveAb(@utf8InCpp String apkPath, @utf8InCpp String instructionSet,
             @utf8InCpp String outputPath);
     void deleteOdex(@utf8InCpp String apkPath, @utf8InCpp String instructionSet,
-            @utf8InCpp String outputPath);
+            @nullable @utf8InCpp String outputPath);
 
     boolean reconcileSecondaryDexFile(@utf8InCpp String dexPath, @utf8InCpp String pkgName,
         int uid, in @utf8InCpp String[] isas, @nullable @utf8InCpp String volume_uuid,
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 22de64e..af78bfb 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -1242,13 +1242,7 @@
             ALOGE("installd cannot compute input vdex location for '%s'\n", path);
             return false;
         }
-        if (dexopt_action == DEX2OAT_FOR_BOOT_IMAGE) {
-            // When we dex2oat because of boot image change, we are going to update
-            // in-place the vdex file.
-            in_vdex_wrapper_fd->reset(open(in_vdex_path_str.c_str(), O_RDWR, 0));
-        } else {
-            in_vdex_wrapper_fd->reset(open(in_vdex_path_str.c_str(), O_RDONLY, 0));
-        }
+        in_vdex_wrapper_fd->reset(open(in_vdex_path_str.c_str(), O_RDONLY, 0));
     }
 
     // Infer the name of the output VDEX and create it.
@@ -1257,27 +1251,12 @@
         return false;
     }
 
-    // If we are compiling because the boot image is out of date, we do not
-    // need to recreate a vdex, and can use the same existing one.
-    if (dexopt_action == DEX2OAT_FOR_BOOT_IMAGE &&
-            in_vdex_wrapper_fd->get() != -1 &&
-            in_vdex_path_str == out_vdex_path_str) {
-        // We unlink the file in case the invocation of dex2oat fails, to ensure we don't
-        // have bogus stale vdex files.
-        out_vdex_wrapper_fd->reset(
-              in_vdex_wrapper_fd->get(),
-              [out_vdex_path_str]() { unlink(out_vdex_path_str.c_str()); });
-        // Disable auto close for the in wrapper fd (it will be done when destructing the out
-        // wrapper).
-        in_vdex_wrapper_fd->DisableAutoClose();
-    } else {
-        out_vdex_wrapper_fd->reset(
-              open_output_file(out_vdex_path_str.c_str(), /*recreate*/true, /*permissions*/0644),
-              [out_vdex_path_str]() { unlink(out_vdex_path_str.c_str()); });
-        if (out_vdex_wrapper_fd->get() < 0) {
-            ALOGE("installd cannot open vdex'%s' during dexopt\n", out_vdex_path_str.c_str());
-            return false;
-        }
+    out_vdex_wrapper_fd->reset(
+          open_output_file(out_vdex_path_str.c_str(), /*recreate*/true, /*permissions*/0644),
+          [out_vdex_path_str]() { unlink(out_vdex_path_str.c_str()); });
+    if (out_vdex_wrapper_fd->get() < 0) {
+        ALOGE("installd cannot open vdex'%s' during dexopt\n", out_vdex_path_str.c_str());
+        return false;
     }
     if (!set_permissions_and_ownership(out_vdex_wrapper_fd->get(), is_public, uid,
             out_vdex_path_str.c_str(), is_secondary_dex)) {
diff --git a/cmds/installd/tests/installd_cache_test.cpp b/cmds/installd/tests/installd_cache_test.cpp
index 174ce77..aed068c 100644
--- a/cmds/installd/tests/installd_cache_test.cpp
+++ b/cmds/installd/tests/installd_cache_test.cpp
@@ -146,7 +146,7 @@
     EXPECT_EQ(0, exists("com.example/cache/foo/one"));
     EXPECT_EQ(0, exists("com.example/cache/foo/two"));
 
-    service->freeCache(testUuid, kTbInBytes,
+    service->freeCache(testUuid, kTbInBytes, 0,
             FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
 
     EXPECT_EQ(0, exists("com.example/normal"));
@@ -163,13 +163,13 @@
     touch("com.example/cache/foo/one", kMbInBytes, 60);
     touch("com.example/cache/foo/two", kMbInBytes, 120);
 
-    service->freeCache(testUuid, free() + kKbInBytes,
+    service->freeCache(testUuid, free() + kKbInBytes, 0,
             FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
 
     EXPECT_EQ(-1, exists("com.example/cache/foo/one"));
     EXPECT_EQ(0, exists("com.example/cache/foo/two"));
 
-    service->freeCache(testUuid, free() + kKbInBytes,
+    service->freeCache(testUuid, free() + kKbInBytes, 0,
             FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
 
     EXPECT_EQ(-1, exists("com.example/cache/foo/one"));
@@ -197,7 +197,7 @@
     EXPECT_EQ(2 * kMbInBytes, size("com.example/cache/bar/bar1"));
     EXPECT_EQ(2 * kMbInBytes, size("com.example/cache/bar/bar2"));
 
-    service->freeCache(testUuid, kTbInBytes,
+    service->freeCache(testUuid, kTbInBytes, 0,
             FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
 
     EXPECT_EQ(-1, exists("com.example/cache/foo/foo1"));
@@ -219,7 +219,7 @@
 
     setxattr("com.example/cache/foo", "user.cache_group");
 
-    service->freeCache(testUuid, free() + kKbInBytes,
+    service->freeCache(testUuid, free() + kKbInBytes, 0,
             FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
 
     EXPECT_EQ(-1, exists("com.example/cache/foo/foo1"));
@@ -264,7 +264,7 @@
     setxattr("com.example/cache/tomb", "user.cache_tombstone");
     setxattr("com.example/cache/tomb/group", "user.cache_group");
 
-    service->freeCache(testUuid, free() + kKbInBytes,
+    service->freeCache(testUuid, free() + kKbInBytes, 0,
             FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
 
     EXPECT_EQ(kMbInBytes, size("com.example/cache/group/file1"));
@@ -285,7 +285,7 @@
     EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file1"));
     EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file2"));
 
-    service->freeCache(testUuid, free() + kKbInBytes,
+    service->freeCache(testUuid, free() + kKbInBytes, 0,
             FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
 
     EXPECT_EQ(-1, size("com.example/cache/group/file1"));
@@ -306,7 +306,7 @@
     EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file1"));
     EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file2"));
 
-    service->freeCache(testUuid, kTbInBytes,
+    service->freeCache(testUuid, kTbInBytes, 0,
             FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
 
     EXPECT_EQ(-1, size("com.example/cache/group/file1"));
diff --git a/include/gui/Surface.h b/include/gui/Surface.h
index c836543..0f7e12a 100644
--- a/include/gui/Surface.h
+++ b/include/gui/Surface.h
@@ -160,6 +160,9 @@
 
     status_t getUniqueId(uint64_t* outId) const;
 
+    // Returns the CLOCK_MONOTONIC start time of the last dequeueBuffer call
+    nsecs_t getLastDequeueStartTime() const;
+
 protected:
     virtual ~Surface();
 
@@ -421,6 +424,9 @@
     nsecs_t mLastDequeueDuration = 0;
     nsecs_t mLastQueueDuration = 0;
 
+    // Stores the time right before we call IGBP::dequeueBuffer
+    nsecs_t mLastDequeueStartTime = 0;
+
     Condition mQueueBufferCondition;
 
     uint64_t mNextFrameNumber = 1;
diff --git a/include/gui/SurfaceComposerClient.h b/include/gui/SurfaceComposerClient.h
index ec310cf..145c059 100644
--- a/include/gui/SurfaceComposerClient.h
+++ b/include/gui/SurfaceComposerClient.h
@@ -271,6 +271,7 @@
     uint32_t getStride() const;
     // size of allocated memory in bytes
     size_t getSize() const;
+    android_dataspace getDataSpace() const;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index d471dbf..409a3cb 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -503,13 +503,13 @@
 
     int buf = -1;
     sp<Fence> fence;
-    nsecs_t now = systemTime();
+    nsecs_t startTime = systemTime();
 
     FrameEventHistoryDelta frameTimestamps;
     status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence,
             reqWidth, reqHeight, reqFormat, reqUsage,
             enableFrameTimestamps ? &frameTimestamps : nullptr);
-    mLastDequeueDuration = systemTime() - now;
+    mLastDequeueDuration = systemTime() - startTime;
 
     if (result < 0) {
         ALOGV("dequeueBuffer: IGraphicBufferProducer::dequeueBuffer"
@@ -526,6 +526,9 @@
 
     Mutex::Autolock lock(mMutex);
 
+    // Write this while holding the mutex
+    mLastDequeueStartTime = startTime;
+
     sp<GraphicBuffer>& gbuf(mSlots[buf].buffer);
 
     // this should never happen
@@ -1711,6 +1714,11 @@
     return mGraphicBufferProducer->getUniqueId(outId);
 }
 
+nsecs_t Surface::getLastDequeueStartTime() const {
+    Mutex::Autolock lock(mMutex);
+    return mLastDequeueStartTime;
+}
+
 status_t Surface::getAndFlushRemovedBuffers(std::vector<sp<GraphicBuffer>>* out) {
     if (out == nullptr) {
         ALOGE("%s: out must not be null!", __FUNCTION__);
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 8c83843..7ae2672 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1080,5 +1080,9 @@
     return mBuffer.stride * mBuffer.height * bytesPerPixel(mBuffer.format);
 }
 
+android_dataspace ScreenshotClient::getDataSpace() const {
+    return mBuffer.dataSpace;
+}
+
 // ----------------------------------------------------------------------------
 }; // namespace android
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index fcaa23a..81820de 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -393,6 +393,22 @@
     ASSERT_LE(removedBuffers.size(), 1u);
 }
 
+TEST_F(SurfaceTest, TestGetLastDequeueStartTime) {
+    sp<ANativeWindow> anw(mSurface);
+    ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(), NATIVE_WINDOW_API_CPU));
+
+    ANativeWindowBuffer* buffer = nullptr;
+    int32_t fenceFd = -1;
+
+    nsecs_t before = systemTime(CLOCK_MONOTONIC);
+    anw->dequeueBuffer(anw.get(), &buffer, &fenceFd);
+    nsecs_t after = systemTime(CLOCK_MONOTONIC);
+
+    nsecs_t lastDequeueTime = mSurface->getLastDequeueStartTime();
+    ASSERT_LE(before, lastDequeueTime);
+    ASSERT_GE(after, lastDequeueTime);
+}
+
 class FakeConsumer : public BnConsumerListener {
 public:
     void onFrameAvailable(const BufferItem& /*item*/) override {}
@@ -1568,4 +1584,4 @@
     EXPECT_EQ(-1, outDisplayPresentTime);
 }
 
-}
+} // namespace android
diff --git a/libs/math/include/math/TMatHelpers.h b/libs/math/include/math/TMatHelpers.h
index 5cb725d..1423ade 100644
--- a/libs/math/include/math/TMatHelpers.h
+++ b/libs/math/include/math/TMatHelpers.h
@@ -342,9 +342,9 @@
 template <typename MATRIX>
 String8 asString(const MATRIX& m) {
     String8 s;
-    for (size_t c = 0; c < MATRIX::col_size(); c++) {
+    for (size_t c = 0; c < MATRIX::COL_SIZE; c++) {
         s.append("|  ");
-        for (size_t r = 0; r < MATRIX::row_size(); r++) {
+        for (size_t r = 0; r < MATRIX::ROW_SIZE; r++) {
             s.appendFormat("%7.2f  ", m[r][c]);
         }
         s.append("|\n");
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index c0602e7..2f4b996 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -134,7 +134,10 @@
     if (!buffer) return BAD_VALUE;
 
     GraphicBuffer* gBuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
-    return gBuffer->unlockAsync(fence);
+    if (fence == nullptr)
+        return gBuffer->unlock();
+    else
+        return gBuffer->unlockAsync(fence);
 }
 
 int AHardwareBuffer_sendHandleToUnixSocket(const AHardwareBuffer* buffer, int socketFd) {
diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp
index 94b68e7..d5676cc 100644
--- a/libs/ui/DebugUtils.cpp
+++ b/libs/ui/DebugUtils.cpp
@@ -59,24 +59,20 @@
         case 0:
             switch (dataspace & 0xffff) {
                 case HAL_DATASPACE_JFIF:
-                    return std::string("(deprecated) JFIF (BT601_625, SMPTE_170M Full range)");
+                    return std::string("(deprecated) JFIF (BT601_625)");
 
                 case HAL_DATASPACE_BT601_625:
-                    return std::string("(deprecated) BT601_625 (BT601_625, SMPTE_170M Limited "
-                                       "range)");
+                    return std::string("(deprecated) BT601_625");
 
                 case HAL_DATASPACE_BT601_525:
-                    return std::string("(deprecated) BT601_525 (BT601_525, SMPTE_170M Limited "
-                                       "range)");
+                    return std::string("(deprecated) BT601_525");
 
                 case HAL_DATASPACE_SRGB_LINEAR:
-                    return std::string("(deprecated) SRGB Linear Full range");
-
                 case HAL_DATASPACE_SRGB:
                     return std::string("(deprecated) sRGB");
 
                 case HAL_DATASPACE_V0_BT709:
-                    return std::string("(deprecated) BT709 (BT709, SMPTE_170M Limited range)");
+                    return std::string("(deprecated) BT709");
 
                 case HAL_DATASPACE_ARBITRARY:
                     return std::string("ARBITRARY");
@@ -93,6 +89,29 @@
 }
 
 std::string decodeTransfer(android_dataspace dataspace) {
+    const uint32_t dataspaceSelect = (dataspace & HAL_DATASPACE_STANDARD_MASK);
+    if (dataspaceSelect == 0) {
+        switch (dataspace & 0xffff) {
+            case HAL_DATASPACE_JFIF:
+            case HAL_DATASPACE_BT601_625:
+            case HAL_DATASPACE_BT601_525:
+            case HAL_DATASPACE_V0_BT709:
+                return std::string("SMPTE_170M");
+
+            case HAL_DATASPACE_SRGB_LINEAR:
+            case HAL_DATASPACE_ARBITRARY:
+                return std::string("Linear");
+
+            case HAL_DATASPACE_SRGB:
+                return std::string("sRGB");
+
+            case HAL_DATASPACE_UNKNOWN:
+            // Fallthrough
+            default:
+                return std::string("");
+        }
+    }
+
     const uint32_t dataspaceTransfer = (dataspace & HAL_DATASPACE_TRANSFER_MASK);
     switch (dataspaceTransfer) {
         case HAL_DATASPACE_TRANSFER_UNSPECIFIED:
@@ -127,6 +146,27 @@
 }
 
 std::string decodeRange(android_dataspace dataspace) {
+    const uint32_t dataspaceSelect = (dataspace & HAL_DATASPACE_STANDARD_MASK);
+    if (dataspaceSelect == 0) {
+        switch (dataspace & 0xffff) {
+            case HAL_DATASPACE_JFIF:
+            case HAL_DATASPACE_SRGB_LINEAR:
+            case HAL_DATASPACE_SRGB:
+                return std::string("Full range");
+
+            case HAL_DATASPACE_BT601_625:
+            case HAL_DATASPACE_BT601_525:
+            case HAL_DATASPACE_V0_BT709:
+                return std::string("Limited range)");
+
+            case HAL_DATASPACE_ARBITRARY:
+            case HAL_DATASPACE_UNKNOWN:
+            // Fallthrough
+            default:
+                return std::string("unspecified range");
+        }
+    }
+
     const uint32_t dataspaceRange = (dataspace & HAL_DATASPACE_RANGE_MASK);
     switch (dataspaceRange) {
         case HAL_DATASPACE_RANGE_UNSPECIFIED:
diff --git a/libs/vr/libdvr/dvr_buffer.cpp b/libs/vr/libdvr/dvr_buffer.cpp
index 82469b8..4d9b215 100644
--- a/libs/vr/libdvr/dvr_buffer.cpp
+++ b/libs/vr/libdvr/dvr_buffer.cpp
@@ -1,6 +1,7 @@
 #include "include/dvr/dvr_buffer.h"
 
 #include <android/hardware_buffer.h>
+#include <dvr/dvr_shared_buffers.h>
 #include <private/dvr/buffer_hub_client.h>
 #include <ui/GraphicBuffer.h>
 
@@ -176,6 +177,11 @@
                                   hardware_buffer);
 }
 
+// Retrieve the shared buffer layout version defined in dvr_shared_buffers.h.
+int dvrBufferGlobalLayoutVersionGet() {
+  return android::dvr::kSharedBufferLayoutVersion;
+}
+
 const struct native_handle* dvrWriteBufferGetNativeHandle(
     DvrWriteBuffer* write_buffer) {
   if (!write_buffer || !write_buffer->write_buffer)
diff --git a/libs/vr/libdvr/dvr_surface.cpp b/libs/vr/libdvr/dvr_surface.cpp
index 8d4b3f5..b7c127a 100644
--- a/libs/vr/libdvr/dvr_surface.cpp
+++ b/libs/vr/libdvr/dvr_surface.cpp
@@ -171,8 +171,6 @@
 
   auto status = client->GetGlobalBuffer(key);
   if (!status) {
-    ALOGE("dvrGetGlobalBuffer: Failed to find named buffer key=%d: %s", key,
-          status.GetErrorMessage().c_str());
     return -status.error();
   }
   *out_buffer = CreateDvrBufferFromIonBuffer(status.take());
diff --git a/libs/vr/libdvr/include/dvr/dvr_api.h b/libs/vr/libdvr/include/dvr/dvr_api.h
index c687a63..0c10907 100644
--- a/libs/vr/libdvr/include/dvr/dvr_api.h
+++ b/libs/vr/libdvr/include/dvr/dvr_api.h
@@ -145,6 +145,7 @@
 typedef void (*DvrBufferDestroyPtr)(DvrBuffer* buffer);
 typedef int (*DvrBufferGetAHardwareBufferPtr)(
     DvrBuffer* buffer, AHardwareBuffer** hardware_buffer);
+typedef int (*DvrBufferGlobalLayoutVersionGetPtr)();
 typedef const struct native_handle* (*DvrBufferGetNativeHandlePtr)(
     DvrBuffer* buffer);
 
diff --git a/libs/vr/libdvr/include/dvr/dvr_api_entries.h b/libs/vr/libdvr/include/dvr/dvr_api_entries.h
index 8cdad7d..802a0f7 100644
--- a/libs/vr/libdvr/include/dvr/dvr_api_entries.h
+++ b/libs/vr/libdvr/include/dvr/dvr_api_entries.h
@@ -60,6 +60,7 @@
 DVR_V1_API_ENTRY(BufferDestroy);
 DVR_V1_API_ENTRY(BufferGetAHardwareBuffer);
 DVR_V1_API_ENTRY(BufferGetNativeHandle);
+DVR_V1_API_ENTRY(BufferGlobalLayoutVersionGet);
 
 // Write buffer queue
 DVR_V1_API_ENTRY(WriteBufferQueueDestroy);
diff --git a/libs/vr/libdvr/include/dvr/dvr_buffer.h b/libs/vr/libdvr/include/dvr/dvr_buffer.h
index af55698..935a7b2 100644
--- a/libs/vr/libdvr/include/dvr/dvr_buffer.h
+++ b/libs/vr/libdvr/include/dvr/dvr_buffer.h
@@ -95,6 +95,9 @@
 int dvrBufferGetAHardwareBuffer(DvrBuffer* buffer,
                                 AHardwareBuffer** hardware_buffer);
 
+// Retrieve the shared buffer layout version defined in dvr_shared_buffers.h.
+int dvrBufferGlobalLayoutVersionGet();
+
 // TODO(eieio): Switch to return int and take an out parameter for the native
 // handle.
 const struct native_handle* dvrBufferGetNativeHandle(DvrBuffer* buffer);
diff --git a/libs/vr/libdvr/include/dvr/dvr_vrflinger_config.h b/libs/vr/libdvr/include/dvr/dvr_config.h
similarity index 74%
rename from libs/vr/libdvr/include/dvr/dvr_vrflinger_config.h
rename to libs/vr/libdvr/include/dvr/dvr_config.h
index cfe9d62..3d2c066 100644
--- a/libs/vr/libdvr/include/dvr/dvr_vrflinger_config.h
+++ b/libs/vr/libdvr/include/dvr/dvr_config.h
@@ -1,17 +1,18 @@
-#ifndef ANDROID_DVR_VRFLINGER_CONFIG_H
-#define ANDROID_DVR_VRFLINGER_CONFIG_H
+#ifndef ANDROID_DVR_CONFIG_H
+#define ANDROID_DVR_CONFIG_H
 
 // This header is shared by VrCore and Android and must be kept in sync.
 
+#include <stdint.h>
 #include <sys/cdefs.h>
 
 __BEGIN_DECLS
 
 // This is a shared memory buffer for passing config data from VrCore to
 // libvrflinger in SurfaceFlinger.
-struct DvrVrFlingerConfig {
+struct __attribute__((packed, aligned(16))) DvrConfig {
   // Offset before vsync to submit frames to hardware composer.
-  int frame_post_offset_ns{4000000};
+  int32_t frame_post_offset_ns{4000000};
 
   // If the number of pending fences goes over this count at the point when we
   // are about to submit a new frame to HWC, we will drop the frame. This
@@ -20,11 +21,14 @@
   // the next vsync, at the point when the DMA to the display completes.
   // Currently we use a smart display and the EDS timing coincides with zero
   // pending fences, so this is 0.
-  size_t allowed_pending_fence_count{0};
+  int32_t allowed_pending_fence_count{0};
 
   // New fields should always be added to the end for backwards compat.
+
+  // Reserved padding to 16 bytes.
+  uint8_t pad[8];
 };
 
 __END_DECLS
 
-#endif  // ANDROID_DVR_VRFLINGER_CONFIG_H
+#endif  // ANDROID_DVR_CONFIG_H
diff --git a/libs/vr/libdvr/include/dvr/dvr_hardware_composer_client.h b/libs/vr/libdvr/include/dvr/dvr_hardware_composer_client.h
index 60e22de..0ba76e2 100644
--- a/libs/vr/libdvr/include/dvr/dvr_hardware_composer_client.h
+++ b/libs/vr/libdvr/include/dvr/dvr_hardware_composer_client.h
@@ -24,37 +24,73 @@
 DvrHwcClient* dvrHwcClientCreate(DvrHwcOnFrameCallback callback,
                                  void* client_state);
 
+// Called to free the DvrHwcClient pointer.
 void dvrHwcClientDestroy(DvrHwcClient* client);
 
 // Called to free the frame information.
+// @param frame Pointer for the valid frame used for the query.
 void dvrHwcFrameDestroy(DvrHwcFrame* frame);
 
+// @param frame Pointer for the valid frame used for the query.
+// @return Identifier for the display associated by the frame.
 DvrHwcDisplay dvrHwcFrameGetDisplayId(DvrHwcFrame* frame);
 
+// @param frame Pointer for the valid frame used for the query.
+// @return width of the physical display associated with |frame|. This does not
+// take into account any orientation changes.
 int32_t dvrHwcFrameGetDisplayWidth(DvrHwcFrame* frame);
 
+// @param frame Pointer for the valid frame used for the query.
+// @return height of the physical display associated with |frame|. This does not
+// take into account any orientation changes.
 int32_t dvrHwcFrameGetDisplayHeight(DvrHwcFrame* frame);
 
+// @param frame Pointer for the valid frame used for the query.
 // @return True if the display has been removed. In this case the current frame
 // does not contain any valid layers to display. It is a signal to clean up any
 // display related state.
 bool dvrHwcFrameGetDisplayRemoved(DvrHwcFrame* frame);
 
+// @param frame Pointer for the valid frame used for the query.
 // @return Number of layers in the frame.
 size_t dvrHwcFrameGetLayerCount(DvrHwcFrame* frame);
 
+// @param frame Pointer for the valid frame used for the query.
+// @return The ID of the currently active display configuration.
 uint32_t dvrHwcFrameGetActiveConfig(DvrHwcFrame* frame);
+
+// @param frame Pointer for the valid frame used for the query.
+// @return The ID of the current color mode. See HAL_COLOR_MODE_* for valid
+// values.
 uint32_t dvrHwcFrameGetColorMode(DvrHwcFrame* frame);
+
+// @param frame Pointer for the valid frame used for the query.
+// @param out_matrix Output parameter for a float[16] array which will be filled
+// with the color transform matrix.
+// @param out_hint Output parameter which will contain the color transform hint.
+// See HAL_COLOR_TRANSFORM_* for valid values.
 void dvrHwcFrameGetColorTransform(DvrHwcFrame* frame, float* out_matrix,
                                   int32_t* out_hint);
+
+// @param frame Pointer for the valid frame used for the query.
+// @return The current power mode for the display. See HWC2_POWER_MODE_* for
+// valid values.
 uint32_t dvrHwcFrameGetPowerMode(DvrHwcFrame* frame);
+
+// @param frame Pointer for the valid frame used for the query.
+// @return The current state of vsync. See HWC2_VSYNC_* for valid values.
 uint32_t dvrHwcFrameGetVsyncEnabled(DvrHwcFrame* frame);
 
+// @param frame Pointer for the valid frame used for the query.
+// @param layer_index The index of the layer in the frame.
+// @return A unique ID for the layer.
 DvrHwcLayer dvrHwcFrameGetLayerId(DvrHwcFrame* frame, size_t layer_index);
 
 // Return the graphic buffer associated with the layer at |layer_index| in
 // |frame|.
 //
+// @param frame Pointer for the valid frame used for the query.
+// @param layer_index The index of the layer in the frame.
 // @return Graphic buffer. Caller owns the buffer and is responsible for freeing
 // it. (see AHardwareBuffer_release())
 AHardwareBuffer* dvrHwcFrameGetLayerBuffer(DvrHwcFrame* frame,
@@ -62,42 +98,98 @@
 
 // Returns the fence FD for the layer at index |layer_index| in |frame|.
 //
+// @param frame Pointer for the valid frame used for the query.
+// @param layer_index The index of the layer in the frame.
 // @return Fence FD. Caller owns the FD and is responsible for closing it.
 int dvrHwcFrameGetLayerFence(DvrHwcFrame* frame, size_t layer_index);
 
+// @param frame Pointer for the valid frame used for the query.
+// @param layer_index The index of the layer in the frame.
+// @return describing the portion of the display covered by the layer. Will
+// not exceed the display dimensions.
 DvrHwcRecti dvrHwcFrameGetLayerDisplayFrame(DvrHwcFrame* frame,
                                             size_t layer_index);
 
+// @param frame Pointer for the valid frame used for the query.
+// @param layer_index The index of the layer in the frame.
+// @return describing the portion of the layer that will fill the display
+// frame. Will not exceed the layer dimensions.
 DvrHwcRectf dvrHwcFrameGetLayerCrop(DvrHwcFrame* frame, size_t layer_index);
 
+// @param frame Pointer for the valid frame used for the query.
+// @param layer_index The index of the layer in the frame.
+// @return The blend mode of the layer.
 DvrHwcBlendMode dvrHwcFrameGetLayerBlendMode(DvrHwcFrame* frame,
                                              size_t layer_index);
 
+// @param frame Pointer for the valid frame used for the query.
+// @param layer_index The index of the layer in the frame.
+// @return The alpha value to be applied to the whole layer. Will be in the
+// [0.0, 1.0] range.
 float dvrHwcFrameGetLayerAlpha(DvrHwcFrame* frame, size_t layer_index);
 
+// @param frame Pointer for the valid frame used for the query.
+// @param layer_index The index of the layer in the frame.
+// @return The type of the layer assigned by the window manager.
 uint32_t dvrHwcFrameGetLayerType(DvrHwcFrame* frame, size_t layer_index);
 
+// @param frame Pointer for the valid frame used for the query.
+// @param layer_index The index of the layer in the frame.
+// @return The application id the layer belongs to.
 uint32_t dvrHwcFrameGetLayerApplicationId(DvrHwcFrame* frame,
                                           size_t layer_index);
 
+// @param frame Pointer for the valid frame used for the query.
+// @param layer_index The index of the layer in the frame.
+// @return The z-order for the layer.
 uint32_t dvrHwcFrameGetLayerZOrder(DvrHwcFrame* frame, size_t layer_index);
 
+// @param frame Pointer for the valid frame used for the query.
+// @param layer_index The index of the layer in the frame.
+// @param out_x Output parameter for the x coordinate of the cursor location.
+// @param out_y Output parameter for the y coordinate of the cursor location.
 void dvrHwcFrameGetLayerCursor(DvrHwcFrame* frame, size_t layer_index,
                                int32_t* out_x, int32_t* out_y);
 
+// @param frame Pointer for the valid frame used for the query.
+// @param layer_index The index of the layer in the frame.
+// @return The transformation that needs to be applied to the layer before
+// presenting it. See DVR_HWC_TRANSFORM_* for valid values.
 uint32_t dvrHwcFrameGetLayerTransform(DvrHwcFrame* frame, size_t layer_index);
 
+// @param frame Pointer for the valid frame used for the query.
+// @param layer_index The index of the layer in the frame.
+// @return The dataspace which represents how the pixel values should be
+// interpreted. See HAL_DATASPACE_* for valid values.
 uint32_t dvrHwcFrameGetLayerDataspace(DvrHwcFrame* frame, size_t layer_index);
 
+// @param frame Pointer for the valid frame used for the query.
+// @param layer_index The index of the layer in the frame.
+// @return The color of the layer if layer composition is SOLID_COLOR.
 uint32_t dvrHwcFrameGetLayerColor(DvrHwcFrame* frame, size_t layer_index);
 
+// @param frame Pointer for the valid frame used for the query.
+// @param layer_index The index of the layer in the frame.
+// @return The number of visible regions.
 uint32_t dvrHwcFrameGetLayerNumVisibleRegions(DvrHwcFrame* frame,
                                               size_t layer_index);
+// @param frame Pointer for the valid frame used for the query.
+// @param layer_index The index of the layer in the frame.
+// @param index The index of the visible region for the layer.
+// @return The rectangle describing the visible region.
 DvrHwcRecti dvrHwcFrameGetLayerVisibleRegion(DvrHwcFrame* frame,
                                              size_t layer_index, size_t index);
 
+// @param frame Pointer for the valid frame used for the query.
+// @param layer_index The index of the layer in the frame.
+// @return The number of damanged regions.
 uint32_t dvrHwcFrameGetLayerNumDamagedRegions(DvrHwcFrame* frame,
                                               size_t layer_index);
+
+// @param frame Pointer for the valid frame used for the query.
+// @param layer_index The index of the layer in the frame.
+// @param index The index of the damanged region for the layer.
+// @return The rectangle describing the damaged region.
 DvrHwcRecti dvrHwcFrameGetLayerDamagedRegion(DvrHwcFrame* frame,
                                              size_t layer_index, size_t index);
 #ifdef __cplusplus
diff --git a/libs/vr/libdvr/include/dvr/dvr_hardware_composer_types.h b/libs/vr/libdvr/include/dvr/dvr_hardware_composer_types.h
index 36c30f9..1d5eda6 100644
--- a/libs/vr/libdvr/include/dvr/dvr_hardware_composer_types.h
+++ b/libs/vr/libdvr/include/dvr/dvr_hardware_composer_types.h
@@ -26,6 +26,15 @@
   DVR_HWC_COMPOSITION_SIDEBAND = 5,
 };
 
+enum DvrHwcTransform {
+  DVR_HWC_TRANSFORM_NONE = 0,
+  DVR_HWC_TRANSFORM_FLIP_H = 1,
+  DVR_HWC_TRANSFORM_FLIP_V = 2,
+  DVR_HWC_TRANSFORM_ROT_90 = 4,
+  DVR_HWC_TRANSFORM_ROT_180 = 3,
+  DVR_HWC_TRANSFORM_ROT_270 = 7,
+};
+
 typedef uint64_t DvrHwcDisplay;
 typedef uint64_t DvrHwcLayer;
 
diff --git a/libs/vr/libdvr/include/dvr/dvr_shared_buffers.h b/libs/vr/libdvr/include/dvr/dvr_shared_buffers.h
index ce17f0c..096f800 100644
--- a/libs/vr/libdvr/include/dvr/dvr_shared_buffers.h
+++ b/libs/vr/libdvr/include/dvr/dvr_shared_buffers.h
@@ -1,8 +1,8 @@
 #ifndef ANDROID_DVR_SHARED_BUFFERS_H_
 #define ANDROID_DVR_SHARED_BUFFERS_H_
 
+#include <dvr/dvr_config.h>
 #include <dvr/dvr_pose.h>
-#include <dvr/dvr_vrflinger_config.h>
 #include <dvr/dvr_vsync.h>
 #include <libbroadcastring/broadcast_ring.h>
 
@@ -11,7 +11,7 @@
 namespace dvr {
 
 // Increment when the layout for the buffers change.
-constexpr uint32_t kSharedBufferLayoutVersion = 1;
+enum : uint32_t { kSharedBufferLayoutVersion = 1 };
 
 // Note: These buffers will be mapped from various system processes as well
 // as VrCore and the application processes in a r/w manner.
@@ -26,6 +26,7 @@
 static_assert(sizeof(DvrPoseAsync) == 128, "Unexpected size for DvrPoseAsync");
 static_assert(sizeof(DvrPose) == 96, "Unexpected size for DvrPose");
 static_assert(sizeof(DvrVsync) == 32, "Unexpected size for DvrVsync");
+static_assert(sizeof(DvrConfig) == 16, "Unexpected size for DvrConfig");
 
 // A helper class that provides compile time sized traits for the BroadcastRing.
 template <class DvrType, size_t StaticCount>
@@ -41,13 +42,12 @@
 // Traits classes.
 using DvrPoseTraits = DvrRingBufferTraits<DvrPose, 0>;
 using DvrVsyncTraits = DvrRingBufferTraits<DvrVsync, 2>;
-using DvrVrFlingerConfigTraits = DvrRingBufferTraits<DvrVrFlingerConfig, 2>;
+using DvrConfigTraits = DvrRingBufferTraits<DvrConfig, 2>;
 
 // The broadcast ring classes that will expose the data.
 using DvrPoseRing = BroadcastRing<DvrPose, DvrPoseTraits>;
 using DvrVsyncRing = BroadcastRing<DvrVsync, DvrVsyncTraits>;
-using DvrVrFlingerConfigRing =
-    BroadcastRing<DvrVrFlingerConfig, DvrVrFlingerConfigTraits>;
+using DvrConfigRing = BroadcastRing<DvrConfig, DvrConfigTraits>;
 
 // This is a shared memory buffer for passing pose data estimated at vsyncs.
 //
diff --git a/libs/vr/libdvr/tests/dvr_named_buffer-test.cpp b/libs/vr/libdvr/tests/dvr_named_buffer-test.cpp
index 419083f..566f9de 100644
--- a/libs/vr/libdvr/tests/dvr_named_buffer-test.cpp
+++ b/libs/vr/libdvr/tests/dvr_named_buffer-test.cpp
@@ -1,9 +1,9 @@
 #include <android/hardware_buffer.h>
 #include <dvr/dvr_buffer.h>
+#include <dvr/dvr_config.h>
 #include <dvr/dvr_display_manager.h>
 #include <dvr/dvr_shared_buffers.h>
 #include <dvr/dvr_surface.h>
-#include <dvr/dvr_vrflinger_config.h>
 #include <system/graphics.h>
 
 #include <base/logging.h>
@@ -285,8 +285,8 @@
   const uint64_t usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
                          AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY;
 
-  size_t correct_size = DvrVrFlingerConfigRing::MemorySize();
-  size_t wrong_size = DvrVrFlingerConfigRing::MemorySize(0);
+  size_t correct_size = DvrConfigRing::MemorySize();
+  size_t wrong_size = DvrConfigRing::MemorySize(0);
 
   // Setup an invalid config buffer (too small) and assert that it fails.
   DvrBuffer* setup_buffer = nullptr;
diff --git a/libs/vr/libpdx/Android.bp b/libs/vr/libpdx/Android.bp
index 69300d6..f55e994 100644
--- a/libs/vr/libpdx/Android.bp
+++ b/libs/vr/libpdx/Android.bp
@@ -37,6 +37,7 @@
         "libpdx",
         "liblog",
         "libutils",
+        "libvndksupport",
     ],
 }
 
diff --git a/libs/vr/libpdx/private/pdx/rpc/variant.h b/libs/vr/libpdx/private/pdx/rpc/variant.h
index cb44a51..dc321fa 100644
--- a/libs/vr/libpdx/private/pdx/rpc/variant.h
+++ b/libs/vr/libpdx/private/pdx/rpc/variant.h
@@ -429,7 +429,7 @@
   // Handles assignment from the empty type. This overload supports assignment
   // in visitors using generic lambdas.
   Variant& operator=(EmptyVariant) {
-    Assign(EmptyVariant{});
+    Destruct();
     return *this;
   }
 
@@ -541,7 +541,10 @@
   void Construct(EmptyVariant) {}
 
   // Destroys the active element of the Variant.
-  void Destruct() { value_.Destruct(index_); }
+  void Destruct() {
+    value_.Destruct(index_);
+    index_ = kEmptyIndex;
+  }
 
   // Assigns the Variant when non-empty and the current type matches the target
   // type, otherwise destroys the current value and constructs a element of the
@@ -562,8 +565,6 @@
       Construct(std::forward<T>(value));
     }
   }
-  // Handles assignment from an empty Variant.
-  void Assign(EmptyVariant) { Destruct(); }
 };
 
 // Utility type to extract/convert values from a variant. This class simplifies
diff --git a/libs/vr/libpdx/variant_tests.cpp b/libs/vr/libpdx/variant_tests.cpp
index 325f33f..b1ebc9b 100644
--- a/libs/vr/libpdx/variant_tests.cpp
+++ b/libs/vr/libpdx/variant_tests.cpp
@@ -584,6 +584,25 @@
     EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
     EXPECT_EQ(1u, InstrumentType<int>::copy_assignment_count());
   }
+
+  {
+    InstrumentType<int>::clear();
+
+    // Construct from temporary, temporary ctor/dtor.
+    Variant<int, InstrumentType<int>> v(InstrumentType<int>(25));
+
+    // Assign EmptyVariant.
+    v = EmptyVariant{};
+
+    EXPECT_EQ(2u, InstrumentType<int>::constructor_count());
+    EXPECT_EQ(2u, InstrumentType<int>::destructor_count());
+    EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+    EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
+  }
+  EXPECT_EQ(2u, InstrumentType<int>::constructor_count());
+  EXPECT_EQ(2u, InstrumentType<int>::destructor_count());
+  EXPECT_EQ(0u, InstrumentType<int>::move_assignment_count());
+  EXPECT_EQ(0u, InstrumentType<int>::copy_assignment_count());
 }
 
 TEST(Variant, MoveConstructor) {
@@ -758,7 +777,7 @@
     Variant<std::string> b;
 
     std::swap(a, b);
-    EXPECT_TRUE(!a.empty());
+    EXPECT_TRUE(a.empty());
     EXPECT_TRUE(!b.empty());
     ASSERT_TRUE(b.is<std::string>());
     EXPECT_EQ("1", std::get<std::string>(b));
@@ -770,7 +789,7 @@
 
     std::swap(a, b);
     EXPECT_TRUE(!a.empty());
-    EXPECT_TRUE(!b.empty());
+    EXPECT_TRUE(b.empty());
     ASSERT_TRUE(a.is<std::string>());
     EXPECT_EQ("1", std::get<std::string>(a));
   }
diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp
index 7a78d1f..c18ae82 100644
--- a/libs/vr/libvrflinger/hardware_composer.cpp
+++ b/libs/vr/libvrflinger/hardware_composer.cpp
@@ -481,9 +481,9 @@
 
 int HardwareComposer::MapConfigBuffer(IonBuffer& ion_buffer) {
   std::lock_guard<std::mutex> lock(shared_config_mutex_);
-  shared_config_ring_ = DvrVrFlingerConfigRing();
+  shared_config_ring_ = DvrConfigRing();
 
-  if (ion_buffer.width() < DvrVrFlingerConfigRing::MemorySize()) {
+  if (ion_buffer.width() < DvrConfigRing::MemorySize()) {
     ALOGE("HardwareComposer::MapConfigBuffer: invalid buffer size.");
     return -EINVAL;
   }
@@ -497,8 +497,7 @@
     return -EPERM;
   }
 
-  shared_config_ring_ =
-      DvrVrFlingerConfigRing::Create(buffer_base, ion_buffer.width());
+  shared_config_ring_ = DvrConfigRing::Create(buffer_base, ion_buffer.width());
   ion_buffer.Unlock();
 
   return 0;
@@ -506,7 +505,7 @@
 
 void HardwareComposer::ConfigBufferDeleted() {
   std::lock_guard<std::mutex> lock(shared_config_mutex_);
-  shared_config_ring_ = DvrVrFlingerConfigRing();
+  shared_config_ring_ = DvrConfigRing();
 }
 
 void HardwareComposer::UpdateConfigBuffer() {
@@ -514,7 +513,7 @@
   if (!shared_config_ring_.is_valid())
     return;
   // Copy from latest record in shared_config_ring_ to local copy.
-  DvrVrFlingerConfig record;
+  DvrConfig record;
   if (shared_config_ring_.GetNewest(&shared_config_ring_sequence_, &record)) {
     post_thread_config_ = record;
   }
diff --git a/libs/vr/libvrflinger/hardware_composer.h b/libs/vr/libvrflinger/hardware_composer.h
index de6f9ff..98e8905 100644
--- a/libs/vr/libvrflinger/hardware_composer.h
+++ b/libs/vr/libvrflinger/hardware_composer.h
@@ -17,7 +17,7 @@
 #include <tuple>
 #include <vector>
 
-#include <dvr/dvr_vrflinger_config.h>
+#include <dvr/dvr_config.h>
 #include <dvr/dvr_vsync.h>
 #include <pdx/file_handle.h>
 #include <pdx/rpc/variant.h>
@@ -452,10 +452,10 @@
   std::unique_ptr<CPUMappedBroadcastRing<DvrVsyncRing>> vsync_ring_;
 
   // Broadcast ring for receiving config data from the DisplayManager.
-  DvrVrFlingerConfigRing shared_config_ring_;
+  DvrConfigRing shared_config_ring_;
   uint32_t shared_config_ring_sequence_{0};
   // Config buffer for reading from the post thread.
-  DvrVrFlingerConfig post_thread_config_;
+  DvrConfig post_thread_config_;
   std::mutex shared_config_mutex_;
 
   static constexpr int kPostThreadInterrupted = 1;
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 6bff38a..835e72b 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -897,19 +897,15 @@
             egl_tls_t::setContext(EGL_NO_CONTEXT);
         }
     } else {
-        // Force return to current context for drivers that cannot handle errors
-        EGLBoolean restore_result = EGL_FALSE;
 
-        // get a reference to the old current objects
-        ContextRef _c2(dp.get(), cur_c);
-        SurfaceRef _d2(dp.get(), cur_c->draw);
-        SurfaceRef _r2(dp.get(), cur_c->read);
+        if (cur_c != NULL) {
+            // Force return to current context for drivers that cannot handle errors
+            EGLBoolean restore_result = EGL_FALSE;
+            // get a reference to the old current objects
+            ContextRef _c2(dp.get(), cur_c);
+            SurfaceRef _d2(dp.get(), cur_c->draw);
+            SurfaceRef _r2(dp.get(), cur_c->read);
 
-        if (cur_c == NULL) {
-            restore_result = dp->makeCurrent(c, cur_c,
-                    EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT,
-                    EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-        } else {
             c = cur_c;
             impl_ctx = c->context;
             impl_draw = EGL_NO_SURFACE;
@@ -925,13 +921,13 @@
             restore_result = dp->makeCurrent(c, cur_c,
                     cur_c->draw, cur_c->read, cur_c->context,
                     impl_draw, impl_read, impl_ctx);
-        }
-        if (restore_result == EGL_TRUE) {
-            _c2.acquire();
-            _r2.acquire();
-            _d2.acquire();
-        } else {
-            ALOGE("Could not restore original EGL context");
+            if (restore_result == EGL_TRUE) {
+                _c2.acquire();
+                _r2.acquire();
+                _d2.acquire();
+            } else {
+                ALOGE("Could not restore original EGL context");
+            }
         }
         // this will ALOGE the error
         egl_connection_t* const cnx = &gEGLImpl;
diff --git a/services/inputflinger/InputWindow.h b/services/inputflinger/InputWindow.h
index feca6cf..0ac868b 100644
--- a/services/inputflinger/InputWindow.h
+++ b/services/inputflinger/InputWindow.h
@@ -101,7 +101,7 @@
         TYPE_NAVIGATION_BAR     = FIRST_SYSTEM_WINDOW+19,
         TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20,
         TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21,
-        TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW+22,
+        TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW+27,
         TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW+34,
         LAST_SYSTEM_WINDOW      = 2999,
     };
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 0366630..263ff00 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -856,6 +856,10 @@
 
 Error Layer::setDataspace(android_dataspace_t dataspace)
 {
+    if (dataspace == mDataSpace) {
+        return Error::None;
+    }
+    mDataSpace = dataspace;
     auto intDataspace = static_cast<Hwc2::Dataspace>(dataspace);
     auto intError = mDevice.mComposer->setLayerDataspace(mDisplayId,
             mId, intDataspace);
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 97582a7..15a43df 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -326,6 +326,7 @@
     hwc2_display_t mDisplayId;
     Device& mDevice;
     hwc2_layer_t mId;
+    android_dataspace mDataSpace = HAL_DATASPACE_UNKNOWN;
 };
 
 } // namespace HWC2
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index d19f964..1b9a230 100755
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -401,6 +401,10 @@
     Transform t = getTransform();
     win = t.transform(win);
 
+    if (!s.finalCrop.isEmpty()) {
+        win.intersect(s.finalCrop, &win);
+    }
+
     const sp<Layer>& p = getParent();
     // Now we need to calculate the parent bounds, so we can clip ourselves to those.
     // When calculating the parent bounds for purposes of clipping,
@@ -2531,6 +2535,14 @@
     return mSurfaceFlingerConsumer->getTransformToDisplayInverse();
 }
 
+size_t Layer::getChildrenCount() const {
+    size_t count = 0;
+    for (const sp<Layer>& child : mCurrentChildren) {
+        count += 1 + child->getChildrenCount();
+    }
+    return count;
+}
+
 void Layer::addChild(const sp<Layer>& layer) {
     mCurrentChildren.add(layer);
     layer->setParent(this);
@@ -2555,7 +2567,25 @@
     }
 
     for (const sp<Layer>& child : mCurrentChildren) {
-        newParent->addChild(child);
+        // We don't call addChild as we need to delay updating the child's parent pointer until
+        // a transaction occurs. Remember a refresh could occur in between now and the next
+        // transaction, in which case the Layer's parent pointer would be updated, but changes
+        // made to the parent in the same transaction would not have applied.
+        // This means that the following kind of scenario wont work:
+        //
+        // 1. Existing and visible child and parent surface exist
+        // 2. Create new surface hidden
+        // 3. Open transaction
+        // 4. Show the new surface, and reparent the old surface's children to it.
+        // 5. Close transaction.
+        //
+        // If we were to update the parent pointer immediately, then the child surface
+        // could disappear for one frame as it pointed at the new parent which
+        // hasn't yet become visible as the transaction hasn't yet occurred.
+        //
+        // Instead we defer the reparenting to commitChildList which happens as part
+        // of the global transaction.
+        newParent->mCurrentChildren.add(child);
 
         sp<Client> client(child->mClientRef.promote());
         if (client != nullptr) {
@@ -2725,6 +2755,8 @@
 void Layer::commitChildList() {
     for (size_t i = 0; i < mCurrentChildren.size(); i++) {
         const auto& child = mCurrentChildren[i];
+        child->setParent(this);
+
         child->commitChildList();
     }
     mDrawingChildren = mCurrentChildren;
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index c9ebf99..6955d73 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -518,6 +518,7 @@
                                  const LayerVector::Visitor& visitor);
     void traverseInZOrder(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor);
 
+    size_t getChildrenCount() const;
     void addChild(const sp<Layer>& layer);
     // Returns index if removed, or negative value otherwise
     // for symmetry with Vector::remove
diff --git a/services/surfaceflinger/RenderEngine/Description.cpp b/services/surfaceflinger/RenderEngine/Description.cpp
index 0dab872..effd319 100644
--- a/services/surfaceflinger/RenderEngine/Description.cpp
+++ b/services/surfaceflinger/RenderEngine/Description.cpp
@@ -26,8 +26,7 @@
 
 namespace android {
 
-Description::Description() :
-    mUniformsDirty(true) {
+Description::Description() {
     mPlaneAlpha = 1.0f;
     mPremultipliedAlpha = false;
     mOpaque = true;
@@ -41,28 +40,20 @@
 }
 
 void Description::setPlaneAlpha(GLclampf planeAlpha) {
-    if (planeAlpha != mPlaneAlpha) {
-        mUniformsDirty = true;
-        mPlaneAlpha = planeAlpha;
-    }
+    mPlaneAlpha = planeAlpha;
 }
 
 void Description::setPremultipliedAlpha(bool premultipliedAlpha) {
-    if (premultipliedAlpha != mPremultipliedAlpha) {
-        mPremultipliedAlpha = premultipliedAlpha;
-    }
+    mPremultipliedAlpha = premultipliedAlpha;
 }
 
 void Description::setOpaque(bool opaque) {
-    if (opaque != mOpaque) {
-        mOpaque = opaque;
-    }
+    mOpaque = opaque;
 }
 
 void Description::setTexture(const Texture& texture) {
     mTexture = texture;
     mTextureEnabled = true;
-    mUniformsDirty = true;
 }
 
 void Description::disableTexture() {
@@ -74,12 +65,10 @@
     mColor[1] = green;
     mColor[2] = blue;
     mColor[3] = alpha;
-    mUniformsDirty = true;
 }
 
 void Description::setProjectionMatrix(const mat4& mtx) {
     mProjectionMatrix = mtx;
-    mUniformsDirty = true;
 }
 
 void Description::setColorMatrix(const mat4& mtx) {
@@ -92,5 +81,8 @@
     return mColorMatrix;
 }
 
+void Description::setWideGamut(bool wideGamut) {
+    mIsWideGamut = wideGamut;
+}
 
 } /* namespace android */
diff --git a/services/surfaceflinger/RenderEngine/Description.h b/services/surfaceflinger/RenderEngine/Description.h
index 8a3447c..3beffdf 100644
--- a/services/surfaceflinger/RenderEngine/Description.h
+++ b/services/surfaceflinger/RenderEngine/Description.h
@@ -54,6 +54,8 @@
     bool mColorMatrixEnabled;
     mat4 mColorMatrix;
 
+    bool mIsWideGamut;
+
 public:
     Description();
     ~Description();
@@ -67,9 +69,7 @@
     void setProjectionMatrix(const mat4& mtx);
     void setColorMatrix(const mat4& mtx);
     const mat4& getColorMatrix() const;
-
-private:
-    bool mUniformsDirty;
+    void setWideGamut(bool wideGamut);
 };
 
 } /* namespace android */
diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
index a1ee294..37a530b 100644
--- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
@@ -139,11 +139,10 @@
         // Display-P3 only.
         mat3 srgbToP3 = ColorSpaceConnector(ColorSpace::sRGB(), ColorSpace::DisplayP3()).getTransform();
 
-        // color transform needs to be transposed and expanded to 4x4
-        // to be what the shader wants
+        // color transform needs to be expanded to 4x4 to be what the shader wants
         // mat has an initializer that expands mat3 to mat4, but
         // not an assignment operator
-        mat4 gamutTransform(transpose(srgbToP3));
+        mat4 gamutTransform(srgbToP3);
         mSrgbToDisplayP3 = gamutTransform;
     }
 #endif
@@ -386,6 +385,7 @@
         Description wideColorState = mState;
         if (mDataSpace != HAL_DATASPACE_DISPLAY_P3) {
             wideColorState.setColorMatrix(mState.getColorMatrix() * mSrgbToDisplayP3);
+            wideColorState.setWideGamut(true);
             ALOGV("drawMesh: gamut transform applied");
         }
         ProgramCache::getInstance().useProgram(wideColorState);
diff --git a/services/surfaceflinger/RenderEngine/ProgramCache.cpp b/services/surfaceflinger/RenderEngine/ProgramCache.cpp
index ba11259..06b2252 100644
--- a/services/surfaceflinger/RenderEngine/ProgramCache.cpp
+++ b/services/surfaceflinger/RenderEngine/ProgramCache.cpp
@@ -129,7 +129,9 @@
     .set(Key::OPACITY_MASK,
             description.mOpaque ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT)
     .set(Key::COLOR_MATRIX_MASK,
-            description.mColorMatrixEnabled ? Key::COLOR_MATRIX_ON :  Key::COLOR_MATRIX_OFF);
+            description.mColorMatrixEnabled ? Key::COLOR_MATRIX_ON :  Key::COLOR_MATRIX_OFF)
+    .set(Key::WIDE_GAMUT_MASK,
+            description.mIsWideGamut ? Key::WIDE_GAMUT_ON : Key::WIDE_GAMUT_OFF);
     return needs;
 }
 
@@ -175,6 +177,50 @@
     if (needs.hasColorMatrix()) {
         fs << "uniform mat4 colorMatrix;";
     }
+    if (needs.hasColorMatrix()) {
+        // When in wide gamut mode, the color matrix will contain a color space
+        // conversion matrix that needs to be applied in linear space
+        // When not in wide gamut, we can simply no-op the transfer functions
+        // and let the shader compiler get rid of them
+        if (needs.isWideGamut()) {
+            fs << R"__SHADER__(
+                  float OETF_sRGB(const float linear) {
+                      return linear <= 0.0031308 ?
+                              linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055;
+                  }
+
+                  vec3 OETF_sRGB(const vec3 linear) {
+                      return vec3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
+                  }
+
+                  vec3 OETF_scRGB(const vec3 linear) {
+                      return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
+                  }
+
+                  float EOTF_sRGB(float srgb) {
+                      return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4);
+                  }
+
+                  vec3 EOTF_sRGB(const vec3 srgb) {
+                      return vec3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
+                  }
+
+                  vec3 EOTF_scRGB(const vec3 srgb) {
+                      return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
+                  }
+            )__SHADER__";
+        } else {
+            fs << R"__SHADER__(
+                  vec3 OETF_scRGB(const vec3 linear) {
+                      return linear;
+                  }
+
+                  vec3 EOTF_scRGB(const vec3 srgb) {
+                      return srgb;
+                  }
+            )__SHADER__";
+        }
+    }
     fs << "void main(void) {" << indent;
     if (needs.isTexturing()) {
         fs << "gl_FragColor = texture2D(sampler, outTexCoords);";
@@ -197,13 +243,15 @@
     if (needs.hasColorMatrix()) {
         if (!needs.isOpaque() && needs.isPremultiplied()) {
             // un-premultiply if needed before linearization
-            fs << "gl_FragColor.rgb = gl_FragColor.rgb/gl_FragColor.a;";
+            // avoid divide by 0 by adding 0.5/256 to the alpha channel
+            fs << "gl_FragColor.rgb = gl_FragColor.rgb / (gl_FragColor.a + 0.0019);";
         }
-        fs << "vec4 transformed = colorMatrix * vec4(gl_FragColor.rgb, 1);";
-        fs << "gl_FragColor.rgb = transformed.rgb/transformed.a;";
+        fs << "vec4 transformed = colorMatrix * vec4(EOTF_scRGB(gl_FragColor.rgb), 1);";
+        // We assume the last row is always {0,0,0,1} and we skip the division by w
+        fs << "gl_FragColor.rgb = OETF_scRGB(transformed.rgb);";
         if (!needs.isOpaque() && needs.isPremultiplied()) {
             // and re-premultiply if needed after gamma correction
-            fs << "gl_FragColor.rgb = gl_FragColor.rgb*gl_FragColor.a;";
+            fs << "gl_FragColor.rgb = gl_FragColor.rgb * (gl_FragColor.a + 0.0019);";
         }
     }
 
diff --git a/services/surfaceflinger/RenderEngine/ProgramCache.h b/services/surfaceflinger/RenderEngine/ProgramCache.h
index 1fa53d3..5b0fbcd 100644
--- a/services/surfaceflinger/RenderEngine/ProgramCache.h
+++ b/services/surfaceflinger/RenderEngine/ProgramCache.h
@@ -69,6 +69,10 @@
             COLOR_MATRIX_OFF        =       0x00000000,
             COLOR_MATRIX_ON         =       0x00000020,
             COLOR_MATRIX_MASK       =       0x00000020,
+
+            WIDE_GAMUT_OFF          =       0x00000000,
+            WIDE_GAMUT_ON           =       0x00000040,
+            WIDE_GAMUT_MASK         =       0x00000040,
         };
 
         inline Key() : mKey(0) { }
@@ -97,10 +101,13 @@
         inline bool hasColorMatrix() const {
             return (mKey & COLOR_MATRIX_MASK) == COLOR_MATRIX_ON;
         }
+        inline bool isWideGamut() const {
+            return (mKey & WIDE_GAMUT_MASK) == WIDE_GAMUT_ON;
+        }
 
         // this is the definition of a friend function -- not a method of class Needs
         friend inline int strictly_order_type(const Key& lhs, const Key& rhs) {
-            return  (lhs.mKey < rhs.mKey) ? 1 : 0;
+            return (lhs.mKey < rhs.mKey) ? 1 : 0;
         }
     };
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 7392006..edc0140 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -19,6 +19,7 @@
 
 #include <stdint.h>
 #include <sys/types.h>
+#include <algorithm>
 #include <errno.h>
 #include <math.h>
 #include <mutex>
@@ -1675,9 +1676,25 @@
     }
 }
 
+mat4 SurfaceFlinger::computeSaturationMatrix() const {
+    if (mSaturation == 1.0f) {
+        return mat4();
+    }
+
+    // Rec.709 luma coefficients
+    float3 luminance{0.213f, 0.715f, 0.072f};
+    luminance *= 1.0f - mSaturation;
+    return mat4(
+        vec4{luminance.r + mSaturation, luminance.r, luminance.r, 0.0f},
+        vec4{luminance.g, luminance.g + mSaturation, luminance.g, 0.0f},
+        vec4{luminance.b, luminance.b, luminance.b + mSaturation, 0.0f},
+        vec4{0.0f, 0.0f, 0.0f, 1.0f}
+    );
+}
+
 // pickColorMode translates a given dataspace into the best available color mode.
 // Currently only support sRGB and Display-P3.
-android_color_mode SurfaceFlinger::pickColorMode(android_dataspace dataSpace) {
+android_color_mode SurfaceFlinger::pickColorMode(android_dataspace dataSpace) const {
     switch (dataSpace) {
         // treat Unknown as regular SRGB buffer, since that's what the rest of the
         // system expects.
@@ -1700,11 +1717,19 @@
     }
 }
 
-android_dataspace SurfaceFlinger::bestTargetDataSpace(android_dataspace a, android_dataspace b) {
+android_dataspace SurfaceFlinger::bestTargetDataSpace(
+        android_dataspace a, android_dataspace b) const {
     // Only support sRGB and Display-P3 right now.
     if (a == HAL_DATASPACE_DISPLAY_P3 || b == HAL_DATASPACE_DISPLAY_P3) {
         return HAL_DATASPACE_DISPLAY_P3;
     }
+    if (a == HAL_DATASPACE_V0_SCRGB_LINEAR || b == HAL_DATASPACE_V0_SCRGB_LINEAR) {
+        return HAL_DATASPACE_DISPLAY_P3;
+    }
+    if (a == HAL_DATASPACE_V0_SCRGB || b == HAL_DATASPACE_V0_SCRGB) {
+        return HAL_DATASPACE_DISPLAY_P3;
+    }
+
     return HAL_DATASPACE_V0_SRGB;
 }
 
@@ -1772,7 +1797,7 @@
     }
 
 
-    mat4 colorMatrix = mColorMatrix * mDaltonizer();
+    mat4 colorMatrix = mColorMatrix * computeSaturationMatrix() * mDaltonizer();
 
     // Set the per-frame data
     for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
@@ -2508,8 +2533,8 @@
         ALOGV("hasClientComposition");
 
 #ifdef USE_HWC2
-        mRenderEngine->setColorMode(displayDevice->getActiveColorMode());
         mRenderEngine->setWideColor(displayDevice->getWideColorSupport());
+        mRenderEngine->setColorMode(displayDevice->getActiveColorMode());
 #endif
         if (!displayDevice->makeCurrent(mEGLDisplay, mEGLContext)) {
             ALOGW("DisplayDevice::makeCurrent failed. Aborting surface composition for display %s",
@@ -2659,8 +2684,13 @@
         if (parent == nullptr) {
             mCurrentState.layersSortedByZ.add(lbc);
         } else {
+            if (mCurrentState.layersSortedByZ.indexOf(parent) < 0) {
+                ALOGE("addClientLayer called with a removed parent");
+                return NAME_NOT_FOUND;
+            }
             parent->addChild(lbc);
         }
+
         mGraphicBufferProducerList.add(IInterface::asBinder(gbc));
         mLayersAdded = true;
         mNumLayers++;
@@ -2679,6 +2709,17 @@
     const ssize_t index = (p != nullptr) ? p->removeChild(layer) :
         mCurrentState.layersSortedByZ.remove(layer);
 
+    if (p != nullptr) {
+        sp<Layer> ancestor = p;
+        while (ancestor->getParent() != nullptr) {
+            ancestor = ancestor->getParent();
+        }
+        if (mCurrentState.layersSortedByZ.indexOf(ancestor) < 0) {
+            ALOGE("removeLayer called with a layer whose parent has been removed");
+            return NAME_NOT_FOUND;
+        }
+    }
+
     // As a matter of normal operation, the LayerCleaner will produce a second
     // attempt to remove the surface. The Layer will be kept alive in mDrawingState
     // so we will succeed in promoting it, but it's already been removed
@@ -2695,7 +2736,7 @@
 
     mLayersPendingRemoval.add(layer);
     mLayersRemoved = true;
-    mNumLayers--;
+    mNumLayers -= 1 + layer->getChildrenCount();
     setTransactionFlags(eTransactionNeeded);
     return NO_ERROR;
 }
@@ -3893,9 +3934,7 @@
                 // apply a color matrix
                 n = data.readInt32();
                 if (n) {
-                    // color matrix is sent as mat3 matrix followed by vec3
-                    // offset, then packed into a mat4 where the last row is
-                    // the offset and extra values are 0
+                    // color matrix is sent as a column-major mat4 matrix
                     for (size_t i = 0 ; i < 4; i++) {
                         for (size_t j = 0; j < 4; j++) {
                             mColorMatrix[i][j] = data.readFloat();
@@ -3904,6 +3943,14 @@
                 } else {
                     mColorMatrix = mat4();
                 }
+
+                // Check that supplied matrix's last row is {0,0,0,1} so we can avoid
+                // the division by w in the fragment shader
+                float4 lastRow(transpose(mColorMatrix)[3]);
+                if (any(greaterThan(abs(lastRow - float4{0, 0, 0, 1}), float4{1e-4f}))) {
+                    ALOGE("The color transform's last row must be (0, 0, 0, 1)");
+                }
+
                 invalidateHwcGeometry();
                 repaintEverything();
                 return NO_ERROR;
@@ -3947,6 +3994,13 @@
                 mUseHwcVirtualDisplays = !n;
                 return NO_ERROR;
             }
+            case 1022: { // Set saturation boost
+                mSaturation = std::max(0.0f, std::min(data.readFloat(), 2.0f));
+
+                invalidateHwcGeometry();
+                repaintEverything();
+                return NO_ERROR;
+            }
         }
     }
     return err;
@@ -4210,6 +4264,11 @@
         ALOGE("Invalid crop rect: b = %d (> %d)", sourceCrop.bottom, hw_h);
     }
 
+#ifdef USE_HWC2
+     engine.setWideColor(hw->getWideColorSupport());
+     engine.setColorMode(hw->getActiveColorMode());
+#endif
+
     // make sure to clear all GL error flags
     engine.checkErrors();
 
@@ -4339,6 +4398,14 @@
                             hw, sourceCrop, reqWidth, reqHeight, minLayerZ, maxLayerZ, true,
                             useIdentityTransform, rotation);
 
+#ifdef USE_HWC2
+                        if (hasWideColorDisplay) {
+                            native_window_set_buffers_data_space(window,
+                                getRenderEngine().usesWideColor() ?
+                                    HAL_DATASPACE_DISPLAY_P3 : HAL_DATASPACE_V0_SRGB);
+                        }
+#endif
+
                         // Attempt to create a sync khr object that can produce a sync point. If that
                         // isn't available, create a non-dupable sync object in the fallback path and
                         // wait on it directly.
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 1bc689d..68a088a 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -511,8 +511,10 @@
 
     // Given a dataSpace, returns the appropriate color_mode to use
     // to display that dataSpace.
-    android_color_mode pickColorMode(android_dataspace dataSpace);
-    android_dataspace bestTargetDataSpace(android_dataspace a, android_dataspace b);
+    android_color_mode pickColorMode(android_dataspace dataSpace) const;
+    android_dataspace bestTargetDataSpace(android_dataspace a, android_dataspace b) const;
+
+    mat4 computeSaturationMatrix() const;
 
     void setUpHWComposer();
     void doComposition();
@@ -747,7 +749,9 @@
     std::atomic<bool> mVrFlingerRequestsDisplay;
     static bool useVrFlinger;
 #endif
-    };
+
+    float mSaturation = 1.0f;
+};
 }; // namespace android
 
 #endif // ANDROID_SURFACE_FLINGER_H
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index 68519a1..b7792c7 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -834,6 +834,38 @@
     }
 }
 
+TEST_F(ChildLayerTest, ChildLayerCropping) {
+    SurfaceComposerClient::openGlobalTransaction();
+    mChild->show();
+    mChild->setPosition(0, 0);
+    mFGSurfaceControl->setPosition(0, 0);
+    mFGSurfaceControl->setCrop(Rect(0, 0, 5, 5));
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    {
+        ScreenCapture::captureScreen(&mCapture);
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(4, 4);
+        mCapture->expectBGColor(5, 5);
+    }
+}
+
+TEST_F(ChildLayerTest, ChildLayerFinalCropping) {
+    SurfaceComposerClient::openGlobalTransaction();
+    mChild->show();
+    mChild->setPosition(0, 0);
+    mFGSurfaceControl->setPosition(0, 0);
+    mFGSurfaceControl->setFinalCrop(Rect(0, 0, 5, 5));
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    {
+        ScreenCapture::captureScreen(&mCapture);
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(4, 4);
+        mCapture->expectBGColor(5, 5);
+    }
+}
+
 TEST_F(ChildLayerTest, ChildLayerConstraints) {
     SurfaceComposerClient::openGlobalTransaction();
     mChild->show();