Merge "Set window to transparent when screenshotting layer." into pi-dev
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index bbff6fb..bcdd03e 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -33,7 +33,7 @@
 #define TEST_APP_PRIVATE_DIR "/data/app-private/"
 #define TEST_APP_EPHEMERAL_DIR "/data/app-ephemeral/"
 #define TEST_ASEC_DIR "/mnt/asec/"
-#define TEST_EXPAND_DIR "/mnt/expand/"
+#define TEST_EXPAND_DIR "/mnt/expand/00000000-0000-0000-0000-000000000000/"
 
 #define TEST_SYSTEM_DIR1 "/system/app/"
 #define TEST_SYSTEM_DIR2 "/vendor/app/"
@@ -116,6 +116,41 @@
             << bad_path5 << " should be rejected as a invalid path";
 }
 
+TEST_F(UtilsTest, IsValidApkPath_TopDir) {
+    EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/com.example"));
+    EXPECT_EQ(0, validate_apk_path(TEST_EXPAND_DIR "app/com.example"));
+    EXPECT_EQ(-1, validate_apk_path(TEST_DATA_DIR "data/com.example"));
+    EXPECT_EQ(-1, validate_apk_path(TEST_EXPAND_DIR "data/com.example"));
+}
+
+TEST_F(UtilsTest, IsValidApkPath_TopFile) {
+    EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/com.example/base.apk"));
+    EXPECT_EQ(0, validate_apk_path(TEST_EXPAND_DIR "app/com.example/base.apk"));
+    EXPECT_EQ(-1, validate_apk_path(TEST_DATA_DIR "data/com.example/base.apk"));
+    EXPECT_EQ(-1, validate_apk_path(TEST_EXPAND_DIR "data/com.example/base.apk"));
+}
+
+TEST_F(UtilsTest, IsValidApkPath_OatDir) {
+    EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/com.example/oat"));
+    EXPECT_EQ(0, validate_apk_path_subdirs(TEST_EXPAND_DIR "app/com.example/oat"));
+    EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_DATA_DIR "data/com.example/oat"));
+    EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_EXPAND_DIR "data/com.example/oat"));
+}
+
+TEST_F(UtilsTest, IsValidApkPath_OatDirDir) {
+    EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/com.example/oat/arm64"));
+    EXPECT_EQ(0, validate_apk_path_subdirs(TEST_EXPAND_DIR "app/com.example/oat/arm64"));
+    EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_DATA_DIR "data/com.example/oat/arm64"));
+    EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_EXPAND_DIR "data/com.example/oat/arm64"));
+}
+
+TEST_F(UtilsTest, IsValidApkPath_OatDirDirFile) {
+    EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/com.example/oat/arm64/base.odex"));
+    EXPECT_EQ(0, validate_apk_path_subdirs(TEST_EXPAND_DIR "app/com.example/oat/arm64/base.odex"));
+    EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_DATA_DIR "data/com.example/oat/arm64/base.odex"));
+    EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_EXPAND_DIR "data/com.example/oat/arm64/base.odex"));
+}
+
 TEST_F(UtilsTest, IsValidApkPath_Private) {
     // Internal directories
     const char *private1 = TEST_APP_PRIVATE_DIR "example.apk";
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index a8c32ed..1ff45e4 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -855,21 +855,25 @@
  * that path. Returns -1 when an invalid path is encountered and 0 when a valid path
  * is encountered.
  */
-static int validate_apk_path_internal(const char *path, int maxSubdirs) {
-    std::string path_ = path;
-    if (validate_path(android_app_dir, path_, maxSubdirs) == 0) {
+static int validate_apk_path_internal(const std::string& path, int maxSubdirs) {
+    if (validate_path(android_app_dir, path, maxSubdirs) == 0) {
         return 0;
-    } else if (validate_path(android_app_private_dir, path_, maxSubdirs) == 0) {
+    } else if (validate_path(android_app_private_dir, path, maxSubdirs) == 0) {
         return 0;
-    } else if (validate_path(android_app_ephemeral_dir, path_, maxSubdirs) == 0) {
+    } else if (validate_path(android_app_ephemeral_dir, path, maxSubdirs) == 0) {
         return 0;
-    } else if (validate_path(android_asec_dir, path_, maxSubdirs) == 0) {
+    } else if (validate_path(android_asec_dir, path, maxSubdirs) == 0) {
         return 0;
-    } else if (validate_path(android_mnt_expand_dir, path_, std::max(maxSubdirs, 2)) == 0) {
-        return 0;
-    } else {
-        return -1;
+    } else if (android::base::StartsWith(path, android_mnt_expand_dir)) {
+        // Rewrite the path as if it were on internal storage, and test that
+        size_t end = path.find('/', android_mnt_expand_dir.size() + 1);
+        if (end != std::string::npos) {
+            auto modified = path;
+            modified.replace(0, end + 1, android_data_dir);
+            return validate_apk_path_internal(modified, maxSubdirs);
+        }
     }
+    return -1;
 }
 
 int validate_apk_path(const char* path) {
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp
index 8e393c0..ff22048 100644
--- a/cmds/lshal/ListCommand.cpp
+++ b/cmds/lshal/ListCommand.cpp
@@ -503,7 +503,7 @@
     using namespace ::android::hidl::manager::V1_0;
     using namespace ::android::hidl::base::V1_0;
     using std::literals::chrono_literals::operator""s;
-    auto ret = timeoutIPC(2s, manager, &IServiceManager::debugDump, [&] (const auto &infos) {
+    auto ret = timeoutIPC(10s, manager, &IServiceManager::debugDump, [&] (const auto &infos) {
         std::map<std::string, TableEntry> entries;
         for (const auto &info : infos) {
             std::string interfaceName = std::string{info.interfaceName.c_str()} + "/" +
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 2d196c1..cb542bf 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -433,6 +433,7 @@
 
     mDataPos = pos;
     mNextObjectHint = 0;
+    mObjectsSorted = false;
 }
 
 status_t Parcel::setDataCapacity(size_t size)
@@ -1469,6 +1470,59 @@
     LOG_ALWAYS_FATAL("Parcel::remove() not yet implemented!");
 }
 
+status_t Parcel::validateReadData(size_t upperBound) const
+{
+    // Don't allow non-object reads on object data
+    if (mObjectsSorted || mObjectsSize <= 1) {
+data_sorted:
+        // Expect to check only against the next object
+        if (mNextObjectHint < mObjectsSize && upperBound > mObjects[mNextObjectHint]) {
+            // For some reason the current read position is greater than the next object
+            // hint. Iterate until we find the right object
+            size_t nextObject = mNextObjectHint;
+            do {
+                if (mDataPos < mObjects[nextObject] + sizeof(flat_binder_object)) {
+                    // Requested info overlaps with an object
+                    ALOGE("Attempt to read from protected data in Parcel %p", this);
+                    return PERMISSION_DENIED;
+                }
+                nextObject++;
+            } while (nextObject < mObjectsSize && upperBound > mObjects[nextObject]);
+            mNextObjectHint = nextObject;
+        }
+        return NO_ERROR;
+    }
+    // Quickly determine if mObjects is sorted.
+    binder_size_t* currObj = mObjects + mObjectsSize - 1;
+    binder_size_t* prevObj = currObj;
+    while (currObj > mObjects) {
+        prevObj--;
+        if(*prevObj > *currObj) {
+            goto data_unsorted;
+        }
+        currObj--;
+    }
+    mObjectsSorted = true;
+    goto data_sorted;
+
+data_unsorted:
+    // Insertion Sort mObjects
+    // Great for mostly sorted lists. If randomly sorted or reverse ordered mObjects become common,
+    // switch to std::sort(mObjects, mObjects + mObjectsSize);
+    for (binder_size_t* iter0 = mObjects + 1; iter0 < mObjects + mObjectsSize; iter0++) {
+        binder_size_t temp = *iter0;
+        binder_size_t* iter1 = iter0 - 1;
+        while (iter1 >= mObjects && *iter1 > temp) {
+            *(iter1 + 1) = *iter1;
+            iter1--;
+        }
+        *(iter1 + 1) = temp;
+    }
+    mNextObjectHint = 0;
+    mObjectsSorted = true;
+    goto data_sorted;
+}
+
 status_t Parcel::read(void* outData, size_t len) const
 {
     if (len > INT32_MAX) {
@@ -1479,6 +1533,10 @@
 
     if ((mDataPos+pad_size(len)) >= mDataPos && (mDataPos+pad_size(len)) <= mDataSize
             && len <= pad_size(len)) {
+        if (mObjectsSize > 0) {
+            status_t err = validateReadData(mDataPos + pad_size(len));
+            if(err != NO_ERROR) return err;
+        }
         memcpy(outData, mData+mDataPos, len);
         mDataPos += pad_size(len);
         ALOGV("read Setting data pos of %p to %zu", this, mDataPos);
@@ -1497,6 +1555,11 @@
 
     if ((mDataPos+pad_size(len)) >= mDataPos && (mDataPos+pad_size(len)) <= mDataSize
             && len <= pad_size(len)) {
+        if (mObjectsSize > 0) {
+            status_t err = validateReadData(mDataPos + pad_size(len));
+            if(err != NO_ERROR) return NULL;
+        }
+
         const void* data = mData+mDataPos;
         mDataPos += pad_size(len);
         ALOGV("readInplace Setting data pos of %p to %zu", this, mDataPos);
@@ -1510,6 +1573,11 @@
     COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
 
     if ((mDataPos+sizeof(T)) <= mDataSize) {
+        if (mObjectsSize > 0) {
+            status_t err = validateReadData(mDataPos + sizeof(T));
+            if(err != NO_ERROR) return err;
+        }
+
         const void* data = mData+mDataPos;
         mDataPos += sizeof(T);
         *pArg =  *reinterpret_cast<const T*>(data);
@@ -2366,6 +2434,7 @@
     mObjects = const_cast<binder_size_t*>(objects);
     mObjectsSize = mObjectsCapacity = objectsCount;
     mNextObjectHint = 0;
+    mObjectsSorted = false;
     mOwner = relFunc;
     mOwnerCookie = relCookie;
     for (size_t i = 0; i < mObjectsSize; i++) {
@@ -2524,6 +2593,7 @@
     mObjects = NULL;
     mObjectsSize = mObjectsCapacity = 0;
     mNextObjectHint = 0;
+    mObjectsSorted = false;
     mHasFds = false;
     mFdsKnown = true;
     mAllowFds = true;
@@ -2610,6 +2680,7 @@
         mDataCapacity = desired;
         mObjectsSize = mObjectsCapacity = objectsSize;
         mNextObjectHint = 0;
+        mObjectsSorted = false;
 
     } else if (mData) {
         if (objectsSize < mObjectsSize) {
@@ -2631,6 +2702,7 @@
             }
             mObjectsSize = objectsSize;
             mNextObjectHint = 0;
+            mObjectsSorted = false;
         }
 
         // We own the data, so we can just do a realloc().
@@ -2703,6 +2775,7 @@
     mObjectsSize = 0;
     mObjectsCapacity = 0;
     mNextObjectHint = 0;
+    mObjectsSorted = false;
     mHasFds = false;
     mFdsKnown = true;
     mAllowFds = true;
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 5d36526..dede78f 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -417,6 +417,7 @@
     void                freeDataNoInit();
     void                initState();
     void                scanForFds() const;
+    status_t            validateReadData(size_t len) const;
                         
     template<class T>
     status_t            readAligned(T *pArg) const;
@@ -463,6 +464,7 @@
     size_t              mObjectsSize;
     size_t              mObjectsCapacity;
     mutable size_t      mNextObjectHint;
+    mutable bool        mObjectsSorted;
 
     mutable bool        mFdsKnown;
     mutable bool        mHasFds;
diff --git a/libs/vr/libpdx/private/pdx/service.h b/libs/vr/libpdx/private/pdx/service.h
index 0d30614..13aa3e9 100644
--- a/libs/vr/libpdx/private/pdx/service.h
+++ b/libs/vr/libpdx/private/pdx/service.h
@@ -589,6 +589,14 @@
   }
 
   /*
+   * Return true if a channel with the given ID exists in the Channel map.
+   */
+  bool HasChannelId(int channel_id) const {
+    std::lock_guard<std::mutex> autolock(channels_mutex_);
+    return channels_.find(channel_id) != channels_.end();
+  }
+
+  /*
    * Subclasses of Service may override this method to provide a text string
    * describing the state of the service. This method is called by
    * HandleSystemMessage in response to the standard
diff --git a/libs/vr/libvrflinger/display_manager_service.cpp b/libs/vr/libvrflinger/display_manager_service.cpp
index ef8cca3..34b3b0a 100644
--- a/libs/vr/libvrflinger/display_manager_service.cpp
+++ b/libs/vr/libvrflinger/display_manager_service.cpp
@@ -45,6 +45,14 @@
   const int user_id = message.GetEffectiveUserId();
   const bool trusted = user_id == AID_ROOT || IsTrustedUid(user_id);
 
+  // Check if the display_manager_ has a defunct channel.
+  if (display_manager_ && !HasChannelId(display_manager_->channel_id())) {
+    ALOGE("DisplayManagerService::OnChannelOpen: Found defunct channel %d with "
+          "no OnChannelClose, clearing prior display manager.",
+          display_manager_->channel_id());
+    display_manager_ = nullptr;
+  }
+
   // Prevent more than one display manager from registering at a time or
   // untrusted UIDs from connecting.
   if (display_manager_ || !trusted) {
diff --git a/opengl/include/GLES2/gl2ext.h b/opengl/include/GLES2/gl2ext.h
index 3ad39d9..e8d7515 100644
--- a/opengl/include/GLES2/gl2ext.h
+++ b/opengl/include/GLES2/gl2ext.h
@@ -3612,17 +3612,6 @@
 #define GL_SHADER_BINARY_VIV              0x8FC4
 #endif /* GL_VIV_shader_binary */
 
-/* Temporary hack to allow frameworks/base/libs/hwui/debug to build.
- * This function was removed from the Khronos version of the headers
- * (it is specified with the EXT prefix, not OES). */
-#ifndef GL_ANDROID_draw_elements_base_vertex_backwards_compatibility
-#define GL_ANDROID_draw_elements_base_vertex_backwards_compatibility 1
-typedef void (GL_APIENTRYP PFNGLMULTIDRAWELEMENTSBASEVERTEXOESPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex);
-#ifdef GL_GLEXT_PROTOTYPES
-GL_APICALL void GL_APIENTRY glMultiDrawElementsBaseVertexOES (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex);
-#endif
-#endif /* GL_ANDROID_draw_elements_base_vertex_backwards_compatibility */
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 58a774b..d90ab1d 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -55,6 +55,7 @@
 using namespace android::hardware::configstore;
 using namespace android::hardware::configstore::V1_0;
 using android::ui::ColorMode;
+using android::ui::RenderIntent;
 
 /*
  * Initialize the display to the specified values.
@@ -75,8 +76,8 @@
         std::unique_ptr<RE::Surface> renderSurface,
         int displayWidth,
         int displayHeight,
-        bool supportWideColor,
-        bool supportHdr,
+        bool hasWideColorGamut,
+        bool hasHdr10,
         int initialPowerMode)
     : lastCompositionHadVisibleLayers(false),
       mFlinger(flinger),
@@ -98,8 +99,8 @@
       mActiveConfig(0),
       mActiveColorMode(ColorMode::NATIVE),
       mColorTransform(HAL_COLOR_TRANSFORM_IDENTITY),
-      mDisplayHasWideColor(supportWideColor),
-      mDisplayHasHdr(supportHdr)
+      mHasWideColorGamut(hasWideColorGamut),
+      mHasHdr10(hasHdr10)
 {
     // clang-format on
 
@@ -268,6 +269,14 @@
     return mActiveColorMode;
 }
 
+RenderIntent DisplayDevice::getActiveRenderIntent() const {
+    return mActiveRenderIntent;
+}
+
+void DisplayDevice::setActiveRenderIntent(RenderIntent renderIntent) {
+    mActiveRenderIntent = renderIntent;
+}
+
 void DisplayDevice::setColorTransform(const mat4& transform) {
     const bool isIdentity = (transform == mat4());
     mColorTransform =
@@ -279,10 +288,15 @@
 }
 
 void DisplayDevice::setCompositionDataSpace(ui::Dataspace dataspace) {
+    mCompositionDataSpace = dataspace;
     ANativeWindow* const window = mNativeWindow.get();
     native_window_set_buffers_data_space(window, static_cast<android_dataspace>(dataspace));
 }
 
+ui::Dataspace DisplayDevice::getCompositionDataSpace() const {
+    return mCompositionDataSpace;
+}
+
 // ----------------------------------------------------------------------------
 
 void DisplayDevice::setLayerStack(uint32_t stack) {
@@ -464,8 +478,8 @@
                         tr[0][1], tr[1][1], tr[2][1], tr[0][2], tr[1][2], tr[2][2]);
     auto const surface = static_cast<Surface*>(window);
     ui::Dataspace dataspace = surface->getBuffersDataSpace();
-    result.appendFormat("   wideColor=%d, hdr=%d, colorMode=%s, dataspace: %s (%d)\n",
-                        mDisplayHasWideColor, mDisplayHasHdr,
+    result.appendFormat("   wideColorGamut=%d, hdr10=%d, colorMode=%s, dataspace: %s (%d)\n",
+                        mHasWideColorGamut, mHasHdr10,
                         decodeColorMode(mActiveColorMode).c_str(),
                         dataspaceDetails(static_cast<android_dataspace>(dataspace)).c_str(), dataspace);
 
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 61ce30c..b8a8906 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -84,8 +84,8 @@
             std::unique_ptr<RE::Surface> renderSurface,
             int displayWidth,
             int displayHeight,
-            bool supportWideColor,
-            bool supportHdr,
+            bool hasWideColorGamut,
+            bool hasHdr10,
             int initialPowerMode);
     // clang-format on
 
@@ -135,8 +135,8 @@
     // machine happy without actually queueing a buffer if nothing has changed
     status_t beginFrame(bool mustRecompose) const;
     status_t prepareFrame(HWComposer& hwc);
-    bool getWideColorSupport() const { return mDisplayHasWideColor; }
-    bool getHdrSupport() const { return mDisplayHasHdr; }
+    bool hasWideColorGamut() const { return mHasWideColorGamut; }
+    bool hasHdr10() const { return mHasHdr10; }
 
     void swapBuffers(HWComposer& hwc) const;
 
@@ -165,9 +165,12 @@
 
     ui::ColorMode getActiveColorMode() const;
     void setActiveColorMode(ui::ColorMode mode);
+    ui::RenderIntent getActiveRenderIntent() const;
+    void setActiveRenderIntent(ui::RenderIntent renderIntent);
     android_color_transform_t getColorTransform() const;
     void setColorTransform(const mat4& transform);
     void setCompositionDataSpace(ui::Dataspace dataspace);
+    ui::Dataspace getCompositionDataSpace() const;
 
     /* ------------------------------------------------------------------------
      * Display active config management.
@@ -241,14 +244,17 @@
     int mActiveConfig;
     // current active color mode
     ui::ColorMode mActiveColorMode;
+    // Current active render intent.
+    ui::RenderIntent mActiveRenderIntent;
+    ui::Dataspace mCompositionDataSpace;
     // Current color transform
     android_color_transform_t mColorTransform;
 
     // Need to know if display is wide-color capable or not.
     // Initialized by SurfaceFlinger when the DisplayDevice is created.
     // Fed to RenderEngine during composition.
-    bool mDisplayHasWideColor;
-    bool mDisplayHasHdr;
+    bool mHasWideColorGamut;
+    bool mHasHdr10;
 };
 
 struct DisplayDeviceState {
@@ -291,9 +297,9 @@
     bool isSecure() const override { return mDevice->isSecure(); }
     bool needsFiltering() const override { return mDevice->needsFiltering(); }
     Rect getSourceCrop() const override { return mSourceCrop; }
-    bool getWideColorSupport() const override { return mDevice->getWideColorSupport(); }
-    ui::ColorMode getActiveColorMode() const override {
-        return mDevice->getActiveColorMode();
+    bool getWideColorSupport() const override { return mDevice->hasWideColorGamut(); }
+    ui::Dataspace getDataSpace() const override {
+        return mDevice->getCompositionDataSpace();
     }
 
 private:
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 9d356d8..8c0050e 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1643,6 +1643,21 @@
     return true;
 }
 
+// Dataspace::UNKNOWN, Dataspace::SRGB, Dataspace::SRGB_LINEAR,
+// Dataspace::V0_SRGB and Dataspace::V0_SRGB_LINEAR are considered legacy
+// SRGB data space for now.
+// Note that Dataspace::V0_SRGB and Dataspace::V0_SRGB_LINEAR are not legacy
+// data space, however since framework doesn't distinguish them out of legacy
+// SRGB, we have to treat them as the same for now.
+bool Layer::isLegacySrgbDataSpace() const {
+    // TODO(lpy) b/77652630, need to figure out when UNKNOWN can be treated as SRGB.
+    return mDrawingState.dataSpace == ui::Dataspace::UNKNOWN ||
+        mDrawingState.dataSpace == ui::Dataspace::SRGB ||
+        mDrawingState.dataSpace == ui::Dataspace::SRGB_LINEAR ||
+        mDrawingState.dataSpace == ui::Dataspace::V0_SRGB ||
+        mDrawingState.dataSpace == ui::Dataspace::V0_SRGB_LINEAR;
+}
+
 void Layer::setParent(const sp<Layer>& layer) {
     mCurrentParent = layer;
 }
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 8d2a048..d382a1a 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -297,6 +297,13 @@
     bool reparent(const sp<IBinder>& newParentHandle);
     bool detachChildren();
 
+    // Before color management is introduced, contents on Android have to be
+    // desaturated in order to match what they appears like visually.
+    // With color management, these contents will appear desaturated, thus
+    // needed to be saturated so that they match what they are designed for
+    // visually. When returns true, legacy SRGB data space is passed to HWC.
+    bool isLegacySrgbDataSpace() const;
+
     // If we have received a new buffer this frame, we will pass its surface
     // damage down to hardware composer. Otherwise, we must send a region with
     // one empty rect.
diff --git a/services/surfaceflinger/LayerStats.cpp b/services/surfaceflinger/LayerStats.cpp
index 38ea6ed..04ab121 100644
--- a/services/surfaceflinger/LayerStats.cpp
+++ b/services/surfaceflinger/LayerStats.cpp
@@ -57,11 +57,12 @@
 }
 
 void LayerStats::traverseLayerTreeStatsLocked(
-        std::vector<std::unique_ptr<LayerProtoParser::Layer>> layerTree,
-        const LayerProtoParser::LayerGlobal* layerGlobal, std::vector<std::string>& layerShapeVec) {
-    for (std::unique_ptr<LayerProtoParser::Layer>& layer : layerTree) {
+        const std::vector<std::unique_ptr<LayerProtoParser::Layer>>& layerTree,
+        const LayerProtoParser::LayerGlobal& layerGlobal,
+        std::vector<std::string>* const outLayerShapeVec) {
+    for (const auto& layer : layerTree) {
         if (!layer) continue;
-        traverseLayerTreeStatsLocked(std::move(layer->children), layerGlobal, layerShapeVec);
+        traverseLayerTreeStatsLocked(layer->children, layerGlobal, outLayerShapeVec);
         std::string key = "";
         base::StringAppendF(&key, ",%s", layer->type.c_str());
         base::StringAppendF(&key, ",%s", layerCompositionType(layer->hwcCompositionType));
@@ -70,21 +71,21 @@
         base::StringAppendF(&key, ",%s", layerPixelFormat(layer->activeBuffer.format));
         base::StringAppendF(&key, ",%s", layer->dataspace.c_str());
         base::StringAppendF(&key, ",%s",
-                            destinationLocation(layer->hwcFrame.left, layerGlobal->resolution[0],
+                            destinationLocation(layer->hwcFrame.left, layerGlobal.resolution[0],
                                                 true));
         base::StringAppendF(&key, ",%s",
-                            destinationLocation(layer->hwcFrame.top, layerGlobal->resolution[1],
+                            destinationLocation(layer->hwcFrame.top, layerGlobal.resolution[1],
                                                 false));
         base::StringAppendF(&key, ",%s",
                             destinationSize(layer->hwcFrame.right - layer->hwcFrame.left,
-                                            layerGlobal->resolution[0], true));
+                                            layerGlobal.resolution[0], true));
         base::StringAppendF(&key, ",%s",
                             destinationSize(layer->hwcFrame.bottom - layer->hwcFrame.top,
-                                            layerGlobal->resolution[1], false));
+                                            layerGlobal.resolution[1], false));
         base::StringAppendF(&key, ",%s", scaleRatioWH(layer.get()).c_str());
         base::StringAppendF(&key, ",%s", alpha(static_cast<float>(layer->color.a)));
 
-        layerShapeVec.push_back(key);
+        outLayerShapeVec->push_back(key);
         ALOGV("%s", key.c_str());
     }
 }
@@ -97,7 +98,7 @@
     std::vector<std::string> layerShapeVec;
 
     std::lock_guard<std::mutex> lock(mMutex);
-    traverseLayerTreeStatsLocked(std::move(layerTree), &layerGlobal, layerShapeVec);
+    traverseLayerTreeStatsLocked(layerTree, layerGlobal, &layerShapeVec);
 
     std::string layerShapeKey =
             base::StringPrintf("%d,%s,%s,%s", static_cast<int32_t>(layerShapeVec.size()),
diff --git a/services/surfaceflinger/LayerStats.h b/services/surfaceflinger/LayerStats.h
index 7871fc6..7a190fd 100644
--- a/services/surfaceflinger/LayerStats.h
+++ b/services/surfaceflinger/LayerStats.h
@@ -38,9 +38,9 @@
 private:
     // Traverse layer tree to get all visible layers' stats
     void traverseLayerTreeStatsLocked(
-            std::vector<std::unique_ptr<LayerProtoParser::Layer>> layerTree,
-            const LayerProtoParser::LayerGlobal* layerGlobal,
-            std::vector<std::string>& layerShapeVec);
+            const std::vector<std::unique_ptr<LayerProtoParser::Layer>>& layerTree,
+            const LayerProtoParser::LayerGlobal& layerGlobal,
+            std::vector<std::string>* const outLayerShapeVec);
     // Convert layer's top-left position into 8x8 percentage of the display
     static const char* destinationLocation(int32_t location, int32_t range, bool isHorizontal);
     // Convert layer's size into 8x8 percentage of the display
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
index 62a3d5b..3630677 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -30,15 +30,14 @@
     virtual bool isSecure() const = 0;
     virtual bool needsFiltering() const = 0;
     virtual Rect getSourceCrop() const = 0;
+    virtual bool getWideColorSupport() const = 0;
+    virtual ui::Dataspace getDataSpace() const = 0;
 
     virtual void render(std::function<void()> drawLayers) { drawLayers(); }
 
     int getReqHeight() const { return mReqHeight; };
     int getReqWidth() const { return mReqWidth; };
     Transform::orientation_flags getRotationFlags() const { return mRotationFlags; };
-    virtual bool getWideColorSupport() const = 0;
-    virtual ui::ColorMode getActiveColorMode() const = 0;
-
     status_t updateDimensions();
 
     CaptureFill getCaptureFill() const { return mCaptureFill; };
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 781a30c..dfde30b 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -157,6 +157,31 @@
     return std::string(value) == "true";
 }
 
+DisplayColorSetting toDisplayColorSetting(int value) {
+    switch(value) {
+        case 0:
+            return DisplayColorSetting::MANAGED;
+        case 1:
+            return DisplayColorSetting::UNMANAGED;
+        case 2:
+            return DisplayColorSetting::ENHANCED;
+        default:
+            return DisplayColorSetting::MANAGED;
+    }
+}
+
+std::string decodeDisplayColorSetting(DisplayColorSetting displayColorSetting) {
+    switch(displayColorSetting) {
+        case DisplayColorSetting::MANAGED:
+            return std::string("Natural Mode");
+        case DisplayColorSetting::UNMANAGED:
+            return std::string("Saturated Mode");
+        case DisplayColorSetting::ENHANCED:
+            return std::string("Auto Color Mode");
+    }
+    return std::string("Unknown Display Color Setting");
+}
+
 NativeWindowSurface::~NativeWindowSurface() = default;
 
 namespace impl {
@@ -690,6 +715,9 @@
         ALOGE("Run StartPropertySetThread failed!");
     }
 
+    mLegacySrgbSaturationMatrix = getBE().mHwc->getDataspaceSaturationMatrix(HWC_DISPLAY_PRIMARY,
+            Dataspace::SRGB_LINEAR);
+
     ALOGV("Done initializing");
 }
 
@@ -697,14 +725,13 @@
     char value[PROPERTY_VALUE_MAX];
 
     property_get("persist.sys.sf.color_saturation", value, "1.0");
-    mSaturation = atof(value);
-    ALOGV("Saturation is set to %.2f", mSaturation);
+    mGlobalSaturationFactor = atof(value);
+    ALOGV("Saturation is set to %.2f", mGlobalSaturationFactor);
 
     property_get("persist.sys.sf.native_mode", value, "0");
-    mForceNativeColorMode = atoi(value) == 1;
-    if (mForceNativeColorMode) {
-        ALOGV("Forcing native color mode");
-    }
+    mDisplayColorSetting = toDisplayColorSetting(atoi(value));
+    ALOGV("Display Color Setting is set to %s.",
+          decodeDisplayColorSetting(mDisplayColorSetting).c_str());
 }
 
 void SurfaceFlinger::startBootAnim() {
@@ -989,11 +1016,29 @@
 }
 
 void SurfaceFlinger::setActiveColorModeInternal(const sp<DisplayDevice>& hw,
-        ColorMode mode) {
+                                                ColorMode mode, Dataspace dataSpace) {
     int32_t type = hw->getDisplayType();
     ColorMode currentMode = hw->getActiveColorMode();
+    Dataspace currentDataSpace = hw->getCompositionDataSpace();
+    RenderIntent currentRenderIntent = hw->getActiveRenderIntent();
 
-    if (mode == currentMode) {
+    // Natural Mode means it's color managed and the color must be right,
+    // thus we pick RenderIntent::COLORIMETRIC as render intent.
+    // Native Mode means the display is not color managed, and whichever
+    // render intent is picked doesn't matter, thus return
+    // RenderIntent::COLORIMETRIC as default here.
+    RenderIntent renderIntent = RenderIntent::COLORIMETRIC;
+
+    // In Auto Color Mode, we want to strech to panel color space, right now
+    // only the built-in display supports it.
+    if (mDisplayColorSetting == DisplayColorSetting::ENHANCED &&
+        mBuiltinDisplaySupportsEnhance &&
+        hw->getDisplayType() == DisplayDevice::DISPLAY_PRIMARY) {
+        renderIntent = RenderIntent::ENHANCE;
+    }
+
+    if (mode == currentMode && dataSpace == currentDataSpace &&
+        renderIntent == currentRenderIntent) {
         return;
     }
 
@@ -1002,11 +1047,15 @@
         return;
     }
 
-    ALOGD("Set active color mode: %s (%d), type=%d", decodeColorMode(mode).c_str(), mode,
-          hw->getDisplayType());
-
     hw->setActiveColorMode(mode);
-    getHwComposer().setActiveColorMode(type, mode, RenderIntent::COLORIMETRIC);
+    hw->setCompositionDataSpace(dataSpace);
+    hw->setActiveRenderIntent(renderIntent);
+    getHwComposer().setActiveColorMode(type, mode, renderIntent);
+
+    ALOGV("Set active color mode: %s (%d), active render intent: %s (%d), type=%d",
+          decodeColorMode(mode).c_str(), mode,
+          decodeRenderIntent(renderIntent).c_str(), renderIntent,
+          hw->getDisplayType());
 }
 
 
@@ -1037,7 +1086,7 @@
                 ALOGW("Attempt to set active color mode %s %d for virtual display",
                       decodeColorMode(mMode).c_str(), mMode);
             } else {
-                mFlinger.setActiveColorModeInternal(hw, mMode);
+                mFlinger.setActiveColorModeInternal(hw, mMode, Dataspace::UNKNOWN);
             }
             return true;
         }
@@ -1072,7 +1121,7 @@
     std::unique_ptr<HdrCapabilities> capabilities =
             getBE().mHwc->getHdrCapabilities(displayDevice->getHwcDisplayId());
     if (capabilities) {
-        if (displayDevice->getWideColorSupport() && !displayDevice->getHdrSupport()) {
+        if (displayDevice->hasWideColorGamut() && !displayDevice->hasHdr10()) {
             // insert HDR10 as we will force client composition for HDR10
             // layers
             std::vector<int32_t> types = capabilities->getSupportedHdrTypes();
@@ -1821,73 +1870,78 @@
 }
 
 mat4 SurfaceFlinger::computeSaturationMatrix() const {
-    if (mSaturation == 1.0f) {
+    if (mGlobalSaturationFactor == 1.0f) {
         return mat4();
     }
 
     // Rec.709 luma coefficients
     float3 luminance{0.213f, 0.715f, 0.072f};
-    luminance *= 1.0f - mSaturation;
+    luminance *= 1.0f - mGlobalSaturationFactor;
     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{luminance.r + mGlobalSaturationFactor, luminance.r, luminance.r, 0.0f},
+        vec4{luminance.g, luminance.g + mGlobalSaturationFactor, luminance.g, 0.0f},
+        vec4{luminance.b, luminance.b, luminance.b + mGlobalSaturationFactor, 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.
-ColorMode SurfaceFlinger::pickColorMode(Dataspace dataSpace) const {
-    if (mForceNativeColorMode) {
-        return ColorMode::NATIVE;
+// Returns a dataspace that fits all visible layers.  The returned dataspace
+// can only be one of
+//
+//  - Dataspace::V0_SRGB
+//  - Dataspace::DISPLAY_P3
+//  - Dataspace::V0_SCRGB_LINEAR
+// TODO(b/73825729) Add BT2020 data space.
+ui::Dataspace SurfaceFlinger::getBestDataspace(
+        const sp<const DisplayDevice>& displayDevice) const {
+    Dataspace bestDataspace = Dataspace::V0_SRGB;
+    for (const auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
+        switch (layer->getDataSpace()) {
+            case Dataspace::V0_SCRGB:
+            case Dataspace::V0_SCRGB_LINEAR:
+                // return immediately
+                return Dataspace::V0_SCRGB_LINEAR;
+            case Dataspace::BT2020_PQ:
+            case Dataspace::BT2020_ITU_PQ:
+                // Historically, HDR dataspaces are ignored by SurfaceFlinger. But
+                // since SurfaceFlinger simulates HDR support now, it should honor
+                // them unless there is also native support.
+                if (!displayDevice->hasHdr10()) {
+                    return Dataspace::V0_SCRGB_LINEAR;
+                }
+                break;
+            case Dataspace::DISPLAY_P3:
+                bestDataspace = Dataspace::DISPLAY_P3;
+                break;
+            default:
+                break;
+        }
     }
 
-    switch (dataSpace) {
-        // treat Unknown as regular SRGB buffer, since that's what the rest of the
-        // system expects.
-        case Dataspace::UNKNOWN:
-        case Dataspace::SRGB:
-        case Dataspace::V0_SRGB:
-            return ColorMode::SRGB;
-            break;
-
-        case Dataspace::DISPLAY_P3:
-            return ColorMode::DISPLAY_P3;
-            break;
-
-        default:
-            // TODO (courtneygo): Do we want to assert an error here?
-            ALOGE("No color mode mapping for %s (%#x)",
-                  dataspaceDetails(static_cast<android_dataspace>(dataSpace)).c_str(),
-                  dataSpace);
-            return ColorMode::SRGB;
-            break;
-    }
+    return bestDataspace;
 }
 
-Dataspace SurfaceFlinger::bestTargetDataSpace(
-        Dataspace a, Dataspace b, bool hasHdr) const {
-    // Only support sRGB and Display-P3 right now.
-    if (a == Dataspace::DISPLAY_P3 || b == Dataspace::DISPLAY_P3) {
-        return Dataspace::DISPLAY_P3;
-    }
-    if (a == Dataspace::V0_SCRGB_LINEAR || b == Dataspace::V0_SCRGB_LINEAR) {
-        return Dataspace::DISPLAY_P3;
-    }
-    if (a == Dataspace::V0_SCRGB || b == Dataspace::V0_SCRGB) {
-        return Dataspace::DISPLAY_P3;
-    }
-    if (!hasHdr) {
-        if (a == Dataspace::BT2020_PQ || b == Dataspace::BT2020_PQ) {
-            return Dataspace::DISPLAY_P3;
-        }
-        if (a == Dataspace::BT2020_ITU_PQ || b == Dataspace::BT2020_ITU_PQ) {
-            return Dataspace::DISPLAY_P3;
-        }
+// Pick the ColorMode / Dataspace for the display device.
+// TODO(b/73825729) Add BT2020 color mode.
+void SurfaceFlinger::pickColorMode(const sp<DisplayDevice>& displayDevice,
+        ColorMode* outMode, Dataspace* outDataSpace) const {
+    if (mDisplayColorSetting == DisplayColorSetting::UNMANAGED) {
+        *outMode = ColorMode::NATIVE;
+        *outDataSpace = Dataspace::UNKNOWN;
+        return;
     }
 
-    return Dataspace::V0_SRGB;
+    switch (getBestDataspace(displayDevice)) {
+        case Dataspace::DISPLAY_P3:
+        case Dataspace::V0_SCRGB_LINEAR:
+            *outMode = ColorMode::DISPLAY_P3;
+            *outDataSpace = Dataspace::DISPLAY_P3;
+            break;
+        default:
+            *outMode = ColorMode::SRGB;
+            *outDataSpace = Dataspace::V0_SRGB;
+            break;
+    }
 }
 
 void SurfaceFlinger::setUpHWComposer() {
@@ -1970,7 +2024,7 @@
         for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
             if ((layer->getDataSpace() == Dataspace::BT2020_PQ ||
                  layer->getDataSpace() == Dataspace::BT2020_ITU_PQ) &&
-                    !displayDevice->getHdrSupport()) {
+                    !displayDevice->hasHdr10()) {
                 layer->forceClientComposition(hwcId);
             }
 
@@ -1984,19 +2038,10 @@
         }
 
         if (hasWideColorDisplay) {
-            ColorMode newColorMode;
-            Dataspace newDataSpace = Dataspace::V0_SRGB;
-
-            for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
-                newDataSpace = bestTargetDataSpace(layer->getDataSpace(), newDataSpace,
-                        displayDevice->getHdrSupport());
-                ALOGV("layer: %s, dataspace: %s (%#x), newDataSpace: %s (%#x)",
-                      layer->getName().string(), dataspaceDetails(static_cast<android_dataspace>(layer->getDataSpace())).c_str(),
-                      layer->getDataSpace(), dataspaceDetails(static_cast<android_dataspace>(newDataSpace)).c_str(), newDataSpace);
-            }
-            newColorMode = pickColorMode(newDataSpace);
-
-            setActiveColorModeInternal(displayDevice, newColorMode);
+            ColorMode colorMode;
+            Dataspace dataSpace;
+            pickColorMode(displayDevice, &colorMode, &dataSpace);
+            setActiveColorModeInternal(displayDevice, colorMode, dataSpace);
         }
     }
 
@@ -2204,29 +2249,40 @@
 sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal(
         const wp<IBinder>& display, int hwcId, const DisplayDeviceState& state,
         const sp<DisplaySurface>& dispSurface, const sp<IGraphicBufferProducer>& producer) {
-    bool hasWideColorSupport = false;
+    bool hasWideColorGamut = false;
     if (hasWideColorDisplay) {
-        std::vector<ColorMode> modes = getHwComposer().getColorModes(state.type);
+        std::vector<ColorMode> modes = getHwComposer().getColorModes(hwcId);
         for (ColorMode colorMode : modes) {
             switch (colorMode) {
                 case ColorMode::DISPLAY_P3:
                 case ColorMode::ADOBE_RGB:
                 case ColorMode::DCI_P3:
-                    hasWideColorSupport = true;
+                    hasWideColorGamut = true;
                     break;
+                // TODO(lpy) Handle BT2020, BT2100_PQ and BT2100_HLG properly.
                 default:
                     break;
             }
+
+            std::vector<RenderIntent> renderIntents = getHwComposer().getRenderIntents(hwcId,
+                                                                                       colorMode);
+            if (state.type == DisplayDevice::DISPLAY_PRIMARY) {
+                for (auto intent : renderIntents) {
+                    if (intent == RenderIntent::ENHANCE) {
+                        mBuiltinDisplaySupportsEnhance = true;
+                        break;
+                    }
+                }
+            }
         }
     }
 
-    bool hasHdrSupport = false;
-    std::unique_ptr<HdrCapabilities> hdrCapabilities =
-            getHwComposer().getHdrCapabilities(state.type);
+    bool hasHdr10 = false;
+    std::unique_ptr<HdrCapabilities> hdrCapabilities = getHwComposer().getHdrCapabilities(hwcId);
     if (hdrCapabilities) {
         const std::vector<int32_t> types = hdrCapabilities->getSupportedHdrTypes();
         auto iter = std::find(types.cbegin(), types.cend(), HAL_HDR_HDR10);
-        hasHdrSupport = iter != types.cend();
+        hasHdr10 = iter != types.cend();
     }
 
     auto nativeWindowSurface = mCreateNativeWindowSurface(producer);
@@ -2260,18 +2316,19 @@
     sp<DisplayDevice> hw =
             new DisplayDevice(this, state.type, hwcId, state.isSecure, display, nativeWindow,
                               dispSurface, std::move(renderSurface), displayWidth, displayHeight,
-                              hasWideColorSupport, hasHdrSupport, initialPowerMode);
+                              hasWideColorGamut, hasHdr10, initialPowerMode);
 
     if (maxFrameBufferAcquiredBuffers >= 3) {
         nativeWindowSurface->preallocateBuffers();
     }
 
     ColorMode defaultColorMode = ColorMode::NATIVE;
-    if (hasWideColorSupport) {
+    Dataspace defaultDataSpace = Dataspace::UNKNOWN;
+    if (hasWideColorGamut) {
         defaultColorMode = ColorMode::SRGB;
+        defaultDataSpace = Dataspace::V0_SRGB;
     }
-    setActiveColorModeInternal(hw, defaultColorMode);
-    hw->setCompositionDataSpace(Dataspace::UNKNOWN);
+    setActiveColorModeInternal(hw, defaultColorMode, defaultDataSpace);
     hw->setLayerStack(state.layerStack);
     hw->setProjection(state.orientation, state.viewport, state.frame);
     hw->setDisplayName(state.displayName);
@@ -2822,23 +2879,27 @@
     const Region bounds(displayDevice->bounds());
     const DisplayRenderArea renderArea(displayDevice);
     const auto hwcId = displayDevice->getHwcDisplayId();
+    const bool hasClientComposition = getBE().mHwc->hasClientComposition(hwcId);
+    const bool hasDeviceComposition = getBE().mHwc->hasDeviceComposition(hwcId);
+    const bool skipClientColorTransform = getBE().mHwc->hasCapability(
+        HWC2::Capability::SkipClientColorTransform);
+    ATRACE_INT("hasClientComposition", hasClientComposition);
 
     mat4 oldColorMatrix;
-    const bool applyColorMatrix = !getBE().mHwc->hasDeviceComposition(hwcId) &&
-            !getBE().mHwc->hasCapability(HWC2::Capability::SkipClientColorTransform);
+    mat4 legacySrgbSaturationMatrix = mLegacySrgbSaturationMatrix;
+    const bool applyColorMatrix = !hasDeviceComposition && !skipClientColorTransform;
     if (applyColorMatrix) {
-        mat4 colorMatrix = mColorMatrix * mDaltonizer();
+        mat4 colorMatrix = mColorMatrix * computeSaturationMatrix() * mDaltonizer();
         oldColorMatrix = getRenderEngine().setupColorTransform(colorMatrix);
+        legacySrgbSaturationMatrix = colorMatrix * legacySrgbSaturationMatrix;
     }
 
-    bool hasClientComposition = getBE().mHwc->hasClientComposition(hwcId);
     if (hasClientComposition) {
         ALOGV("hasClientComposition");
 
         Dataspace outputDataspace = Dataspace::UNKNOWN;
-        if (displayDevice->getWideColorSupport() &&
-              displayDevice->getActiveColorMode() == ColorMode::DISPLAY_P3) {
-            outputDataspace = Dataspace::DISPLAY_P3;
+        if (displayDevice->hasWideColorGamut()) {
+            outputDataspace = displayDevice->getCompositionDataSpace();
         }
         getBE().mRenderEngine->setOutputDataSpace(outputDataspace);
 
@@ -2855,7 +2916,6 @@
         }
 
         // Never touch the framebuffer if we don't have any framebuffer layers
-        const bool hasDeviceComposition = getBE().mHwc->hasDeviceComposition(hwcId);
         if (hasDeviceComposition) {
             // when using overlays, we assume a fully transparent framebuffer
             // NOTE: we could reduce how much we need to clear, for instance
@@ -2930,7 +2990,20 @@
                         break;
                     }
                     case HWC2::Composition::Client: {
+                        // Only apply saturation matrix layer that is legacy SRGB dataspace
+                        // when auto color mode is on.
+                        bool restore = false;
+                        mat4 savedMatrix;
+                        if (mDisplayColorSetting == DisplayColorSetting::ENHANCED &&
+                            layer->isLegacySrgbDataSpace()) {
+                            savedMatrix =
+                                getRenderEngine().setupColorTransform(legacySrgbSaturationMatrix);
+                            restore = true;
+                        }
                         layer->draw(renderArea, clip);
+                        if (restore) {
+                            getRenderEngine().setupColorTransform(savedMatrix);
+                        }
                         break;
                     }
                     default:
@@ -4004,7 +4077,7 @@
 
 void SurfaceFlinger::dumpWideColorInfo(String8& result) const {
     result.appendFormat("hasWideColorDisplay: %d\n", hasWideColorDisplay);
-    result.appendFormat("forceNativeColorMode: %d\n", mForceNativeColorMode);
+    result.appendFormat("DisplayColorSetting: %d\n", mDisplayColorSetting);
 
     // TODO: print out if wide-color mode is active or not
 
@@ -4500,15 +4573,22 @@
                 return NO_ERROR;
             }
             case 1022: { // Set saturation boost
-                mSaturation = std::max(0.0f, std::min(data.readFloat(), 2.0f));
+                mGlobalSaturationFactor = std::max(0.0f, std::min(data.readFloat(), 2.0f));
 
                 invalidateHwcGeometry();
                 repaintEverything();
                 return NO_ERROR;
             }
             case 1023: { // Set native mode
-                mForceNativeColorMode = data.readInt32() == 1;
+                int32_t value = data.readInt32();
+                if (value > 2) {
+                    return BAD_VALUE;
+                }
+                if (value == 2 && !mBuiltinDisplaySupportsEnhance) {
+                    return BAD_VALUE;
+                }
 
+                mDisplayColorSetting = toDisplayColorSetting(value);
                 invalidateHwcGeometry();
                 repaintEverything();
                 return NO_ERROR;
@@ -4535,6 +4615,23 @@
                 reply->writeBool(mTracing.isEnabled());
                 return NO_ERROR;
             }
+            // Is a DisplayColorSetting supported?
+            case 1027: {
+                int32_t value = data.readInt32();
+                switch (value) {
+                    case 0:
+                        reply->writeBool(hasWideColorDisplay);
+                        return NO_ERROR;
+                    case 1:
+                        reply->writeBool(true);
+                        return NO_ERROR;
+                    case 2:
+                        reply->writeBool(mBuiltinDisplaySupportsEnhance);
+                        return NO_ERROR;
+                    default:
+                        return BAD_VALUE;
+                }
+            }
         }
     }
     return err;
@@ -4602,6 +4699,7 @@
                 mCrop(crop),
                 mFlinger(flinger),
                 mChildrenOnly(childrenOnly) {}
+        const Transform& getTransform() const override { return mTransform; }
         Rect getBounds() const override {
             const Layer::State& layerState(mLayer->getDrawingState());
             return Rect(layerState.active.w, layerState.active.h);
@@ -4610,7 +4708,15 @@
         int getWidth() const override { return mLayer->getDrawingState().active.w; }
         bool isSecure() const override { return false; }
         bool needsFiltering() const override { return false; }
-        const Transform& getTransform() const { return mTransform; }
+        Rect getSourceCrop() const override {
+            if (mCrop.isEmpty()) {
+                return getBounds();
+            } else {
+                return mCrop;
+            }
+        }
+        bool getWideColorSupport() const override { return false; }
+        Dataspace getDataSpace() const override { return Dataspace::UNKNOWN; }
 
         class ReparentForDrawing {
         public:
@@ -4639,16 +4745,6 @@
             }
         }
 
-        Rect getSourceCrop() const override {
-            if (mCrop.isEmpty()) {
-                return getBounds();
-            } else {
-                return mCrop;
-            }
-        }
-        bool getWideColorSupport() const override { return false; }
-        ColorMode getActiveColorMode() const override { return ColorMode::NATIVE; }
-
     private:
         const sp<Layer> mLayer;
         const Rect mCrop;
@@ -4820,9 +4916,8 @@
     }
 
     Dataspace outputDataspace = Dataspace::UNKNOWN;
-    if (renderArea.getWideColorSupport() &&
-          renderArea.getActiveColorMode() == ColorMode::DISPLAY_P3) {
-        outputDataspace = Dataspace::DISPLAY_P3;
+    if (renderArea.getWideColorSupport()) {
+        outputDataspace = renderArea.getDataSpace();
     }
     getBE().mRenderEngine->setOutputDataSpace(outputDataspace);
 
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index a29d1d7..14028ff 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -123,6 +123,12 @@
     eTransactionMask          = 0x0f,
 };
 
+enum class DisplayColorSetting : int32_t {
+    MANAGED = 0,
+    UNMANAGED = 1,
+    ENHANCED = 2,
+};
+
 // A thin interface to abstract creating instances of Surface (gui/Surface.h) to
 // use as a NativeWindow.
 class NativeWindowSurface {
@@ -467,7 +473,9 @@
                               bool stateLockHeld);
 
     // Called on the main thread in response to setActiveColorMode()
-    void setActiveColorModeInternal(const sp<DisplayDevice>& hw, ui::ColorMode colorMode);
+    void setActiveColorModeInternal(const sp<DisplayDevice>& hw,
+                                    ui::ColorMode colorMode,
+                                    ui::Dataspace dataSpace);
 
     // Returns whether the transaction actually modified any state
     bool handleMessageTransaction();
@@ -641,9 +649,10 @@
 
     // Given a dataSpace, returns the appropriate color_mode to use
     // to display that dataSpace.
-    ui::ColorMode pickColorMode(ui::Dataspace dataSpace) const;
-    ui::Dataspace bestTargetDataSpace(ui::Dataspace a, ui::Dataspace b,
-            bool hasHdr) const;
+    ui::Dataspace getBestDataspace(const sp<const DisplayDevice>& displayDevice) const;
+    void pickColorMode(const sp<DisplayDevice>& displayDevice,
+                       ui::ColorMode* outMode,
+                       ui::Dataspace* outDataSpace) const;
 
     mat4 computeSaturationMatrix() const;
 
@@ -846,7 +855,6 @@
 
     size_t mNumLayers;
 
-
     // Verify that transaction is being called by an approved process:
     // either AID_GRAPHICS or AID_SYSTEM.
     status_t CheckTransactCodeCredentials(uint32_t code);
@@ -856,8 +864,12 @@
     static bool useVrFlinger;
     std::thread::id mMainThreadId;
 
-    float mSaturation = 1.0f;
-    bool mForceNativeColorMode = false;
+    DisplayColorSetting mDisplayColorSetting = DisplayColorSetting::MANAGED;
+    // Applied on sRGB layers when the render intent is non-colorimetric.
+    mat4 mLegacySrgbSaturationMatrix;
+    // Applied globally.
+    float mGlobalSaturationFactor = 1.0f;
+    bool mBuiltinDisplaySupportsEnhance = false;
 
     using CreateBufferQueueFunction =
             std::function<void(sp<IGraphicBufferProducer>* /* outProducer */,
diff --git a/services/vr/hardware_composer/impl/vr_hwc.cpp b/services/vr/hardware_composer/impl/vr_hwc.cpp
index 11ffc3f..4af47d2 100644
--- a/services/vr/hardware_composer/impl/vr_hwc.cpp
+++ b/services/vr/hardware_composer/impl/vr_hwc.cpp
@@ -251,20 +251,22 @@
 bool VrHwc::hasCapability(hwc2_capability_t /* capability */) { return false; }
 
 void VrHwc::registerEventCallback(EventCallback* callback) {
-  event_callback_ = callback;
-
-  if (client_ != nullptr) {
-    {
-      int32_t width, height;
-      GetPrimaryDisplaySize(&width, &height);
-      std::lock_guard<std::mutex> guard(mutex_);
-      // Create the primary display late to avoid initialization issues between
-      // VR HWC and SurfaceFlinger.
-      displays_[kDefaultDisplayId].reset(new HwcDisplay(width, height));
-    }
-    event_callback_->onHotplug(kDefaultDisplayId,
-                               IComposerCallback::Connection::CONNECTED);
+  {
+    std::lock_guard<std::mutex> guard(mutex_);
+    event_callback_ = callback;
+    int32_t width, height;
+    GetPrimaryDisplaySize(&width, &height);
+    // Create the primary display late to avoid initialization issues between
+    // VR HWC and SurfaceFlinger.
+    displays_[kDefaultDisplayId].reset(new HwcDisplay(width, height));
   }
+  event_callback_->onHotplug(kDefaultDisplayId,
+                             IComposerCallback::Connection::CONNECTED);
+}
+
+void VrHwc::unregisterEventCallback() {
+  std::lock_guard<std::mutex> guard(mutex_);
+  event_callback_ = nullptr;
 }
 
 uint32_t VrHwc::getMaxVirtualDisplayCount() { return 1; }
diff --git a/services/vr/hardware_composer/impl/vr_hwc.h b/services/vr/hardware_composer/impl/vr_hwc.h
index d5d5f55..85e587a 100644
--- a/services/vr/hardware_composer/impl/vr_hwc.h
+++ b/services/vr/hardware_composer/impl/vr_hwc.h
@@ -213,7 +213,7 @@
 
   std::string dumpDebugInfo() override { return {}; }
   void registerEventCallback(EventCallback* callback) override;
-  void unregisterEventCallback() override {}
+  void unregisterEventCallback() override;
 
   uint32_t getMaxVirtualDisplayCount() override;
   Error createVirtualDisplay(uint32_t width, uint32_t height,