Merge changes from topic 'vk-debug-report' into nyc-dev

* changes:
  vulkan: improve vulkan::driver logcat messages
  vulkan: use Logger in vulkan::api
  vulkan: use Logger in the generated code
  vulkan: add DebugReportLogger
  vulkan: make debug report callbacks available in DeviceData
  vulkan: avoid duplicated app messages
  vulkan: constify DebugReportCallbackList::Message
  vulkan: refactor DebugReportCallbackList
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index e01aa3f..facf300 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -53,6 +53,8 @@
 
 const char* k_traceAppsNumberProperty = "debug.atrace.app_number";
 const char* k_traceAppsPropertyTemplate = "debug.atrace.app_%d";
+const char* k_coreServiceCategory = "core_services";
+const char* k_coreServicesProp = "ro.atrace.core.services";
 
 typedef enum { OPT, REQ } requiredness  ;
 
@@ -100,6 +102,7 @@
     { "pm",         "Package Manager",  ATRACE_TAG_PACKAGE_MANAGER, { } },
     { "ss",         "System Server",    ATRACE_TAG_SYSTEM_SERVER, { } },
     { "database",   "Database",         ATRACE_TAG_DATABASE, { } },
+    { k_coreServiceCategory, "Core services", 0, { } },
     { "sched",      "CPU Scheduling",   0, {
         { REQ,      "/sys/kernel/debug/tracing/events/sched/sched_switch/enable" },
         { REQ,      "/sys/kernel/debug/tracing/events/sched/sched_wakeup/enable" },
@@ -331,6 +334,12 @@
 // or /sys/ files.
 static bool isCategorySupported(const TracingCategory& category)
 {
+    if (strcmp(category.name, k_coreServiceCategory) == 0) {
+        char value[PROPERTY_VALUE_MAX];
+        property_get(k_coreServicesProp, value, "");
+        return strlen(value) != 0;
+    }
+
     bool ok = category.tags != 0;
     for (int i = 0; i < MAX_SYS_FILES; i++) {
         const char* path = category.sysfiles[i].path;
@@ -727,7 +736,24 @@
         }
     }
     ok &= setTagsProperty(tags);
-    ok &= setAppCmdlineProperty(g_debugAppCmdLine);
+
+    bool coreServicesTagEnabled = false;
+    for (int i = 0; i < NELEM(k_categories); i++) {
+        if (strcmp(k_categories[i].name, k_coreServiceCategory) == 0) {
+            coreServicesTagEnabled = g_categoryEnables[i];
+        }
+    }
+
+    std::string packageList(g_debugAppCmdLine);
+    if (coreServicesTagEnabled) {
+        char value[PROPERTY_VALUE_MAX];
+        property_get(k_coreServicesProp, value, "");
+        if (!packageList.empty()) {
+            packageList += ",";
+        }
+        packageList += value;
+    }
+    ok &= setAppCmdlineProperty(packageList.data());
     ok &= pokeBinderServices();
 
     // Disable all the sysfs enables.  This is done as a separate loop from
diff --git a/cmds/installd/commands.cpp b/cmds/installd/commands.cpp
index 90ef277..34b570a 100644
--- a/cmds/installd/commands.cpp
+++ b/cmds/installd/commands.cpp
@@ -315,9 +315,9 @@
 
     // Copy app
     {
-        std::string from(create_data_app_package_path(from_uuid, data_app_name));
-        std::string to(create_data_app_package_path(to_uuid, data_app_name));
-        std::string to_parent(create_data_app_path(to_uuid));
+        auto from = create_data_app_package_path(from_uuid, data_app_name);
+        auto to = create_data_app_package_path(to_uuid, data_app_name);
+        auto to_parent = create_data_app_path(to_uuid);
 
         char *argv[] = {
             (char*) kCpPath,
@@ -346,27 +346,18 @@
     }
 
     // Copy private data for all known users
-    // TODO: handle user_de paths
     for (auto user : users) {
-        std::string from(create_data_user_ce_package_path(from_uuid, user, package_name));
-        std::string to(create_data_user_ce_package_path(to_uuid, user, package_name));
-        std::string to_parent(create_data_user_ce_path(to_uuid, user));
 
         // Data source may not exist for all users; that's okay
-        if (access(from.c_str(), F_OK) != 0) {
-            LOG(INFO) << "Missing source " << from;
+        auto from_ce = create_data_user_ce_package_path(from_uuid, user, package_name);
+        if (access(from_ce.c_str(), F_OK) != 0) {
+            LOG(INFO) << "Missing source " << from_ce;
             continue;
         }
 
-        std::string user_path(create_data_user_ce_path(to_uuid, user));
-        if (fs_prepare_dir(user_path.c_str(), 0771, AID_SYSTEM, AID_SYSTEM) != 0) {
-            LOG(ERROR) << "Failed to prepare user target " << user_path;
-            goto fail;
-        }
-
         if (create_app_data(to_uuid, package_name, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE,
                 appid, seinfo, target_sdk_version) != 0) {
-            LOG(ERROR) << "Failed to create package target " << to;
+            LOG(ERROR) << "Failed to create package target on " << to_uuid;
             goto fail;
         }
 
@@ -377,17 +368,35 @@
             (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */
             (char*) "-P", /* Do not follow symlinks [default] */
             (char*) "-d", /* don't dereference symlinks */
-            (char*) from.c_str(),
-            (char*) to_parent.c_str()
+            nullptr,
+            nullptr
         };
 
-        LOG(DEBUG) << "Copying " << from << " to " << to;
-        int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, NULL, false, true);
+        {
+            auto from = create_data_user_de_package_path(from_uuid, user, package_name);
+            auto to = create_data_user_de_path(to_uuid, user);
+            argv[6] = (char*) from.c_str();
+            argv[7] = (char*) to.c_str();
 
-        if (rc != 0) {
-            LOG(ERROR) << "Failed copying " << from << " to " << to
-                    << ": status " << rc;
-            goto fail;
+            LOG(DEBUG) << "Copying " << from << " to " << to;
+            int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, NULL, false, true);
+            if (rc != 0) {
+                LOG(ERROR) << "Failed copying " << from << " to " << to << " with status " << rc;
+                goto fail;
+            }
+        }
+        {
+            auto from = create_data_user_ce_package_path(from_uuid, user, package_name);
+            auto to = create_data_user_ce_path(to_uuid, user);
+            argv[6] = (char*) from.c_str();
+            argv[7] = (char*) to.c_str();
+
+            LOG(DEBUG) << "Copying " << from << " to " << to;
+            int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, NULL, false, true);
+            if (rc != 0) {
+                LOG(ERROR) << "Failed copying " << from << " to " << to << " with status " << rc;
+                goto fail;
+            }
         }
 
         if (restorecon_app_data(to_uuid, package_name, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE,
@@ -405,15 +414,23 @@
 fail:
     // Nuke everything we might have already copied
     {
-        std::string to(create_data_app_package_path(to_uuid, data_app_name));
+        auto to = create_data_app_package_path(to_uuid, data_app_name);
         if (delete_dir_contents(to.c_str(), 1, NULL) != 0) {
             LOG(WARNING) << "Failed to rollback " << to;
         }
     }
     for (auto user : users) {
-        std::string to(create_data_user_ce_package_path(to_uuid, user, package_name));
-        if (delete_dir_contents(to.c_str(), 1, NULL) != 0) {
-            LOG(WARNING) << "Failed to rollback " << to;
+        {
+            auto to = create_data_user_de_package_path(to_uuid, user, package_name);
+            if (delete_dir_contents(to.c_str(), 1, NULL) != 0) {
+                LOG(WARNING) << "Failed to rollback " << to;
+            }
+        }
+        {
+            auto to = create_data_user_ce_package_path(to_uuid, user, package_name);
+            if (delete_dir_contents(to.c_str(), 1, NULL) != 0) {
+                LOG(WARNING) << "Failed to rollback " << to;
+            }
         }
     }
     return -1;
@@ -452,16 +469,11 @@
  * also require that apps constantly modify file metadata even
  * when just reading from the cache, which is pretty awful.
  */
-int free_cache(const char *uuid, int64_t free_size)
-{
+int free_cache(const char *uuid, int64_t free_size) {
     cache_t* cache;
     int64_t avail;
-    DIR *d;
-    struct dirent *de;
-    char tmpdir[PATH_MAX];
-    char *dirpos;
 
-    std::string data_path(create_data_path(uuid));
+    auto data_path = create_data_path(uuid);
 
     avail = data_disk_free(data_path);
     if (avail < 0) return -1;
@@ -471,65 +483,12 @@
 
     cache = start_cache_collection();
 
-    // Special case for owner on internal storage
-    if (uuid == nullptr) {
-        std::string _tmpdir(create_data_user_ce_path(nullptr, 0));
-        add_cache_files(cache, _tmpdir.c_str(), "cache");
-    }
-
-    // Search for other users and add any cache files from them.
-    std::string _tmpdir(create_data_path(uuid) + "/" + SECONDARY_USER_PREFIX);
-    strcpy(tmpdir, _tmpdir.c_str());
-
-    dirpos = tmpdir + strlen(tmpdir);
-    d = opendir(tmpdir);
-    if (d != NULL) {
-        while ((de = readdir(d))) {
-            if (de->d_type == DT_DIR) {
-                const char *name = de->d_name;
-                    /* always skip "." and ".." */
-                if (name[0] == '.') {
-                    if (name[1] == 0) continue;
-                    if ((name[1] == '.') && (name[2] == 0)) continue;
-                }
-                if ((strlen(name)+(dirpos-tmpdir)) < (sizeof(tmpdir)-1)) {
-                    strcpy(dirpos, name);
-                    //ALOGI("adding cache files from %s\n", tmpdir);
-                    add_cache_files(cache, tmpdir, "cache");
-                } else {
-                    ALOGW("Path exceeds limit: %s%s", tmpdir, name);
-                }
-            }
-        }
-        closedir(d);
-    }
-
-    // Collect cache files on external storage for all users (if it is mounted as part
-    // of the internal storage).
-    strcpy(tmpdir, android_media_dir.path);
-    dirpos = tmpdir + strlen(tmpdir);
-    d = opendir(tmpdir);
-    if (d != NULL) {
-        while ((de = readdir(d))) {
-            if (de->d_type == DT_DIR) {
-                const char *name = de->d_name;
-                    /* skip any dir that doesn't start with a number, so not a user */
-                if (name[0] < '0' || name[0] > '9') {
-                    continue;
-                }
-                if ((strlen(name)+(dirpos-tmpdir)) < (sizeof(tmpdir)-1)) {
-                    strcpy(dirpos, name);
-                    if (lookup_media_dir(tmpdir, "Android") == 0
-                            && lookup_media_dir(tmpdir, "data") == 0) {
-                        //ALOGI("adding cache files from %s\n", tmpdir);
-                        add_cache_files(cache, tmpdir, "cache");
-                    }
-                } else {
-                    ALOGW("Path exceeds limit: %s%s", tmpdir, name);
-                }
-            }
-        }
-        closedir(d);
+    auto users = get_known_users(uuid);
+    for (auto user : users) {
+        add_cache_files(cache, create_data_user_ce_path(uuid, user));
+        add_cache_files(cache, create_data_user_de_path(uuid, user));
+        add_cache_files(cache,
+                StringPrintf("%s/Android/data", create_data_media_path(uuid, user).c_str()));
     }
 
     clear_cache_files(data_path, cache, free_size);
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 90d2a9e..c838993 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -521,56 +521,6 @@
     return res;
 }
 
-int lookup_media_dir(char basepath[PATH_MAX], const char *dir)
-{
-    DIR *d;
-    struct dirent *de;
-    struct stat s;
-    char* dirpos = basepath + strlen(basepath);
-
-    if ((*(dirpos-1)) != '/') {
-        *dirpos = '/';
-        dirpos++;
-    }
-
-    CACHE_NOISY(ALOGI("Looking up %s in %s\n", dir, basepath));
-    // Verify the path won't extend beyond our buffer, to avoid
-    // repeated checking later.
-    if ((dirpos-basepath+strlen(dir)) >= (PATH_MAX-1)) {
-        ALOGW("Path exceeds limit: %s%s", basepath, dir);
-        return -1;
-    }
-
-    // First, can we find this directory with the case that is given?
-    strcpy(dirpos, dir);
-    if (stat(basepath, &s) >= 0) {
-        CACHE_NOISY(ALOGI("Found direct: %s\n", basepath));
-        return 0;
-    }
-
-    // Not found with that case...  search through all entries to find
-    // one that matches regardless of case.
-    *dirpos = 0;
-
-    d = opendir(basepath);
-    if (d == NULL) {
-        return -1;
-    }
-
-    while ((de = readdir(d))) {
-        if (strcasecmp(de->d_name, dir) == 0) {
-            strcpy(dirpos, de->d_name);
-            closedir(d);
-            CACHE_NOISY(ALOGI("Found search: %s\n", basepath));
-            return 0;
-        }
-    }
-
-    ALOGW("Couldn't find %s in %s", dir, basepath);
-    closedir(d);
-    return -1;
-}
-
 int64_t data_disk_free(const std::string& data_path)
 {
     struct statfs sfs;
@@ -829,13 +779,13 @@
     return 0;
 }
 
-void add_cache_files(cache_t* cache, const char *basepath, const char *cachedir)
-{
+void add_cache_files(cache_t* cache, const std::string& data_path) {
     DIR *d;
     struct dirent *de;
     char dirname[PATH_MAX];
 
-    CACHE_NOISY(ALOGI("add_cache_files: base=%s cachedir=%s\n", basepath, cachedir));
+    const char* basepath = data_path.c_str();
+    CACHE_NOISY(ALOGI("add_cache_files: basepath=%s\n", basepath));
 
     d = opendir(basepath);
     if (d == NULL) {
@@ -861,12 +811,11 @@
                 pathpos++;
                 *pathpos = 0;
             }
-            if (cachedir != NULL) {
-                snprintf(pathpos, sizeof(dirname)-(pathpos-dirname), "%s/%s", name, cachedir);
-            } else {
-                snprintf(pathpos, sizeof(dirname)-(pathpos-dirname), "%s", name);
-            }
+
+            // TODO: also try searching using xattr when CE is locked
+            snprintf(pathpos, sizeof(dirname)-(pathpos-dirname), "%s/cache", name);
             CACHE_NOISY(ALOGI("Adding cache files from dir: %s\n", dirname));
+
             subdir = opendir(dirname);
             if (subdir != NULL) {
                 size_t dirnameLen = strlen(dirname);
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 477baea..60df356 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -114,13 +114,11 @@
 
 int copy_dir_files(const char *srcname, const char *dstname, uid_t owner, gid_t group);
 
-int lookup_media_dir(char basepath[PATH_MAX], const char *dir);
-
 int64_t data_disk_free(const std::string& data_path);
 
 cache_t* start_cache_collection();
 
-void add_cache_files(cache_t* cache, const char *basepath, const char *cachedir);
+void add_cache_files(cache_t* cache, const std::string& data_path);
 
 void clear_cache_files(const std::string& data_path, cache_t* cache, int64_t free_size);
 
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index a0161b3..c7e8ff2 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -1964,7 +1964,13 @@
 
     for (int i=0 ; err==NO_ERROR && i<numFds ; i++) {
         h->data[i] = dup(readFileDescriptor());
-        if (h->data[i] < 0) err = BAD_VALUE;
+        if (h->data[i] < 0) {
+            for (int j = 0; j < i; j++) {
+                close(h->data[j]);
+            }
+            native_handle_delete(h);
+            return 0;
+        }
     }
     err = read(h->data + numFds, sizeof(int)*numInts);
     if (err != NO_ERROR) {
diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp
index 6f4c89d..3491043 100644
--- a/libs/gui/BufferItemConsumer.cpp
+++ b/libs/gui/BufferItemConsumer.cpp
@@ -49,6 +49,10 @@
 
 void BufferItemConsumer::setName(const String8& name) {
     Mutex::Autolock _l(mMutex);
+    if (mAbandoned) {
+        BI_LOGE("setName: BufferItemConsumer is abandoned!");
+        return;
+    }
     mName = name;
     mConsumer->setConsumerName(name);
 }
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index 052de3d..ba34eb6 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -130,11 +130,18 @@
 
     for (int s : mActiveBuffers) {
         const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer);
-        result.appendFormat("%s%s[%02d:%p] state=%-8s, %p [%4ux%4u:%4u,%3X]\n",
-                prefix, (mSlots[s].mBufferState.isAcquired()) ? ">" : " ", s,
-                buffer.get(), mSlots[s].mBufferState.string(), buffer->handle,
-                buffer->width, buffer->height, buffer->stride, buffer->format);
-
+        // A dequeued buffer might be null if it's still being allocated
+        if (buffer.get()) {
+            result.appendFormat("%s%s[%02d:%p] state=%-8s, %p "
+                    "[%4ux%4u:%4u,%3X]\n", prefix,
+                    (mSlots[s].mBufferState.isAcquired()) ? ">" : " ", s,
+                    buffer.get(), mSlots[s].mBufferState.string(),
+                    buffer->handle, buffer->width, buffer->height,
+                    buffer->stride, buffer->format);
+        } else {
+            result.appendFormat("%s [%02d:%p] state=%-8s\n", prefix, s,
+                    buffer.get(), mSlots[s].mBufferState.string());
+        }
     }
     for (int s : mFreeBuffers) {
         const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer);
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index 2187e5e..a6a9712 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -163,6 +163,10 @@
 
 void ConsumerBase::abandonLocked() {
     CB_LOGV("abandonLocked");
+    if (mAbandoned) {
+        CB_LOGE("abandonLocked: ConsumerBase is abandoned!");
+        return;
+    }
     for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
         freeBufferLocked(i);
     }
@@ -187,6 +191,11 @@
     CB_LOGV("detachBuffer");
     Mutex::Autolock lock(mMutex);
 
+    if (mAbandoned) {
+        CB_LOGE("detachBuffer: ConsumerBase is abandoned!");
+        return NO_INIT;
+    }
+
     status_t result = mConsumer->detachBuffer(slot);
     if (result != NO_ERROR) {
         CB_LOGE("Failed to detach buffer: %d", result);
@@ -200,17 +209,29 @@
 
 status_t ConsumerBase::setDefaultBufferSize(uint32_t width, uint32_t height) {
     Mutex::Autolock _l(mMutex);
+    if (mAbandoned) {
+        CB_LOGE("setDefaultBufferSize: ConsumerBase is abandoned!");
+        return NO_INIT;
+    }
     return mConsumer->setDefaultBufferSize(width, height);
 }
 
 status_t ConsumerBase::setDefaultBufferFormat(PixelFormat defaultFormat) {
     Mutex::Autolock _l(mMutex);
+    if (mAbandoned) {
+        CB_LOGE("setDefaultBufferFormat: ConsumerBase is abandoned!");
+        return NO_INIT;
+    }
     return mConsumer->setDefaultBufferFormat(defaultFormat);
 }
 
 status_t ConsumerBase::setDefaultBufferDataSpace(
         android_dataspace defaultDataSpace) {
     Mutex::Autolock _l(mMutex);
+    if (mAbandoned) {
+        CB_LOGE("setDefaultBufferDataSpace: ConsumerBase is abandoned!");
+        return NO_INIT;
+    }
     return mConsumer->setDefaultBufferDataSpace(defaultDataSpace);
 }
 
@@ -233,6 +254,11 @@
 
 status_t ConsumerBase::acquireBufferLocked(BufferItem *item,
         nsecs_t presentWhen, uint64_t maxFrameNumber) {
+    if (mAbandoned) {
+        CB_LOGE("acquireBufferLocked: ConsumerBase is abandoned!");
+        return NO_INIT;
+    }
+
     status_t err = mConsumer->acquireBuffer(item, presentWhen, maxFrameNumber);
     if (err != NO_ERROR) {
         return err;
@@ -289,6 +315,10 @@
 status_t ConsumerBase::releaseBufferLocked(
         int slot, const sp<GraphicBuffer> graphicBuffer,
         EGLDisplay display, EGLSyncKHR eglFence) {
+    if (mAbandoned) {
+        CB_LOGE("releaseBufferLocked: ConsumerBase is abandoned!");
+        return NO_INIT;
+    }
     // If consumer no longer tracks this graphicBuffer (we received a new
     // buffer on the same slot), the buffer producer is definitely no longer
     // tracking it.
diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp
index 7ed3d0f..8393160 100644
--- a/libs/gui/CpuConsumer.cpp
+++ b/libs/gui/CpuConsumer.cpp
@@ -52,6 +52,10 @@
 
 void CpuConsumer::setName(const String8& name) {
     Mutex::Autolock _l(mMutex);
+    if (mAbandoned) {
+        CC_LOGE("setName: CpuConsumer is abandoned!");
+        return;
+    }
     mName = name;
     mConsumer->setConsumerName(name);
 }
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index 7a1ad46..a06c7b5 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -209,6 +209,10 @@
 status_t GLConsumer::setDefaultBufferSize(uint32_t w, uint32_t h)
 {
     Mutex::Autolock lock(mMutex);
+    if (mAbandoned) {
+        GLC_LOGE("setDefaultBufferSize: GLConsumer is abandoned!");
+        return NO_INIT;
+    }
     mDefaultWidth = w;
     mDefaultHeight = h;
     return mConsumer->setDefaultBufferSize(w, h);
@@ -416,21 +420,6 @@
         return err;
     }
 
-    // For investigating b/27674961
-      if (mEglSlots[slot].mEglImage == nullptr) {
-          ALOGE("If you see this message in a log please post the log to "
-              "b/27674961");
-          ALOGE("slot = %d, mCurrentTexture = %d, mCurrentTextureImage = %p",
-                  slot, mCurrentTexture, mCurrentTextureImage.get());
-          for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
-              ALOGE("mEglSlots[%d].mEglImage = %p", i,
-                      mEglSlots[i].mEglImage.get());
-          }
-          String8 dump;
-          dumpLocked(dump, "");
-          ALOGE("%s", dump.string());
-      }
-
     // Ensure we have a valid EglImageKHR for the slot, creating an EglImage
     // if nessessary, for the gralloc buffer currently in the slot in
     // ConsumerBase.
@@ -1074,34 +1063,58 @@
 
 void GLConsumer::setName(const String8& name) {
     Mutex::Autolock _l(mMutex);
+    if (mAbandoned) {
+        GLC_LOGE("setName: GLConsumer is abandoned!");
+        return;
+    }
     mName = name;
     mConsumer->setConsumerName(name);
 }
 
 status_t GLConsumer::setDefaultBufferFormat(PixelFormat defaultFormat) {
     Mutex::Autolock lock(mMutex);
+    if (mAbandoned) {
+        GLC_LOGE("setDefaultBufferFormat: GLConsumer is abandoned!");
+        return NO_INIT;
+    }
     return mConsumer->setDefaultBufferFormat(defaultFormat);
 }
 
 status_t GLConsumer::setDefaultBufferDataSpace(
         android_dataspace defaultDataSpace) {
     Mutex::Autolock lock(mMutex);
+    if (mAbandoned) {
+        GLC_LOGE("setDefaultBufferDataSpace: GLConsumer is abandoned!");
+        return NO_INIT;
+    }
     return mConsumer->setDefaultBufferDataSpace(defaultDataSpace);
 }
 
 status_t GLConsumer::setConsumerUsageBits(uint32_t usage) {
     Mutex::Autolock lock(mMutex);
+    if (mAbandoned) {
+        GLC_LOGE("setConsumerUsageBits: GLConsumer is abandoned!");
+        return NO_INIT;
+    }
     usage |= DEFAULT_USAGE_FLAGS;
     return mConsumer->setConsumerUsageBits(usage);
 }
 
 status_t GLConsumer::setTransformHint(uint32_t hint) {
     Mutex::Autolock lock(mMutex);
+    if (mAbandoned) {
+        GLC_LOGE("setTransformHint: GLConsumer is abandoned!");
+        return NO_INIT;
+    }
     return mConsumer->setTransformHint(hint);
 }
 
 status_t GLConsumer::setMaxAcquiredBufferCount(int maxAcquiredBuffers) {
     Mutex::Autolock lock(mMutex);
+    if (mAbandoned) {
+        GLC_LOGE("setMaxAcquiredBufferCount: GLConsumer is abandoned!");
+        return NO_INIT;
+    }
     return mConsumer->setMaxAcquiredBufferCount(maxAcquiredBuffers);
 }
 
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index f7678e4..f898ada 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -96,6 +96,7 @@
     mDestroyLayer(nullptr),
     mGetActiveConfig(nullptr),
     mGetChangedCompositionTypes(nullptr),
+    mGetColorModes(nullptr),
     mGetDisplayAttribute(nullptr),
     mGetDisplayConfigs(nullptr),
     mGetDisplayName(nullptr),
@@ -107,6 +108,8 @@
     mPresentDisplay(nullptr),
     mSetActiveConfig(nullptr),
     mSetClientTarget(nullptr),
+    mSetColorMode(nullptr),
+    mSetColorTransform(nullptr),
     mSetOutputBuffer(nullptr),
     mSetPowerMode(nullptr),
     mSetVsyncEnabled(nullptr),
@@ -117,6 +120,7 @@
     mSetLayerBlendMode(nullptr),
     mSetLayerColor(nullptr),
     mSetLayerCompositionType(nullptr),
+    mSetLayerDataspace(nullptr),
     mSetLayerDisplayFrame(nullptr),
     mSetLayerPlaneAlpha(nullptr),
     mSetLayerSidebandStream(nullptr),
@@ -337,6 +341,8 @@
             mGetActiveConfig)) return;
     if (!loadFunctionPointer(FunctionDescriptor::GetChangedCompositionTypes,
             mGetChangedCompositionTypes)) return;
+    if (!loadFunctionPointer(FunctionDescriptor::GetColorModes,
+            mGetColorModes)) return;
     if (!loadFunctionPointer(FunctionDescriptor::GetDisplayAttribute,
             mGetDisplayAttribute)) return;
     if (!loadFunctionPointer(FunctionDescriptor::GetDisplayConfigs,
@@ -359,6 +365,10 @@
             mSetActiveConfig)) return;
     if (!loadFunctionPointer(FunctionDescriptor::SetClientTarget,
             mSetClientTarget)) return;
+    if (!loadFunctionPointer(FunctionDescriptor::SetColorMode,
+            mSetColorMode)) return;
+    if (!loadFunctionPointer(FunctionDescriptor::SetColorTransform,
+            mSetColorTransform)) return;
     if (!loadFunctionPointer(FunctionDescriptor::SetOutputBuffer,
             mSetOutputBuffer)) return;
     if (!loadFunctionPointer(FunctionDescriptor::SetPowerMode,
@@ -381,6 +391,8 @@
             mSetLayerColor)) return;
     if (!loadFunctionPointer(FunctionDescriptor::SetLayerCompositionType,
             mSetLayerCompositionType)) return;
+    if (!loadFunctionPointer(FunctionDescriptor::SetLayerDataspace,
+            mSetLayerDataspace)) return;
     if (!loadFunctionPointer(FunctionDescriptor::SetLayerDisplayFrame,
             mSetLayerDisplayFrame)) return;
     if (!loadFunctionPointer(FunctionDescriptor::SetLayerPlaneAlpha,
@@ -551,6 +563,28 @@
     return Error::None;
 }
 
+Error Display::getColorModes(std::vector<int32_t>* outModes) const
+{
+    uint32_t numModes = 0;
+    int32_t intError = mDevice.mGetColorModes(mDevice.mHwcDevice, mId,
+            &numModes, nullptr);
+    auto error = static_cast<Error>(intError);
+    if (error != Error::None)  {
+        return error;
+    }
+
+    std::vector<int32_t> modes(numModes);
+    intError = mDevice.mGetColorModes(mDevice.mHwcDevice, mId, &numModes,
+            modes.data());
+    error = static_cast<Error>(intError);
+    if (error != Error::None) {
+        return error;
+    }
+
+    std::swap(*outModes, modes);
+    return Error::None;
+}
+
 std::vector<std::shared_ptr<const Display::Config>> Display::getConfigs() const
 {
     std::vector<std::shared_ptr<const Config>> configs;
@@ -752,6 +786,20 @@
     return static_cast<Error>(intError);
 }
 
+Error Display::setColorMode(int32_t mode)
+{
+    int32_t intError = mDevice.mSetColorMode(mDevice.mHwcDevice, mId, mode);
+    return static_cast<Error>(intError);
+}
+
+Error Display::setColorTransform(const android::mat4& matrix,
+        android_color_transform_t hint)
+{
+    int32_t intError = mDevice.mSetColorTransform(mDevice.mHwcDevice, mId,
+            matrix.asArray(), static_cast<int32_t>(hint));
+    return static_cast<Error>(intError);
+}
+
 Error Display::setOutputBuffer(const sp<GraphicBuffer>& buffer,
         const sp<Fence>& releaseFence)
 {
@@ -965,6 +1013,14 @@
     return static_cast<Error>(intError);
 }
 
+Error Layer::setDataspace(android_dataspace_t dataspace)
+{
+    auto intDataspace = static_cast<int32_t>(dataspace);
+    int32_t intError = mDevice.mSetLayerDataspace(mDevice.mHwcDevice,
+            mDisplayId, mId, intDataspace);
+    return static_cast<Error>(intError);
+}
+
 Error Layer::setDisplayFrame(const Rect& frame)
 {
     hwc_rect_t hwcRect{frame.left, frame.top, frame.right, frame.bottom};
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 967add0..e40602f 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -24,6 +24,7 @@
 #undef HWC2_USE_CPP11
 
 #include <ui/HdrCapabilities.h>
+#include <ui/mat4.h>
 
 #include <utils/Log.h>
 #include <utils/StrongPointer.h>
@@ -143,6 +144,7 @@
     HWC2_PFN_DESTROY_LAYER mDestroyLayer;
     HWC2_PFN_GET_ACTIVE_CONFIG mGetActiveConfig;
     HWC2_PFN_GET_CHANGED_COMPOSITION_TYPES mGetChangedCompositionTypes;
+    HWC2_PFN_GET_COLOR_MODES mGetColorModes;
     HWC2_PFN_GET_DISPLAY_ATTRIBUTE mGetDisplayAttribute;
     HWC2_PFN_GET_DISPLAY_CONFIGS mGetDisplayConfigs;
     HWC2_PFN_GET_DISPLAY_NAME mGetDisplayName;
@@ -154,6 +156,8 @@
     HWC2_PFN_PRESENT_DISPLAY mPresentDisplay;
     HWC2_PFN_SET_ACTIVE_CONFIG mSetActiveConfig;
     HWC2_PFN_SET_CLIENT_TARGET mSetClientTarget;
+    HWC2_PFN_SET_COLOR_MODE mSetColorMode;
+    HWC2_PFN_SET_COLOR_TRANSFORM mSetColorTransform;
     HWC2_PFN_SET_OUTPUT_BUFFER mSetOutputBuffer;
     HWC2_PFN_SET_POWER_MODE mSetPowerMode;
     HWC2_PFN_SET_VSYNC_ENABLED mSetVsyncEnabled;
@@ -166,6 +170,7 @@
     HWC2_PFN_SET_LAYER_BLEND_MODE mSetLayerBlendMode;
     HWC2_PFN_SET_LAYER_COLOR mSetLayerColor;
     HWC2_PFN_SET_LAYER_COMPOSITION_TYPE mSetLayerCompositionType;
+    HWC2_PFN_SET_LAYER_DATASPACE mSetLayerDataspace;
     HWC2_PFN_SET_LAYER_DISPLAY_FRAME mSetLayerDisplayFrame;
     HWC2_PFN_SET_LAYER_PLANE_ALPHA mSetLayerPlaneAlpha;
     HWC2_PFN_SET_LAYER_SIDEBAND_STREAM mSetLayerSidebandStream;
@@ -273,6 +278,8 @@
             std::shared_ptr<const Config>* outConfig) const;
     [[clang::warn_unused_result]] Error getChangedCompositionTypes(
             std::unordered_map<std::shared_ptr<Layer>, Composition>* outTypes);
+    [[clang::warn_unused_result]] Error getColorModes(
+            std::vector<int32_t>* outModes) const;
 
     // Doesn't call into the HWC2 device, so no errors are possible
     std::vector<std::shared_ptr<const Config>> getConfigs() const;
@@ -297,6 +304,9 @@
             buffer_handle_t target,
             const android::sp<android::Fence>& acquireFence,
             android_dataspace_t dataspace);
+    [[clang::warn_unused_result]] Error setColorMode(int32_t mode);
+    [[clang::warn_unused_result]] Error setColorTransform(
+            const android::mat4& matrix, android_color_transform_t hint);
     [[clang::warn_unused_result]] Error setOutputBuffer(
             const android::sp<android::GraphicBuffer>& buffer,
             const android::sp<android::Fence>& releaseFence);
@@ -360,6 +370,8 @@
     [[clang::warn_unused_result]] Error setBlendMode(BlendMode mode);
     [[clang::warn_unused_result]] Error setColor(hwc_color_t color);
     [[clang::warn_unused_result]] Error setCompositionType(Composition type);
+    [[clang::warn_unused_result]] Error setDataspace(
+            android_dataspace_t dataspace);
     [[clang::warn_unused_result]] Error setDisplayFrame(
             const android::Rect& frame);
     [[clang::warn_unused_result]] Error setPlaneAlpha(float alpha);
diff --git a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
index 6ebcdfe..a90e074 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
@@ -75,6 +75,8 @@
 
 using namespace HWC2;
 
+static constexpr Attribute ColorTransform = static_cast<Attribute>(6);
+
 namespace android {
 
 void HWC2On1Adapter::DisplayContentsDeleter::operator()(
@@ -214,6 +216,10 @@
                     displayHook<decltype(&Display::getChangedCompositionTypes),
                     &Display::getChangedCompositionTypes, uint32_t*,
                     hwc2_layer_t*, int32_t*>);
+        case FunctionDescriptor::GetColorModes:
+            return asFP<HWC2_PFN_GET_COLOR_MODES>(
+                    displayHook<decltype(&Display::getColorModes),
+                    &Display::getColorModes, uint32_t*, int32_t*>);
         case FunctionDescriptor::GetDisplayAttribute:
             return asFP<HWC2_PFN_GET_DISPLAY_ATTRIBUTE>(
                     getDisplayAttributeHook);
@@ -261,6 +267,12 @@
                     displayHook<decltype(&Display::setClientTarget),
                     &Display::setClientTarget, buffer_handle_t, int32_t,
                     int32_t>);
+        case FunctionDescriptor::SetColorMode:
+            return asFP<HWC2_PFN_SET_COLOR_MODE>(
+                    displayHook<decltype(&Display::setColorMode),
+                    &Display::setColorMode, int32_t>);
+        case FunctionDescriptor::SetColorTransform:
+            return asFP<HWC2_PFN_SET_COLOR_TRANSFORM>(setColorTransformHook);
         case FunctionDescriptor::SetOutputBuffer:
             return asFP<HWC2_PFN_SET_OUTPUT_BUFFER>(
                     displayHook<decltype(&Display::setOutputBuffer),
@@ -299,6 +311,8 @@
         case FunctionDescriptor::SetLayerCompositionType:
             return asFP<HWC2_PFN_SET_LAYER_COMPOSITION_TYPE>(
                     setLayerCompositionTypeHook);
+        case FunctionDescriptor::SetLayerDataspace:
+            return asFP<HWC2_PFN_SET_LAYER_DATASPACE>(setLayerDataspaceHook);
         case FunctionDescriptor::SetLayerDisplayFrame:
             return asFP<HWC2_PFN_SET_LAYER_DISPLAY_FRAME>(
                     layerHook<decltype(&Layer::setDisplayFrame),
@@ -554,6 +568,7 @@
     mVsyncEnabled(Vsync::Invalid),
     mClientTarget(),
     mOutputBuffer(),
+    mHasColorTransform(false),
     mLayers(),
     mHwc1LayerMap() {}
 
@@ -681,6 +696,22 @@
     return Error::None;
 }
 
+Error HWC2On1Adapter::Display::getColorModes(uint32_t* outNumModes,
+        int32_t* outModes)
+{
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    if (!outModes) {
+        *outNumModes = mColorModes.size();
+        return Error::None;
+    }
+    uint32_t numModes = std::min(*outNumModes,
+            static_cast<uint32_t>(mColorModes.size()));
+    std::copy_n(mColorModes.cbegin(), numModes, outModes);
+    *outNumModes = numModes;
+    return Error::None;
+}
+
 Error HWC2On1Adapter::Display::getConfigs(uint32_t* outNumConfigs,
         hwc2_config_t* outConfigs)
 {
@@ -827,14 +858,27 @@
     if (!config) {
         return Error::BadConfig;
     }
-    mActiveConfig = config;
-    if (mDevice.mHwc1MinorVersion >= 4) {
-        int error = mDevice.mHwc1Device->setActiveConfig(mDevice.mHwc1Device,
-                mHwc1Id, static_cast<int>(configId));
-        ALOGE_IF(error != 0,
-                "setActiveConfig: Failed to set active config on HWC1 (%d)",
-                error);
+    if (config == mActiveConfig) {
+        return Error::None;
     }
+
+    if (mDevice.mHwc1MinorVersion >= 4) {
+        uint32_t hwc1Id = 0;
+        auto error = config->getHwc1IdForColorMode(mActiveColorMode, &hwc1Id);
+        if (error != Error::None) {
+            return error;
+        }
+
+        int intError = mDevice.mHwc1Device->setActiveConfig(mDevice.mHwc1Device,
+                mHwc1Id, static_cast<int>(hwc1Id));
+        if (intError != 0) {
+            ALOGE("setActiveConfig: Failed to set active config on HWC1 (%d)",
+                intError);
+            return Error::BadConfig;
+        }
+        mActiveConfig = config;
+    }
+
     return Error::None;
 }
 
@@ -850,6 +894,48 @@
     return Error::None;
 }
 
+Error HWC2On1Adapter::Display::setColorMode(int32_t mode)
+{
+    std::unique_lock<std::recursive_mutex> lock (mStateMutex);
+
+    ALOGV("[%" PRIu64 "] setColorMode(%d)", mId, mode);
+
+    if (mode == mActiveColorMode) {
+        return Error::None;
+    }
+    if (mColorModes.count(mode) == 0) {
+        ALOGE("[%" PRIu64 "] Mode %d not found in mColorModes", mId, mode);
+        return Error::Unsupported;
+    }
+
+    uint32_t hwc1Config = 0;
+    auto error = mActiveConfig->getHwc1IdForColorMode(mode, &hwc1Config);
+    if (error != Error::None) {
+        return error;
+    }
+
+    ALOGV("[%" PRIu64 "] Setting HWC1 config %u", mId, hwc1Config);
+    int intError = mDevice.mHwc1Device->setActiveConfig(mDevice.mHwc1Device,
+            mHwc1Id, hwc1Config);
+    if (intError != 0) {
+        ALOGE("[%" PRIu64 "] Failed to set HWC1 config (%d)", mId, intError);
+        return Error::Unsupported;
+    }
+
+    mActiveColorMode = mode;
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::setColorTransform(android_color_transform_t hint)
+{
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    ALOGV("%" PRIu64 "] setColorTransform(%d)", mId,
+            static_cast<int32_t>(hint));
+    mHasColorTransform = (hint != HAL_COLOR_TRANSFORM_IDENTITY);
+    return Error::None;
+}
+
 Error HWC2On1Adapter::Display::setOutputBuffer(buffer_handle_t buffer,
         int32_t releaseFence)
 {
@@ -1002,7 +1088,17 @@
     return Error::None;
 }
 
-static constexpr uint32_t ATTRIBUTES[] = {
+static constexpr uint32_t ATTRIBUTES_WITH_COLOR[] = {
+    HWC_DISPLAY_VSYNC_PERIOD,
+    HWC_DISPLAY_WIDTH,
+    HWC_DISPLAY_HEIGHT,
+    HWC_DISPLAY_DPI_X,
+    HWC_DISPLAY_DPI_Y,
+    HWC_DISPLAY_COLOR_TRANSFORM,
+    HWC_DISPLAY_NO_ATTRIBUTE,
+};
+
+static constexpr uint32_t ATTRIBUTES_WITHOUT_COLOR[] = {
     HWC_DISPLAY_VSYNC_PERIOD,
     HWC_DISPLAY_WIDTH,
     HWC_DISPLAY_HEIGHT,
@@ -1010,9 +1106,23 @@
     HWC_DISPLAY_DPI_Y,
     HWC_DISPLAY_NO_ATTRIBUTE,
 };
-static constexpr size_t NUM_ATTRIBUTES = sizeof(ATTRIBUTES) / sizeof(uint32_t);
 
-static constexpr uint32_t ATTRIBUTE_MAP[] = {
+static constexpr size_t NUM_ATTRIBUTES_WITH_COLOR =
+        sizeof(ATTRIBUTES_WITH_COLOR) / sizeof(uint32_t);
+static_assert(sizeof(ATTRIBUTES_WITH_COLOR) > sizeof(ATTRIBUTES_WITHOUT_COLOR),
+        "Attribute tables have unexpected sizes");
+
+static constexpr uint32_t ATTRIBUTE_MAP_WITH_COLOR[] = {
+    6, // HWC_DISPLAY_NO_ATTRIBUTE = 0
+    0, // HWC_DISPLAY_VSYNC_PERIOD = 1,
+    1, // HWC_DISPLAY_WIDTH = 2,
+    2, // HWC_DISPLAY_HEIGHT = 3,
+    3, // HWC_DISPLAY_DPI_X = 4,
+    4, // HWC_DISPLAY_DPI_Y = 5,
+    5, // HWC_DISPLAY_COLOR_TRANSFORM = 6,
+};
+
+static constexpr uint32_t ATTRIBUTE_MAP_WITHOUT_COLOR[] = {
     5, // HWC_DISPLAY_NO_ATTRIBUTE = 0
     0, // HWC_DISPLAY_VSYNC_PERIOD = 1,
     1, // HWC_DISPLAY_WIDTH = 2,
@@ -1024,7 +1134,14 @@
 template <uint32_t attribute>
 static constexpr bool attributesMatch()
 {
-    return ATTRIBUTES[ATTRIBUTE_MAP[attribute]] == attribute;
+    bool match = (attribute ==
+            ATTRIBUTES_WITH_COLOR[ATTRIBUTE_MAP_WITH_COLOR[attribute]]);
+    if (attribute == HWC_DISPLAY_COLOR_TRANSFORM) {
+        return match;
+    }
+
+    return match && (attribute ==
+            ATTRIBUTES_WITHOUT_COLOR[ATTRIBUTE_MAP_WITHOUT_COLOR[attribute]]);
 }
 static_assert(attributesMatch<HWC_DISPLAY_VSYNC_PERIOD>(),
         "Tables out of sync");
@@ -1032,6 +1149,8 @@
 static_assert(attributesMatch<HWC_DISPLAY_HEIGHT>(), "Tables out of sync");
 static_assert(attributesMatch<HWC_DISPLAY_DPI_X>(), "Tables out of sync");
 static_assert(attributesMatch<HWC_DISPLAY_DPI_Y>(), "Tables out of sync");
+static_assert(attributesMatch<HWC_DISPLAY_COLOR_TRANSFORM>(),
+        "Tables out of sync");
 
 void HWC2On1Adapter::Display::populateConfigs()
 {
@@ -1052,52 +1171,74 @@
 
     for (size_t c = 0; c < numConfigs; ++c) {
         uint32_t hwc1ConfigId = configs[c];
-        hwc2_config_t id = static_cast<hwc2_config_t>(mConfigs.size());
-        mConfigs.emplace_back(
-                std::make_shared<Config>(*this, id, hwc1ConfigId));
-        auto& config = mConfigs[id];
+        auto newConfig = std::make_shared<Config>(*this);
 
-        int32_t values[NUM_ATTRIBUTES] = {};
-        mDevice.mHwc1Device->getDisplayAttributes(mDevice.mHwc1Device, mHwc1Id,
-                hwc1ConfigId, ATTRIBUTES, values);
-
-        config->setAttribute(Attribute::VsyncPeriod,
-                values[ATTRIBUTE_MAP[HWC_DISPLAY_VSYNC_PERIOD]]);
-        config->setAttribute(Attribute::Width,
-                values[ATTRIBUTE_MAP[HWC_DISPLAY_WIDTH]]);
-        config->setAttribute(Attribute::Height,
-                values[ATTRIBUTE_MAP[HWC_DISPLAY_HEIGHT]]);
-        config->setAttribute(Attribute::DpiX,
-                values[ATTRIBUTE_MAP[HWC_DISPLAY_DPI_X]]);
-        config->setAttribute(Attribute::DpiY,
-                values[ATTRIBUTE_MAP[HWC_DISPLAY_DPI_Y]]);
-
-        ALOGV("Found config: %s", config->toString().c_str());
-    }
-
-    ALOGV("Getting active config");
-    if (mDevice.mHwc1Device->getActiveConfig != nullptr) {
-        auto activeConfig = mDevice.mHwc1Device->getActiveConfig(
-                mDevice.mHwc1Device, mHwc1Id);
-        if (activeConfig >= 0) {
-            ALOGV("Setting active config to %d", activeConfig);
-            mActiveConfig = mConfigs[activeConfig];
+        int32_t values[NUM_ATTRIBUTES_WITH_COLOR] = {};
+        bool hasColor = true;
+        auto result = mDevice.mHwc1Device->getDisplayAttributes(
+                mDevice.mHwc1Device, mHwc1Id, hwc1ConfigId,
+                ATTRIBUTES_WITH_COLOR, values);
+        if (result != 0) {
+            mDevice.mHwc1Device->getDisplayAttributes(mDevice.mHwc1Device,
+                    mHwc1Id, hwc1ConfigId, ATTRIBUTES_WITHOUT_COLOR, values);
+            hasColor = false;
         }
-    } else {
-        ALOGV("getActiveConfig is null, choosing config 0");
-        mActiveConfig = mConfigs[0];
+
+        auto attributeMap = hasColor ?
+                ATTRIBUTE_MAP_WITH_COLOR : ATTRIBUTE_MAP_WITHOUT_COLOR;
+
+        newConfig->setAttribute(Attribute::VsyncPeriod,
+                values[attributeMap[HWC_DISPLAY_VSYNC_PERIOD]]);
+        newConfig->setAttribute(Attribute::Width,
+                values[attributeMap[HWC_DISPLAY_WIDTH]]);
+        newConfig->setAttribute(Attribute::Height,
+                values[attributeMap[HWC_DISPLAY_HEIGHT]]);
+        newConfig->setAttribute(Attribute::DpiX,
+                values[attributeMap[HWC_DISPLAY_DPI_X]]);
+        newConfig->setAttribute(Attribute::DpiY,
+                values[attributeMap[HWC_DISPLAY_DPI_Y]]);
+        if (hasColor) {
+            newConfig->setAttribute(ColorTransform,
+                    values[attributeMap[HWC_DISPLAY_COLOR_TRANSFORM]]);
+        }
+
+        // We can only do this after attempting to read the color transform
+        newConfig->setHwc1Id(hwc1ConfigId);
+
+        for (auto& existingConfig : mConfigs) {
+            if (existingConfig->merge(*newConfig)) {
+                ALOGV("Merged config %d with existing config %u: %s",
+                        hwc1ConfigId, existingConfig->getId(),
+                        existingConfig->toString().c_str());
+                newConfig.reset();
+                break;
+            }
+        }
+
+        // If it wasn't merged with any existing config, add it to the end
+        if (newConfig) {
+            newConfig->setId(static_cast<hwc2_config_t>(mConfigs.size()));
+            ALOGV("Found new config %u: %s", newConfig->getId(),
+                    newConfig->toString().c_str());
+            mConfigs.emplace_back(std::move(newConfig));
+        }
     }
+
+    initializeActiveConfig();
+    populateColorModes();
 }
 
 void HWC2On1Adapter::Display::populateConfigs(uint32_t width, uint32_t height)
 {
     std::unique_lock<std::recursive_mutex> lock(mStateMutex);
 
-    mConfigs.emplace_back(std::make_shared<Config>(*this, 0, 0));
+    mConfigs.emplace_back(std::make_shared<Config>(*this));
     auto& config = mConfigs[0];
 
     config->setAttribute(Attribute::Width, static_cast<int32_t>(width));
     config->setAttribute(Attribute::Height, static_cast<int32_t>(height));
+    config->setHwc1Id(0);
+    config->setId(0);
     mActiveConfig = config;
 }
 
@@ -1286,6 +1427,12 @@
     }
 }
 
+bool HWC2On1Adapter::Display::hasColorTransform() const
+{
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+    return mHasColorTransform;
+}
+
 static std::string hwc1CompositionString(int32_t type)
 {
     switch (type) {
@@ -1475,17 +1622,23 @@
     output << "Power mode: " << to_string(mPowerMode) << "  ";
     output << "Vsync: " << to_string(mVsyncEnabled) << '\n';
 
-    output << "    " << mConfigs.size() << " Config" <<
-            (mConfigs.size() == 1 ? "" : "s") << " (* Active)\n";
-    for (const auto& config : mConfigs) {
-        if (config == mActiveConfig) {
-            output << "    * " << config->toString();
+    output << "    Color modes [active]:";
+    for (const auto& mode : mColorModes) {
+        if (mode == mActiveColorMode) {
+            output << " [" << mode << ']';
         } else {
-            output << "      " << config->toString();
+            output << " " << mode;
         }
     }
     output << '\n';
 
+    output << "    " << mConfigs.size() << " Config" <<
+            (mConfigs.size() == 1 ? "" : "s") << " (* active)\n";
+    for (const auto& config : mConfigs) {
+        output << (config == mActiveConfig ? "    * " : "      ");
+        output << config->toString(true) << '\n';
+    }
+
     output << "    " << mLayers.size() << " Layer" <<
             (mLayers.size() == 1 ? "" : "s") << '\n';
     for (const auto& layer : mLayers) {
@@ -1523,15 +1676,85 @@
     return mAttributes.at(attribute);
 }
 
-std::string HWC2On1Adapter::Display::Config::toString() const
+void HWC2On1Adapter::Display::Config::setHwc1Id(uint32_t id)
+{
+    int32_t colorTransform = getAttribute(ColorTransform);
+    mHwc1Ids.emplace(colorTransform, id);
+}
+
+bool HWC2On1Adapter::Display::Config::hasHwc1Id(uint32_t id) const
+{
+    for (const auto& idPair : mHwc1Ids) {
+        if (id == idPair.second) {
+            return true;
+        }
+    }
+    return false;
+}
+
+int32_t HWC2On1Adapter::Display::Config::getColorModeForHwc1Id(
+        uint32_t id) const
+{
+    for (const auto& idPair : mHwc1Ids) {
+        if (id == idPair.second) {
+            return idPair.first;
+        }
+    }
+    return -1;
+}
+
+Error HWC2On1Adapter::Display::Config::getHwc1IdForColorMode(int32_t mode,
+        uint32_t* outId) const
+{
+    for (const auto& idPair : mHwc1Ids) {
+        if (mode == idPair.first) {
+            *outId = idPair.second;
+            return Error::None;
+        }
+    }
+    ALOGE("Unable to find HWC1 ID for color mode %d on config %u", mode, mId);
+    return Error::BadParameter;
+}
+
+bool HWC2On1Adapter::Display::Config::merge(const Config& other)
+{
+    auto attributes = {HWC2::Attribute::Width, HWC2::Attribute::Height,
+            HWC2::Attribute::VsyncPeriod, HWC2::Attribute::DpiX,
+            HWC2::Attribute::DpiY};
+    for (auto attribute : attributes) {
+        if (getAttribute(attribute) != other.getAttribute(attribute)) {
+            return false;
+        }
+    }
+    int32_t otherColorTransform = other.getAttribute(ColorTransform);
+    if (mHwc1Ids.count(otherColorTransform) != 0) {
+        ALOGE("Attempted to merge two configs (%u and %u) which appear to be "
+                "identical", mHwc1Ids.at(otherColorTransform),
+                other.mHwc1Ids.at(otherColorTransform));
+        return false;
+    }
+    mHwc1Ids.emplace(otherColorTransform,
+            other.mHwc1Ids.at(otherColorTransform));
+    return true;
+}
+
+std::set<int32_t> HWC2On1Adapter::Display::Config::getColorTransforms() const
+{
+    std::set<int32_t> colorTransforms;
+    for (const auto& idPair : mHwc1Ids) {
+        colorTransforms.emplace(idPair.first);
+    }
+    return colorTransforms;
+}
+
+std::string HWC2On1Adapter::Display::Config::toString(bool splitLine) const
 {
     std::string output;
 
     const size_t BUFFER_SIZE = 100;
     char buffer[BUFFER_SIZE] = {};
     auto writtenBytes = snprintf(buffer, BUFFER_SIZE,
-            "[%u] %u x %u", mHwcId,
-            mAttributes.at(HWC2::Attribute::Width),
+            "%u x %u", mAttributes.at(HWC2::Attribute::Width),
             mAttributes.at(HWC2::Attribute::Height));
     output.append(buffer, writtenBytes);
 
@@ -1552,6 +1775,31 @@
         output.append(buffer, writtenBytes);
     }
 
+    std::memset(buffer, 0, BUFFER_SIZE);
+    if (splitLine) {
+        writtenBytes = snprintf(buffer, BUFFER_SIZE,
+                "\n        HWC1 ID/Color transform:");
+    } else {
+        writtenBytes = snprintf(buffer, BUFFER_SIZE,
+                ", HWC1 ID/Color transform:");
+    }
+    output.append(buffer, writtenBytes);
+
+
+    for (const auto& id : mHwc1Ids) {
+        int32_t colorTransform = id.first;
+        uint32_t hwc1Id = id.second;
+        std::memset(buffer, 0, BUFFER_SIZE);
+        if (colorTransform == mDisplay.mActiveColorMode) {
+            writtenBytes = snprintf(buffer, BUFFER_SIZE, " [%u/%d]", hwc1Id,
+                    colorTransform);
+        } else {
+            writtenBytes = snprintf(buffer, BUFFER_SIZE, " %u/%d", hwc1Id,
+                    colorTransform);
+        }
+        output.append(buffer, writtenBytes);
+    }
+
     return output;
 }
 
@@ -1564,6 +1812,49 @@
     return mConfigs[configId];
 }
 
+void HWC2On1Adapter::Display::populateColorModes()
+{
+    mColorModes = mConfigs[0]->getColorTransforms();
+    for (const auto& config : mConfigs) {
+        std::set<int32_t> intersection;
+        auto configModes = config->getColorTransforms();
+        std::set_intersection(mColorModes.cbegin(), mColorModes.cend(),
+                configModes.cbegin(), configModes.cend(),
+                std::inserter(intersection, intersection.begin()));
+        std::swap(intersection, mColorModes);
+    }
+}
+
+void HWC2On1Adapter::Display::initializeActiveConfig()
+{
+    if (mDevice.mHwc1Device->getActiveConfig == nullptr) {
+        ALOGV("getActiveConfig is null, choosing config 0");
+        mActiveConfig = mConfigs[0];
+        mActiveColorMode = -1;
+        return;
+    }
+
+    auto activeConfig = mDevice.mHwc1Device->getActiveConfig(
+            mDevice.mHwc1Device, mHwc1Id);
+    if (activeConfig >= 0) {
+        for (const auto& config : mConfigs) {
+            if (config->hasHwc1Id(activeConfig)) {
+                ALOGV("Setting active config to %d for HWC1 config %u",
+                        config->getId(), activeConfig);
+                mActiveConfig = config;
+                mActiveColorMode = config->getColorModeForHwc1Id(activeConfig);
+                break;
+            }
+        }
+        if (!mActiveConfig) {
+            ALOGV("Unable to find active HWC1 config %u, defaulting to "
+                    "config 0", activeConfig);
+            mActiveConfig = mConfigs[0];
+            mActiveColorMode = -1;
+        }
+    }
+}
+
 void HWC2On1Adapter::Display::reallocateHwc1Contents()
 {
     // Allocate an additional layer for the framebuffer target
@@ -1688,6 +1979,7 @@
     mZ(0),
     mReleaseFence(),
     mHwc1Id(0),
+    mHasUnsupportedDataspace(false),
     mHasUnsupportedPlaneAlpha(false) {}
 
 bool HWC2On1Adapter::SortLayersByZ::operator()(
@@ -1748,6 +2040,12 @@
     return Error::None;
 }
 
+Error HWC2On1Adapter::Layer::setDataspace(android_dataspace_t dataspace)
+{
+    mHasUnsupportedDataspace = (dataspace != HAL_DATASPACE_UNKNOWN);
+    return Error::None;
+}
+
 Error HWC2On1Adapter::Layer::setDisplayFrame(hwc_rect_t frame)
 {
     mDisplayFrame.setPending(frame);
@@ -1976,7 +2274,11 @@
 void HWC2On1Adapter::Layer::applyCompositionType(hwc_layer_1_t& hwc1Layer,
         bool applyAllState)
 {
-    if (mHasUnsupportedPlaneAlpha) {
+    // HWC1 never supports color transforms or dataspaces and only sometimes
+    // supports plane alpha (depending on the version). These require us to drop
+    // some or all layers to client composition.
+    if (mHasUnsupportedDataspace || mHasUnsupportedPlaneAlpha ||
+            mDisplay.hasColorTransform()) {
         hwc1Layer.compositionType = HWC_FRAMEBUFFER;
         hwc1Layer.flags = HWC_SKIP_LAYER;
         return;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h
index 6fdb184..caeb188 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h
@@ -191,6 +191,7 @@
                     HWC2::Attribute attribute, int32_t* outValue);
             HWC2::Error getChangedCompositionTypes(uint32_t* outNumElements,
                     hwc2_layer_t* outLayers, int32_t* outTypes);
+            HWC2::Error getColorModes(uint32_t* outNumModes, int32_t* outModes);
             HWC2::Error getConfigs(uint32_t* outNumConfigs,
                     hwc2_config_t* outConfigIds);
             HWC2::Error getDozeSupport(int32_t* outSupport);
@@ -208,6 +209,8 @@
             HWC2::Error setActiveConfig(hwc2_config_t configId);
             HWC2::Error setClientTarget(buffer_handle_t target,
                     int32_t acquireFence, int32_t dataspace);
+            HWC2::Error setColorMode(int32_t mode);
+            HWC2::Error setColorTransform(android_color_transform_t hint);
             HWC2::Error setOutputBuffer(buffer_handle_t buffer,
                     int32_t releaseFence);
             HWC2::Error setPowerMode(HWC2::PowerMode mode);
@@ -231,34 +234,50 @@
             void addRetireFence(int fenceFd);
             void addReleaseFences(const hwc_display_contents_1& hwcContents);
 
+            bool hasColorTransform() const;
+
             std::string dump() const;
 
         private:
             class Config {
                 public:
-                    Config(Display& display, hwc2_config_t id, uint32_t hwcId)
+                    Config(Display& display)
                       : mDisplay(display),
-                        mId(id),
-                        mHwcId(hwcId),
                         mAttributes() {}
 
                     bool isOnDisplay(const Display& display) const {
                         return display.getId() == mDisplay.getId();
                     }
 
-                    hwc2_config_t getId() const { return mId; }
-                    uint32_t getHwcId() const { return mHwcId; }
-
                     void setAttribute(HWC2::Attribute attribute, int32_t value);
                     int32_t getAttribute(HWC2::Attribute attribute) const;
 
-                    std::string toString() const;
+                    void setHwc1Id(uint32_t id);
+                    bool hasHwc1Id(uint32_t id) const;
+                    int32_t getColorModeForHwc1Id(uint32_t id) const;
+                    HWC2::Error getHwc1IdForColorMode(int32_t mode,
+                            uint32_t* outId) const;
+
+                    void setId(hwc2_config_t id) { mId = id; }
+                    hwc2_config_t getId() const { return mId; }
+
+                    // Attempts to merge two configs that differ only in color
+                    // mode. Returns whether the merge was successful
+                    bool merge(const Config& other);
+
+                    std::set<int32_t> getColorTransforms() const;
+
+                    // splitLine divides the output into two lines suitable for
+                    // dumpsys SurfaceFlinger
+                    std::string toString(bool splitLine = false) const;
 
                 private:
                     Display& mDisplay;
-                    const hwc2_config_t mId;
-                    const uint32_t mHwcId;
+                    hwc2_config_t mId;
                     std::unordered_map<HWC2::Attribute, int32_t> mAttributes;
+
+                    // Maps from color transform to HWC1 config ID
+                    std::unordered_map<int32_t, uint32_t> mHwc1Ids;
             };
 
             class Changes {
@@ -312,6 +331,9 @@
             std::shared_ptr<const Config>
                     getConfig(hwc2_config_t configId) const;
 
+            void populateColorModes();
+            void initializeActiveConfig();
+
             void reallocateHwc1Contents();
             void assignHwc1LayerIds();
 
@@ -349,8 +371,11 @@
             std::unique_ptr<Changes> mChanges;
 
             int32_t mHwc1Id;
+
             std::vector<std::shared_ptr<Config>> mConfigs;
             std::shared_ptr<const Config> mActiveConfig;
+            std::set<int32_t> mColorModes;
+            int32_t mActiveColorMode;
             std::string mName;
             HWC2::DisplayType mType;
             HWC2::PowerMode mPowerMode;
@@ -359,6 +384,8 @@
             FencedBuffer mClientTarget;
             FencedBuffer mOutputBuffer;
 
+            bool mHasColorTransform;
+
             std::multiset<std::shared_ptr<Layer>, SortLayersByZ> mLayers;
             std::unordered_map<size_t, std::shared_ptr<Layer>> mHwc1LayerMap;
     };
@@ -390,6 +417,17 @@
                 config, attribute, outValue);
     }
 
+    static int32_t setColorTransformHook(hwc2_device_t* device,
+            hwc2_display_t display, const float* /*matrix*/,
+            int32_t /*android_color_transform_t*/ intHint) {
+        // We intentionally throw away the matrix, because if the hint is
+        // anything other than IDENTITY, we have to fall back to client
+        // composition anyway
+        auto hint = static_cast<android_color_transform_t>(intHint);
+        return callDisplayFunction(device, display, &Display::setColorTransform,
+                hint);
+    }
+
     static int32_t setPowerModeHook(hwc2_device_t* device,
             hwc2_display_t display, int32_t intMode) {
         auto mode = static_cast<HWC2::PowerMode>(intMode);
@@ -467,6 +505,7 @@
             HWC2::Error setBlendMode(HWC2::BlendMode mode);
             HWC2::Error setColor(hwc_color_t color);
             HWC2::Error setCompositionType(HWC2::Composition type);
+            HWC2::Error setDataspace(android_dataspace_t dataspace);
             HWC2::Error setDisplayFrame(hwc_rect_t frame);
             HWC2::Error setPlaneAlpha(float alpha);
             HWC2::Error setSidebandStream(const native_handle_t* stream);
@@ -523,6 +562,7 @@
             DeferredFence mReleaseFence;
 
             size_t mHwc1Id;
+            bool mHasUnsupportedDataspace;
             bool mHasUnsupportedPlaneAlpha;
     };
 
@@ -562,6 +602,13 @@
                 &Layer::setCompositionType, type);
     }
 
+    static int32_t setLayerDataspaceHook(hwc2_device_t* device,
+            hwc2_display_t display, hwc2_layer_t layer, int32_t intDataspace) {
+        auto dataspace = static_cast<android_dataspace_t>(intDataspace);
+        return callLayerFunction(device, display, layer, &Layer::setDataspace,
+                dataspace);
+    }
+
     static int32_t setLayerTransformHook(hwc2_device_t* device,
             hwc2_display_t display, hwc2_layer_t layer, int32_t intTransform) {
         auto transform = static_cast<HWC2::Transform>(intTransform);
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 96252f3..ed320cb 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1820,6 +1820,7 @@
                         // NOTE: We don't need to hold the transaction lock here
                         // because State::active is only accessed from this thread.
                         current.active = front.active;
+                        current.modified = true;
 
                         // recompute visible region
                         recomputeVisibleRegions = true;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index a69b11b..91815f3 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1719,17 +1719,13 @@
                 // Remove the transparent area from the visible region
                 if (translucent) {
                     const Transform tr(s.active.transform);
-                    if (tr.transformed()) {
-                        if (tr.preserveRects()) {
-                            // transform the transparent region
-                            transparentRegion = tr.transform(s.activeTransparentRegion);
-                        } else {
-                            // transformation too complex, can't do the
-                            // transparent region optimization.
-                            transparentRegion.clear();
-                        }
+                    if (tr.preserveRects()) {
+                        // transform the transparent region
+                        transparentRegion = tr.transform(s.activeTransparentRegion);
                     } else {
-                        transparentRegion = s.activeTransparentRegion;
+                        // transformation too complex, can't do the
+                        // transparent region optimization.
+                        transparentRegion.clear();
                     }
                 }
 
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index 00700ab..de46dfa 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -1747,17 +1747,13 @@
                 // Remove the transparent area from the visible region
                 if (translucent) {
                     const Transform tr(s.active.transform);
-                    if (tr.transformed()) {
-                        if (tr.preserveRects()) {
-                            // transform the transparent region
-                            transparentRegion = tr.transform(s.activeTransparentRegion);
-                        } else {
-                            // transformation too complex, can't do the
-                            // transparent region optimization.
-                            transparentRegion.clear();
-                        }
+                    if (tr.preserveRects()) {
+                        // transform the transparent region
+                        transparentRegion = tr.transform(s.activeTransparentRegion);
                     } else {
-                        transparentRegion = s.activeTransparentRegion;
+                        // transformation too complex, can't do the
+                        // transparent region optimization.
+                        transparentRegion.clear();
                     }
                 }
 
diff --git a/services/surfaceflinger/Transform.cpp b/services/surfaceflinger/Transform.cpp
index 35e7e7d..c2be91d 100644
--- a/services/surfaceflinger/Transform.cpp
+++ b/services/surfaceflinger/Transform.cpp
@@ -87,10 +87,6 @@
     return mMatrix[i];
 }
 
-bool Transform::transformed() const {
-    return type() > TRANSLATE;
-}
-
 float Transform::tx() const {
     return mMatrix[2][0];
 }
@@ -224,7 +220,7 @@
 Region Transform::transform(const Region& reg) const
 {
     Region out;
-    if (CC_UNLIKELY(transformed())) {
+    if (CC_UNLIKELY(type() > TRANSLATE)) {
         if (CC_LIKELY(preserveRects())) {
             Region::const_iterator it = reg.begin();
             Region::const_iterator const end = reg.end();
diff --git a/services/surfaceflinger/Transform.h b/services/surfaceflinger/Transform.h
index 9efeb9e..90855da 100644
--- a/services/surfaceflinger/Transform.h
+++ b/services/surfaceflinger/Transform.h
@@ -60,7 +60,6 @@
             };
 
             // query the transform
-            bool        transformed() const;
             bool        preserveRects() const;
             uint32_t    getType() const;
             uint32_t    getOrientation() const;