Apply and Track EGL_GL_COLORSPACE_KHR

As part of implementing EGL_KHR_gl_colorspace we need to
keep track of the EGL_GL_COLORSPACE_KHR so that eqlQuerySurface
gets the value the application set.
Do the work in the EGL layer because colorspace information
is not handled in the driver.
Test: adb shell /data/nativetest/EGL_test/EGL_test
Bug: 37709988

Change-Id: Ic92e717a46769144d724f4a84fb3560df076da4f
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index f9229ad..7c4bdf9 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -473,6 +473,45 @@
     return dataSpace;
 }
 
+static EGLBoolean getColorSpaceAttribute(egl_display_ptr dp, const EGLint* attrib_list,
+                                         EGLint& colorSpace, android_dataspace& dataSpace) {
+    colorSpace = EGL_GL_COLORSPACE_LINEAR_KHR;
+    dataSpace = HAL_DATASPACE_UNKNOWN;
+    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;
+                // Verify that color space is allowed
+                if (colorSpace == EGL_GL_COLORSPACE_SRGB_KHR ||
+                    colorSpace == EGL_GL_COLORSPACE_LINEAR_KHR) {
+                    found = true;
+                } else if (colorSpace == EGL_EXT_gl_colorspace_bt2020_linear &&
+                           dp->haveExtension("EGL_EXT_gl_colorspace_bt2020_linear")) {
+                    found = true;
+                } else if (colorSpace == EGL_EXT_gl_colorspace_bt2020_pq &&
+                           dp->haveExtension("EGL_EXT_gl_colorspace_bt2020_pq")) {
+                    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;
+                }
+                if (!found) {
+                    return false;
+                }
+            }
+        }
+        dataSpace = modifyBufferDataspace(dataSpace, colorSpace);
+    }
+    return true;
+}
+
 EGLSurface eglCreateWindowSurface(  EGLDisplay dpy, EGLConfig config,
                                     NativeWindowType window,
                                     const EGLint *attrib_list)
@@ -513,7 +552,8 @@
                                     &componentType);
 
         EGLint format;
-        android_dataspace dataSpace = HAL_DATASPACE_UNKNOWN;
+        EGLint colorSpace;
+        android_dataspace dataSpace;
         EGLint a = 0;
         EGLint r, g, b;
         r = g = b = 0;
@@ -550,12 +590,9 @@
         }
 
         // now select a corresponding sRGB format if needed
-        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) {
-                    dataSpace = modifyBufferDataspace(dataSpace, *(attr+1));
-                }
-            }
+        if (!getColorSpaceAttribute(dp, attrib_list, colorSpace, dataSpace)) {
+            ALOGE("error invalid colorspace: %d", colorSpace);
+            return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
         }
 
         if (format != 0) {
@@ -586,8 +623,8 @@
         EGLSurface surface = cnx->egl.eglCreateWindowSurface(
                 iDpy, config, window, attrib_list);
         if (surface != EGL_NO_SURFACE) {
-            egl_surface_t* s = new egl_surface_t(dp.get(), config, window,
-                    surface, cnx);
+            egl_surface_t* s =
+                    new egl_surface_t(dp.get(), config, window, surface, colorSpace, cnx);
             return s;
         }
 
@@ -606,12 +643,19 @@
 
     egl_connection_t* cnx = NULL;
     egl_display_ptr dp = validate_display_connection(dpy, cnx);
+    EGLint colorSpace;
+    android_dataspace dataSpace;
     if (dp) {
+        // now select a corresponding sRGB format if needed
+        if (!getColorSpaceAttribute(dp, attrib_list, colorSpace, dataSpace)) {
+            ALOGE("error invalid colorspace: %d", colorSpace);
+            return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
+        }
+
         EGLSurface surface = cnx->egl.eglCreatePixmapSurface(
                 dp->disp.dpy, config, pixmap, attrib_list);
         if (surface != EGL_NO_SURFACE) {
-            egl_surface_t* s = new egl_surface_t(dp.get(), config, NULL,
-                    surface, cnx);
+            egl_surface_t* s = new egl_surface_t(dp.get(), config, NULL, surface, colorSpace, cnx);
             return s;
         }
     }
@@ -625,12 +669,19 @@
 
     egl_connection_t* cnx = NULL;
     egl_display_ptr dp = validate_display_connection(dpy, cnx);
+    EGLint colorSpace;
+    android_dataspace dataSpace;
     if (dp) {
+        // now select a corresponding sRGB format if needed
+        if (!getColorSpaceAttribute(dp, attrib_list, colorSpace, dataSpace)) {
+            ALOGE("error invalid colorspace: %d", colorSpace);
+            return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
+        }
+
         EGLSurface surface = cnx->egl.eglCreatePbufferSurface(
                 dp->disp.dpy, config, attrib_list);
         if (surface != EGL_NO_SURFACE) {
-            egl_surface_t* s = new egl_surface_t(dp.get(), config, NULL,
-                    surface, cnx);
+            egl_surface_t* s = new egl_surface_t(dp.get(), config, NULL, surface, colorSpace, cnx);
             return s;
         }
     }
@@ -669,6 +720,10 @@
         return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 
     egl_surface_t const * const s = get_surface(surface);
+    if (attribute == EGL_GL_COLORSPACE_KHR) {
+        *value = s->getColorSpace();
+        return EGL_TRUE;
+    }
     return s->cnx->egl.eglQuerySurface(
             dp->disp.dpy, s->surface, attribute, value);
 }
@@ -1708,13 +1763,22 @@
     egl_display_ptr dp = validate_display(dpy);
     if (!dp) return EGL_NO_SURFACE;
 
+    EGLint colorSpace = EGL_GL_COLORSPACE_LINEAR_KHR;
+    android_dataspace dataSpace = HAL_DATASPACE_UNKNOWN;
+    // TODO: Probably need to update EGL_KHR_stream_producer_eglsurface to
+    // indicate support for EGL_GL_COLORSPACE_KHR.
+    // now select a corresponding sRGB format if needed
+    if (!getColorSpaceAttribute(dp, attrib_list, colorSpace, dataSpace)) {
+        ALOGE("error invalid colorspace: %d", colorSpace);
+        return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
+    }
+
     egl_connection_t* const cnx = &gEGLImpl;
     if (cnx->dso && cnx->egl.eglCreateStreamProducerSurfaceKHR) {
         EGLSurface surface = cnx->egl.eglCreateStreamProducerSurfaceKHR(
                 dp->disp.dpy, config, stream, attrib_list);
         if (surface != EGL_NO_SURFACE) {
-            egl_surface_t* s = new egl_surface_t(dp.get(), config, NULL,
-                    surface, cnx);
+            egl_surface_t* s = new egl_surface_t(dp.get(), config, NULL, surface, colorSpace, cnx);
             return s;
         }
     }
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index 6238780..837cfa9 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -55,12 +55,15 @@
 
 // ----------------------------------------------------------------------------
 
-egl_surface_t::egl_surface_t(egl_display_t* dpy, EGLConfig config,
-        EGLNativeWindowType win, EGLSurface surface,
-        egl_connection_t const* cnx) :
-    egl_object_t(dpy), surface(surface), config(config), win(win), cnx(cnx),
-    connected(true)
-{
+egl_surface_t::egl_surface_t(egl_display_t* dpy, EGLConfig config, EGLNativeWindowType win,
+                             EGLSurface surface, EGLint colorSpace, egl_connection_t const* cnx)
+      : egl_object_t(dpy),
+        surface(surface),
+        config(config),
+        win(win),
+        cnx(cnx),
+        connected(true),
+        colorSpace(colorSpace) {
     if (win) {
         win->incStrong(this);
     }
diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h
index 8988905..7c3075c 100644
--- a/opengl/libs/EGL/egl_object.h
+++ b/opengl/libs/EGL/egl_object.h
@@ -131,12 +131,12 @@
 public:
     typedef egl_object_t::LocalRef<egl_surface_t, EGLSurface> Ref;
 
-    egl_surface_t(egl_display_t* dpy, EGLConfig config,
-            EGLNativeWindowType win, EGLSurface surface,
-            egl_connection_t const* cnx);
+    egl_surface_t(egl_display_t* dpy, EGLConfig config, EGLNativeWindowType win, EGLSurface surface,
+                  EGLint colorSpace, egl_connection_t const* cnx);
 
     ANativeWindow* getNativeWindow() { return win; }
     ANativeWindow* getNativeWindow() const { return win; }
+    EGLint getColorSpace() const { return colorSpace; }
 
     // Try to keep the order of these fields and size unchanged. It's not public API, but
     // it's not hard to imagine native games accessing them.
@@ -149,6 +149,7 @@
 private:
     bool connected;
     void disconnect();
+    EGLint colorSpace;
 };
 
 class egl_context_t: public egl_object_t {