Refactor color space attribute processing.

getColorSpaceAttribute and stripAttributes have closely related purpose.
Merge them into a single processAttribute function.

This also adjusts which color space attributes are passed to the driver,
based on the EGL extension specifications.

Bug: 78247539
Test: cts-tradefed run singleCommand cts-dev -m CtsDeqpTestCases \
--module-arg CtsDeqpTestCases:include-filter:dEQP-EGL.functional.* \
--skip-preconditions

Change-Id: I79650e0066e54ae92033b280d75f17b4060114bd
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index d6c254d..8bb76d2 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -80,6 +80,7 @@
 extern char const * const gExtensionString;
 
 // clang-format off
+// Extensions implemented by the EGL wrapper.
 char const * const gBuiltinExtensionString =
         "EGL_KHR_get_all_proc_addresses "
         "EGL_ANDROID_presentation_time "
@@ -91,6 +92,7 @@
         "EGL_EXT_surface_CTA861_3_metadata "
         ;
 
+// Whitelist of extensions exposed to applications if implemented in the vendor driver.
 char const * const gExtensionString  =
         "EGL_KHR_image "                        // mandatory
         "EGL_KHR_image_base "                   // mandatory
@@ -451,12 +453,8 @@
 // surfaces
 // ----------------------------------------------------------------------------
 
-// Turn linear formats into corresponding sRGB formats when colorspace is
-// EGL_GL_COLORSPACE_SRGB_KHR, or turn sRGB formats into corresponding linear
-// formats when colorspace is EGL_GL_COLORSPACE_LINEAR_KHR. In any cases where
-// the modification isn't possible, the original dataSpace is returned.
-static android_dataspace modifyBufferDataspace(android_dataspace dataSpace,
-                                               EGLint colorspace) {
+// Translates EGL color spaces to Android data spaces.
+static android_dataspace dataSpaceFromEGLColorSpace(EGLint colorspace) {
     if (colorspace == EGL_GL_COLORSPACE_LINEAR_KHR) {
         return HAL_DATASPACE_SRGB_LINEAR;
     } else if (colorspace == EGL_GL_COLORSPACE_SRGB_KHR) {
@@ -474,196 +472,135 @@
     } else if (colorspace == EGL_GL_COLORSPACE_BT2020_PQ_EXT) {
         return HAL_DATASPACE_BT2020_PQ;
     }
-    return dataSpace;
+    return HAL_DATASPACE_UNKNOWN;
 }
 
-// stripAttributes is used by eglCreateWindowSurface, eglCreatePbufferSurface
-// and eglCreatePixmapSurface to clean up color space related Window parameters
-// that a driver does not advertise support for.
-// Return true if stripped_attrib_list has stripped contents.
+// Returns a list of color spaces understood by the vendor EGL driver.
+static std::vector<EGLint> getDriverColorSpaces(egl_display_ptr dp,
+                                                android_pixel_format format) {
+    std::vector<EGLint> colorSpaces;
+    if (!dp->hasColorSpaceSupport) return colorSpaces;
 
-static EGLBoolean stripAttributes(egl_display_ptr dp, const EGLint* attrib_list,
-                                  EGLint format,
-                                  std::vector<EGLint>& stripped_attrib_list) {
-    std::vector<EGLint> allowedColorSpaces;
-    bool haveColorSpaceSupport = dp->haveExtension("EGL_KHR_gl_colorspace");
-    switch (format) {
-        case HAL_PIXEL_FORMAT_RGBA_8888:
-        case HAL_PIXEL_FORMAT_RGBX_8888:
-        // RGB_888 is never returned by getNativePixelFormat, but is included here for completeness.
-        case HAL_PIXEL_FORMAT_RGB_888:
-            if (haveColorSpaceSupport) {
-                // Spec says:
-                //     [fn1] Only OpenGL and OpenGL ES contexts which support sRGB
-                //     rendering must respect requests for EGL_GL_COLORSPACE_SRGB_KHR, and
-                //     only to sRGB formats supported by the context (normally just SRGB8)
-                //     Older versions not supporting sRGB rendering will ignore this
-                //     surface attribute.
-                //
-                // We support sRGB and pixel format is SRGB8, so allow
-                // the EGL_GL_COLORSPACE_SRGB_KHR and
-                // EGL_GL_COLORSPACE_LINEAR_KHR
-                // colorspaces to be specified.
+    // OpenGL drivers only support sRGB encoding with 8-bit formats.
+    // RGB_888 is never returned by getNativePixelFormat, but is included for completeness.
+    const bool formatSupportsSRGBEncoding =
+        format == HAL_PIXEL_FORMAT_RGBA_8888 || format == HAL_PIXEL_FORMAT_RGBX_8888 ||
+        format == HAL_PIXEL_FORMAT_RGB_888;
+    const bool formatIsFloatingPoint = format == HAL_PIXEL_FORMAT_RGBA_FP16;
 
-                allowedColorSpaces.push_back(EGL_GL_COLORSPACE_SRGB_KHR);
-                allowedColorSpaces.push_back(EGL_GL_COLORSPACE_LINEAR_KHR);
-            }
-            if (findExtension(dp->disp.queryString.extensions,
-                                  "EGL_EXT_gl_colorspace_display_p3_linear")) {
-                allowedColorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT);
-            }
-            if (findExtension(dp->disp.queryString.extensions,
-                                  "EGL_EXT_gl_colorspace_display_p3")) {
-                allowedColorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_EXT);
-            }
-            if (findExtension(dp->disp.queryString.extensions,
-                                  "EGL_EXT_gl_colorspace_bt2020_linear")) {
-                allowedColorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_LINEAR_EXT);
-            }
-            if (findExtension(dp->disp.queryString.extensions,
-                                  "EGL_EXT_gl_colorspace_bt2020_pq")) {
-                allowedColorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_PQ_EXT);
-            }
-            if (findExtension(dp->disp.queryString.extensions,
-                                  "EGL_EXT_gl_colorspace_scrgb_linear")) {
-                allowedColorSpaces.push_back(EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT);
-            }
-            break;
-
-        case HAL_PIXEL_FORMAT_RGBA_FP16:
-        case HAL_PIXEL_FORMAT_RGBA_1010102:
-        case HAL_PIXEL_FORMAT_RGB_565:
-            // Future: if driver supports XXXX extension, we can pass down that colorspace
-        default:
-            break;
-    }
-
-    if (!attrib_list) return false;
-
-    bool stripped = false;
-    for (const EGLint* attr = attrib_list; attr[0] != EGL_NONE; attr += 2) {
-        switch (attr[0]) {
-            case EGL_GL_COLORSPACE_KHR: {
-                    EGLint colorSpace = attr[1];
-                    bool found = false;
-                    // Verify that color space is allowed
-                    for (auto it : allowedColorSpaces) {
-                        if (colorSpace == it) {
-                            found = true;
-                        }
-                    }
-                    if (found) {
-                        // Found supported attribute
-                        stripped_attrib_list.push_back(attr[0]);
-                        stripped_attrib_list.push_back(attr[1]);
-                    } else if (!haveColorSpaceSupport) {
-                        // Device does not support colorspace extension
-                        // pass on the attribute and let downstream
-                        // components validate like normal
-                        stripped_attrib_list.push_back(attr[0]);
-                        stripped_attrib_list.push_back(attr[1]);
-                    } else {
-                        // Found supported attribute that driver does not
-                        // support, strip it.
-                        stripped = true;
-                    }
-                }
-                break;
-            default:
-                stripped_attrib_list.push_back(attr[0]);
-                stripped_attrib_list.push_back(attr[1]);
-                break;
+    if (formatSupportsSRGBEncoding) {
+        // sRGB and linear are always supported when color space support is present.
+        colorSpaces.push_back(EGL_GL_COLORSPACE_SRGB_KHR);
+        colorSpaces.push_back(EGL_GL_COLORSPACE_LINEAR_KHR);
+        // DCI-P3 uses the sRGB transfer function, so it's only relevant for 8-bit formats.
+        if (findExtension(dp->disp.queryString.extensions,
+                              "EGL_EXT_gl_colorspace_display_p3")) {
+            colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_EXT);
         }
     }
 
-    // Make sure there is at least one attribute
-    if (stripped) {
-        stripped_attrib_list.push_back(EGL_NONE);
+    // According to the spec, scRGB is only supported for floating point formats.
+    // For non-linear scRGB, the application is responsible for applying the
+    // transfer function.
+    if (formatIsFloatingPoint) {
+        if (findExtension(dp->disp.queryString.extensions,
+                  "EGL_EXT_gl_colorspace_scrgb")) {
+            colorSpaces.push_back(EGL_GL_COLORSPACE_SCRGB_EXT);
+        }
+        if (findExtension(dp->disp.queryString.extensions,
+                  "EGL_EXT_gl_colorspace_scrgb_linear")) {
+            colorSpaces.push_back(EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT);
+        }
     }
-    return stripped;
+
+    // BT2020 can be used with any pixel format. PQ encoding must be applied by the
+    // application and does not affect the behavior of OpenGL.
+    if (findExtension(dp->disp.queryString.extensions,
+                          "EGL_EXT_gl_colorspace_bt2020_linear")) {
+        colorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_LINEAR_EXT);
+    }
+    if (findExtension(dp->disp.queryString.extensions,
+                          "EGL_EXT_gl_colorspace_bt2020_pq")) {
+        colorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_PQ_EXT);
+    }
+
+    // Linear DCI-P3 simply uses different primaries than standard RGB and thus
+    // can be used with any pixel format.
+    if (findExtension(dp->disp.queryString.extensions,
+                          "EGL_EXT_gl_colorspace_display_p3_linear")) {
+        colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT);
+    }
+    return colorSpaces;
 }
 
-static EGLBoolean getColorSpaceAttribute(egl_display_ptr dp, NativeWindowType window,
-                                         const EGLint* attrib_list, EGLint& colorSpace,
-                                         android_dataspace& dataSpace) {
-    colorSpace = EGL_GL_COLORSPACE_LINEAR_KHR;
-    dataSpace = HAL_DATASPACE_UNKNOWN;
+// Cleans up color space related parameters that the driver does not understand.
+// If there is no color space attribute in attrib_list, colorSpace is left
+// unmodified.
+static EGLBoolean processAttributes(egl_display_ptr dp, NativeWindowType window,
+                                    android_pixel_format format, const EGLint* attrib_list,
+                                    EGLint* colorSpace,
+                                    std::vector<EGLint>* strippedAttribList) {
+    for (const EGLint* attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) {
+        bool copyAttribute = true;
+        if (attr[0] == EGL_GL_COLORSPACE_KHR) {
+            // Fail immediately if the driver doesn't have color space support at all.
+            if (!dp->hasColorSpaceSupport) return false;
+            *colorSpace = attr[1];
 
-    if (attrib_list && dp->haveExtension("EGL_KHR_gl_colorspace")) {
-        for (const EGLint* attr = attrib_list; *attr != EGL_NONE; attr += 2) {
-            if (*attr == EGL_GL_COLORSPACE_KHR) {
-                colorSpace = attr[1];
-                bool found = false;
-                bool verify = true;
-                // Verify that color space is allowed
-                if (colorSpace == EGL_GL_COLORSPACE_SRGB_KHR ||
-                    colorSpace == EGL_GL_COLORSPACE_LINEAR_KHR) {
-                    // SRGB and LINEAR are always supported when EGL_KHR_gl_colorspace
-                    // is available, so no need to verify.
-                    found = true;
-                    verify = false;
-                } else if (colorSpace == EGL_GL_COLORSPACE_BT2020_LINEAR_EXT &&
-                           dp->haveExtension("EGL_EXT_gl_colorspace_bt2020_linear")) {
-                    found = true;
-                } else if (colorSpace == EGL_GL_COLORSPACE_BT2020_PQ_EXT &&
-                           dp->haveExtension("EGL_EXT_gl_colorspace_bt2020_pq")) {
-                    found = true;
-                } else if (colorSpace == EGL_GL_COLORSPACE_SCRGB_EXT &&
-                           dp->haveExtension("EGL_EXT_gl_colorspace_scrgb")) {
-                    found = true;
-                } else if (colorSpace == EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT &&
-                           dp->haveExtension("EGL_EXT_gl_colorspace_scrgb_linear")) {
-                    found = true;
-                } else if (colorSpace == EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT &&
-                           dp->haveExtension("EGL_EXT_gl_colorspace_display_p3_linear")) {
-                    found = true;
-                } else if (colorSpace == EGL_GL_COLORSPACE_DISPLAY_P3_EXT &&
-                           dp->haveExtension("EGL_EXT_gl_colorspace_display_p3")) {
-                    found = true;
+            // Strip the attribute if the driver doesn't understand it.
+            copyAttribute = false;
+            std::vector<EGLint> driverColorSpaces = getDriverColorSpaces(dp, format);
+            for (auto driverColorSpace : driverColorSpaces) {
+                if (attr[1] == driverColorSpace) {
+                    copyAttribute = true;
+                    break;
                 }
-                if (!found) {
-                    return false;
-                }
-                if (verify && window) {
-                    bool wide_color_support = true;
-                    // Ordinarily we'd put a call to native_window_get_wide_color_support
-                    // at the beginning of the function so that we'll have the
-                    // result when needed elsewhere in the function.
-                    // However, because eglCreateWindowSurface is called by SurfaceFlinger and
-                    // SurfaceFlinger is required to answer the call below we would
-                    // end up in a deadlock situation. By moving the call to only happen
-                    // if the application has specifically asked for wide-color we avoid
-                    // the deadlock with SurfaceFlinger since it will not ask for a
-                    // wide-color surface.
-                    int err = native_window_get_wide_color_support(window, &wide_color_support);
-
-                    if (err) {
-                        ALOGE("getColorSpaceAttribute: invalid window (win=%p) "
-                              "failed (%#x) (already connected to another API?)",
-                              window, err);
-                        return false;
-                    }
-                    if (!wide_color_support) {
-                        // Application has asked for a wide-color colorspace but
-                        // wide-color support isn't available on the display the window is on.
-                        return false;
-                    }
-                }
-                // Only change the dataSpace from default if the application
-                // has explicitly set the color space with a EGL_GL_COLORSPACE_KHR attribute.
-                dataSpace = modifyBufferDataspace(dataSpace, colorSpace);
             }
         }
+        if (copyAttribute) {
+            strippedAttribList->push_back(attr[0]);
+            strippedAttribList->push_back(attr[1]);
+        }
+    }
+    // Terminate the attribute list.
+    strippedAttribList->push_back(EGL_NONE);
+
+    // If the passed color space has wide color gamut, check whether the target native window
+    // supports wide color.
+    const bool colorSpaceIsNarrow =
+        *colorSpace == EGL_GL_COLORSPACE_SRGB_KHR ||
+        *colorSpace == EGL_GL_COLORSPACE_LINEAR_KHR;
+    if (window && !colorSpaceIsNarrow) {
+        bool windowSupportsWideColor = true;
+        // Ordinarily we'd put a call to native_window_get_wide_color_support
+        // at the beginning of the function so that we'll have the
+        // result when needed elsewhere in the function.
+        // However, because eglCreateWindowSurface is called by SurfaceFlinger and
+        // SurfaceFlinger is required to answer the call below we would
+        // end up in a deadlock situation. By moving the call to only happen
+        // if the application has specifically asked for wide-color we avoid
+        // the deadlock with SurfaceFlinger since it will not ask for a
+        // wide-color surface.
+        int err = native_window_get_wide_color_support(window, &windowSupportsWideColor);
+
+        if (err) {
+            ALOGE("processAttributes: invalid window (win=%p) "
+                  "failed (%#x) (already connected to another API?)",
+                  window, err);
+            return false;
+        }
+        if (!windowSupportsWideColor) {
+            // Application has asked for a wide-color colorspace but
+            // wide-color support isn't available on the display the window is on.
+            return false;
+        }
     }
     return true;
 }
 
-static EGLBoolean getColorSpaceAttribute(egl_display_ptr dp, const EGLint* attrib_list,
-                                         EGLint& colorSpace, android_dataspace& dataSpace) {
-    return getColorSpaceAttribute(dp, NULL, attrib_list, colorSpace, dataSpace);
-}
-
-void getNativePixelFormat(EGLDisplay dpy, egl_connection_t* cnx, EGLConfig config, EGLint& format) {
+// Gets the native pixel format corrsponding to the passed EGLConfig.
+void getNativePixelFormat(EGLDisplay dpy, egl_connection_t* cnx, EGLConfig config,
+                          android_pixel_format* format) {
     // Set the native window's buffers format to match what this config requests.
     // Whether to use sRGB gamma is not part of the EGLconfig, but is part
     // of our native format. So if sRGB gamma is requested, we have to
@@ -697,27 +634,27 @@
     // endif
     if (a == 0) {
         if (colorDepth <= 16) {
-            format = HAL_PIXEL_FORMAT_RGB_565;
+            *format = HAL_PIXEL_FORMAT_RGB_565;
         } else {
             if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
                 if (colorDepth > 24) {
-                    format = HAL_PIXEL_FORMAT_RGBA_1010102;
+                    *format = HAL_PIXEL_FORMAT_RGBA_1010102;
                 } else {
-                    format = HAL_PIXEL_FORMAT_RGBX_8888;
+                    *format = HAL_PIXEL_FORMAT_RGBX_8888;
                 }
             } else {
-                format = HAL_PIXEL_FORMAT_RGBA_FP16;
+                *format = HAL_PIXEL_FORMAT_RGBA_FP16;
             }
         }
     } else {
         if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
             if (colorDepth > 24) {
-                format = HAL_PIXEL_FORMAT_RGBA_1010102;
+                *format = HAL_PIXEL_FORMAT_RGBA_1010102;
             } else {
-                format = HAL_PIXEL_FORMAT_RGBA_8888;
+                *format = HAL_PIXEL_FORMAT_RGBA_8888;
             }
         } else {
-            format = HAL_PIXEL_FORMAT_RGBA_FP16;
+            *format = HAL_PIXEL_FORMAT_RGBA_FP16;
         }
     }
 }
@@ -758,8 +695,6 @@
     egl_connection_t* cnx = NULL;
     egl_display_ptr dp = validate_display_connection(dpy, cnx);
     if (dp) {
-        EGLDisplay iDpy = dp->disp.dpy;
-
         if (!window) {
             return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
         }
@@ -778,39 +713,36 @@
             return setError(EGL_BAD_ALLOC, EGL_NO_SURFACE);
         }
 
-        EGLint format;
-        getNativePixelFormat(iDpy, cnx, config, format);
+        EGLDisplay iDpy = dp->disp.dpy;
+        android_pixel_format format;
+        getNativePixelFormat(iDpy, cnx, config, &format);
 
         // now select correct colorspace and dataspace based on user's attribute list
-        EGLint colorSpace;
-        android_dataspace dataSpace;
-        if (!getColorSpaceAttribute(dp, window, attrib_list, colorSpace, dataSpace)) {
+        EGLint colorSpace = EGL_GL_COLORSPACE_LINEAR_KHR;
+        std::vector<EGLint> strippedAttribList;
+        if (!processAttributes(dp, window, format, attrib_list, &colorSpace,
+                               &strippedAttribList)) {
             ALOGE("error invalid colorspace: %d", colorSpace);
             return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
         }
+        attrib_list = strippedAttribList.data();
 
-        std::vector<EGLint> strippedAttribList;
-        if (stripAttributes(dp, attrib_list, format, strippedAttribList)) {
-            // Had to modify the attribute list due to use of color space.
-            // Use modified list from here on.
-            attrib_list = strippedAttribList.data();
-        }
-
-        if (format != 0) {
+        {
             int err = native_window_set_buffers_format(window, format);
             if (err != 0) {
                 ALOGE("error setting native window pixel format: %s (%d)",
-                        strerror(-err), err);
+                      strerror(-err), err);
                 native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
                 return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
             }
         }
 
-        if (dataSpace != 0) {
+        android_dataspace dataSpace = dataSpaceFromEGLColorSpace(colorSpace);
+        if (dataSpace != HAL_DATASPACE_UNKNOWN) {
             int err = native_window_set_buffers_data_space(window, dataSpace);
             if (err != 0) {
                 ALOGE("error setting native window pixel dataSpace: %s (%d)",
-                        strerror(-err), err);
+                      strerror(-err), err);
                 native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
                 return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
             }
@@ -844,14 +776,20 @@
 
     egl_connection_t* cnx = NULL;
     egl_display_ptr dp = validate_display_connection(dpy, cnx);
-    EGLint colorSpace;
-    android_dataspace dataSpace;
     if (dp) {
+        EGLDisplay iDpy = dp->disp.dpy;
+        android_pixel_format format;
+        getNativePixelFormat(iDpy, cnx, config, &format);
+
         // now select a corresponding sRGB format if needed
-        if (!getColorSpaceAttribute(dp, attrib_list, colorSpace, dataSpace)) {
+        EGLint colorSpace = EGL_GL_COLORSPACE_LINEAR_KHR;
+        std::vector<EGLint> strippedAttribList;
+        if (!processAttributes(dp, nullptr, format, attrib_list, &colorSpace,
+                               &strippedAttribList)) {
             ALOGE("error invalid colorspace: %d", colorSpace);
             return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
         }
+        attrib_list = strippedAttribList.data();
 
         EGLSurface surface = cnx->egl.eglCreatePixmapSurface(
                 dp->disp.dpy, config, pixmap, attrib_list);
@@ -872,26 +810,18 @@
     egl_display_ptr dp = validate_display_connection(dpy, cnx);
     if (dp) {
         EGLDisplay iDpy = dp->disp.dpy;
-        EGLint format;
-        getNativePixelFormat(iDpy, cnx, config, format);
+        android_pixel_format format;
+        getNativePixelFormat(iDpy, cnx, config, &format);
 
-        // now select correct colorspace and dataspace based on user's attribute list
-        EGLint colorSpace;
-        android_dataspace dataSpace;
-        if (!getColorSpaceAttribute(dp, attrib_list, colorSpace, dataSpace)) {
+        // Select correct colorspace based on user's attribute list
+        EGLint colorSpace = EGL_GL_COLORSPACE_LINEAR_KHR;
+        std::vector<EGLint> strippedAttribList;
+        if (!processAttributes(dp, nullptr, format, attrib_list, &colorSpace,
+                               &strippedAttribList)) {
             ALOGE("error invalid colorspace: %d", colorSpace);
             return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
         }
-
-        // Pbuffers are not displayed so we don't need to store the
-        // colorspace. We do need to filter out color spaces the
-        // driver doesn't know how to process.
-        std::vector<EGLint> strippedAttribList;
-        if (stripAttributes(dp, attrib_list, format, strippedAttribList)) {
-            // Had to modify the attribute list due to use of color space.
-            // Use modified list from here on.
-            attrib_list = strippedAttribList.data();
-        }
+        attrib_list = strippedAttribList.data();
 
         EGLSurface surface = cnx->egl.eglCreatePbufferSurface(
                 dp->disp.dpy, config, attrib_list);
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 74ddd1c..a56890a 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -206,12 +206,16 @@
 
         mExtensionString = gBuiltinExtensionString;
 
+        hasColorSpaceSupport = findExtension(disp.queryString.extensions, "EGL_KHR_gl_colorspace");
+
+        // Note: CDD requires that devices supporting wide color and/or HDR color also support
+        // the EGL_KHR_gl_colorspace extension.
         bool wideColorBoardConfig =
                 getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasWideColorDisplay>(
                         false);
 
         // Add wide-color extensions if device can support wide-color
-        if (wideColorBoardConfig) {
+        if (wideColorBoardConfig && hasColorSpaceSupport) {
             mExtensionString.append(
                     "EGL_EXT_gl_colorspace_scrgb EGL_EXT_gl_colorspace_scrgb_linear "
                     "EGL_EXT_gl_colorspace_display_p3_linear EGL_EXT_gl_colorspace_display_p3 ");
@@ -220,7 +224,7 @@
         bool hasHdrBoardConfig =
                 getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasHDRDisplay>(false);
 
-        if (hasHdrBoardConfig) {
+        if (hasHdrBoardConfig && hasColorSpaceSupport) {
             // hasHDRBoardConfig indicates the system is capable of supporting HDR content.
             // Typically that means there is an HDR capable display attached, but could be
             // support for attaching an HDR display. In either case, advertise support for
diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h
index ccd333d..79a9f08 100644
--- a/opengl/libs/EGL/egl_display.h
+++ b/opengl/libs/EGL/egl_display.h
@@ -113,6 +113,7 @@
     DisplayImpl     disp;
     bool    finishOnSwap;       // property: debug.egl.finish
     bool    traceGpuCompletion; // property: debug.egl.traceGpuCompletion
+    bool    hasColorSpaceSupport;
 
 private:
     friend class egl_display_ptr;