Enable WCG support for ImageWallpaper

Check if the system wallpaper is wcg and request a wcg surface if yes.
Also take multiple display case into account.

Bug: 136338733
Bug: 74008618
Test: Manually
Change-Id: I830dca97be61401453dffb892a7beb8afc0a32f4
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 41604ec..0c9fb2a 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -33,6 +33,7 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.Resources.NotFoundException;
 import android.graphics.Bitmap;
@@ -40,6 +41,8 @@
 import android.graphics.BitmapRegionDecoder;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
+import android.graphics.ColorSpace;
+import android.graphics.ImageDecoder;
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
@@ -63,11 +66,15 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
+import android.view.Display;
 import android.view.WindowManagerGlobal;
 
+import com.android.internal.R;
+
 import libcore.io.IoUtils;
 
 import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
@@ -76,7 +83,10 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -193,7 +203,13 @@
      */
     public static final int FLAG_LOCK = 1 << 1;
 
+    private static final Object sSync = new Object[0];
+    @UnsupportedAppUsage
+    private static Globals sGlobals;
+
     private final Context mContext;
+    private final boolean mWcgEnabled;
+    private final ColorManagementProxy mCmProxy;
 
     /**
      * Special drawable that draws a wallpaper as fast as possible.  Assumes
@@ -388,13 +404,14 @@
         }
 
         public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault,
-                @SetWallpaperFlags int which) {
+                @SetWallpaperFlags int which, ColorManagementProxy cmProxy) {
             return peekWallpaperBitmap(context, returnDefault, which, context.getUserId(),
-                    false /* hardware */);
+                    false /* hardware */, cmProxy);
         }
 
         public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault,
-                @SetWallpaperFlags int which, int userId, boolean hardware) {
+                @SetWallpaperFlags int which, int userId, boolean hardware,
+                ColorManagementProxy cmProxy) {
             if (mService != null) {
                 try {
                     if (!mService.isWallpaperSupported(context.getOpPackageName())) {
@@ -412,7 +429,8 @@
                 mCachedWallpaper = null;
                 mCachedWallpaperUserId = 0;
                 try {
-                    mCachedWallpaper = getCurrentWallpaperLocked(context, userId, hardware);
+                    mCachedWallpaper = getCurrentWallpaperLocked(
+                            context, userId, hardware, cmProxy);
                     mCachedWallpaperUserId = userId;
                 } catch (OutOfMemoryError e) {
                     Log.w(TAG, "Out of memory loading the current wallpaper: " + e);
@@ -450,7 +468,8 @@
             }
         }
 
-        private Bitmap getCurrentWallpaperLocked(Context context, int userId, boolean hardware) {
+        private Bitmap getCurrentWallpaperLocked(Context context, int userId, boolean hardware,
+                ColorManagementProxy cmProxy) {
             if (mService == null) {
                 Log.w(TAG, "WallpaperService not running");
                 return null;
@@ -458,21 +477,29 @@
 
             try {
                 Bundle params = new Bundle();
-                ParcelFileDescriptor fd = mService.getWallpaperWithFeature(
+                ParcelFileDescriptor pfd = mService.getWallpaperWithFeature(
                         context.getOpPackageName(), context.getFeatureId(), this, FLAG_SYSTEM,
                         params, userId);
-                if (fd != null) {
-                    try {
-                        BitmapFactory.Options options = new BitmapFactory.Options();
-                        if (hardware) {
-                            options.inPreferredConfig = Bitmap.Config.HARDWARE;
+
+                if (pfd != null) {
+                    try (BufferedInputStream bis = new BufferedInputStream(
+                            new ParcelFileDescriptor.AutoCloseInputStream(pfd))) {
+                        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                        int data;
+                        while ((data = bis.read()) != -1) {
+                            baos.write(data);
                         }
-                        return BitmapFactory.decodeFileDescriptor(
-                                fd.getFileDescriptor(), null, options);
-                    } catch (OutOfMemoryError e) {
+                        ImageDecoder.Source src = ImageDecoder.createSource(baos.toByteArray());
+                        return ImageDecoder.decodeBitmap(src, ((decoder, info, source) -> {
+                            // Mutable and hardware config can't be set at the same time.
+                            decoder.setMutableRequired(!hardware);
+                            // Let's do color management
+                            if (cmProxy != null) {
+                                cmProxy.doColorManagement(decoder, info);
+                            }
+                        }));
+                    } catch (OutOfMemoryError | IOException e) {
                         Log.w(TAG, "Can't decode file", e);
-                    } finally {
-                        IoUtils.closeQuietly(fd);
                     }
                 }
             } catch (RemoteException e) {
@@ -497,10 +524,6 @@
         }
     }
 
-    private static final Object sSync = new Object[0];
-    @UnsupportedAppUsage
-    private static Globals sGlobals;
-
     static void initGlobals(IWallpaperManager service, Looper looper) {
         synchronized (sSync) {
             if (sGlobals == null) {
@@ -514,6 +537,10 @@
         if (service != null) {
             initGlobals(service, context.getMainLooper());
         }
+        // Check if supports mixed color spaces composition in hardware.
+        mWcgEnabled = context.getResources().getConfiguration().isScreenWideColorGamut()
+                && context.getResources().getBoolean(R.bool.config_enableWcgMode);
+        mCmProxy = new ColorManagementProxy(context);
     }
 
     /**
@@ -531,6 +558,22 @@
     }
 
     /**
+     * Indicate whether wcg (Wide Color Gamut) should be enabled.
+     * <p>
+     * Some devices lack of capability of mixed color spaces composition,
+     * enable wcg on such devices might cause memory or battery concern.
+     * <p>
+     * Therefore, in addition to {@link Configuration#isScreenWideColorGamut()},
+     * we also take mixed color spaces composition (config_enableWcgMode) into account.
+     *
+     * @see Configuration#isScreenWideColorGamut()
+     * @return True if wcg should be enabled for this device.
+     */
+    private boolean shouldEnableWideColorGamut() {
+        return mWcgEnabled;
+    }
+
+    /**
      * Retrieve the current system wallpaper; if
      * no wallpaper is set, the system built-in static wallpaper is returned.
      * This is returned as an
@@ -546,7 +589,7 @@
      *     is not able to access the wallpaper.
      */
     public Drawable getDrawable() {
-        Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM);
+        Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM, mCmProxy);
         if (bm != null) {
             Drawable dr = new BitmapDrawable(mContext.getResources(), bm);
             dr.setDither(false);
@@ -777,7 +820,7 @@
      * null pointer if these is none.
      */
     public Drawable peekDrawable() {
-        Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false, FLAG_SYSTEM);
+        Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false, FLAG_SYSTEM, mCmProxy);
         if (bm != null) {
             Drawable dr = new BitmapDrawable(mContext.getResources(), bm);
             dr.setDither(false);
@@ -801,7 +844,7 @@
      */
     @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE)
     public Drawable getFastDrawable() {
-        Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM);
+        Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM, mCmProxy);
         if (bm != null) {
             return new FastBitmapDrawable(bm);
         }
@@ -817,7 +860,7 @@
      */
     @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE)
     public Drawable peekFastDrawable() {
-       Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false, FLAG_SYSTEM);
+        Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false, FLAG_SYSTEM, mCmProxy);
         if (bm != null) {
             return new FastBitmapDrawable(bm);
         }
@@ -825,6 +868,27 @@
     }
 
     /**
+     * Whether the wallpaper supports Wide Color Gamut or not.
+     * @param which The wallpaper whose image file is to be retrieved. Must be a single
+     *     defined kind of wallpaper, either {@link #FLAG_SYSTEM} or {@link #FLAG_LOCK}.
+     * @return true when supported.
+     *
+     * @see #FLAG_LOCK
+     * @see #FLAG_SYSTEM
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE)
+    public boolean wallpaperSupportsWcg(int which) {
+        if (!shouldEnableWideColorGamut()) {
+            return false;
+        }
+        Bitmap bitmap = sGlobals.peekWallpaperBitmap(mContext, false, which, mCmProxy);
+        return bitmap != null && bitmap.getColorSpace() != null
+                && bitmap.getColorSpace() != ColorSpace.get(ColorSpace.Named.SRGB)
+                && mCmProxy.isSupportedColorSpace(bitmap.getColorSpace());
+    }
+
+    /**
      * Like {@link #getDrawable()} but returns a Bitmap with default {@link Bitmap.Config}.
      *
      * @hide
@@ -852,7 +916,8 @@
      * @hide
      */
     public Bitmap getBitmapAsUser(int userId, boolean hardware) {
-        return sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM, userId, hardware);
+        return sGlobals.peekWallpaperBitmap(
+                mContext, true, FLAG_SYSTEM, userId, hardware, mCmProxy);
     }
 
     /**
@@ -1975,6 +2040,33 @@
         return false;
     }
 
+    /**
+     * A private class to help Globals#getCurrentWallpaperLocked handle color management.
+     */
+    private static class ColorManagementProxy {
+        private final Set<ColorSpace> mSupportedColorSpaces = new HashSet<>();
+
+        ColorManagementProxy(Context context) {
+            // Get a list of supported wide gamut color spaces.
+            Display display = context.getDisplay();
+            if (display != null) {
+                mSupportedColorSpaces.addAll(Arrays.asList(display.getSupportedWideColorGamut()));
+            }
+        }
+
+        boolean isSupportedColorSpace(ColorSpace colorSpace) {
+            return colorSpace != null && (colorSpace == ColorSpace.get(ColorSpace.Named.SRGB)
+                    || mSupportedColorSpaces.contains(colorSpace));
+        }
+
+        void doColorManagement(ImageDecoder decoder, ImageDecoder.ImageInfo info) {
+            if (!isSupportedColorSpace(info.getColorSpace())) {
+                decoder.setTargetColorSpace(ColorSpace.get(ColorSpace.Named.SRGB));
+                Log.w(TAG, "Not supported color space: " + info.getColorSpace());
+            }
+        }
+    }
+
     // Private completion callback for setWallpaper() synchronization
     private class WallpaperSetCompletion extends IWallpaperManagerCallback.Stub {
         final CountDownLatch mLatch;
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index ba25093..821bb25 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -43,7 +43,9 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 
 /**
  * Provides information about the size and density of a logical display.
@@ -382,6 +384,23 @@
     /** @hide */
     public static final int COLOR_MODE_DISPLAY_P3 = 9;
 
+    /** @hide **/
+    @IntDef(prefix = {"COLOR_MODE_"}, value = {
+            COLOR_MODE_INVALID,
+            COLOR_MODE_DEFAULT,
+            COLOR_MODE_BT601_625,
+            COLOR_MODE_BT601_625_UNADJUSTED,
+            COLOR_MODE_BT601_525,
+            COLOR_MODE_BT601_525_UNADJUSTED,
+            COLOR_MODE_BT709,
+            COLOR_MODE_DCI_P3,
+            COLOR_MODE_SRGB,
+            COLOR_MODE_ADOBE_RGB,
+            COLOR_MODE_DISPLAY_P3
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ColorMode {}
+
     /**
      * Indicates that when display is removed, all its activities will be moved to the primary
      * display and the topmost activity should become focused.
@@ -960,6 +979,37 @@
     }
 
     /**
+     * Gets the supported wide color gamuts of this device.
+     *
+     * @return Supported WCG color spaces.
+     * @hide
+     */
+    public @ColorMode ColorSpace[] getSupportedWideColorGamut() {
+        synchronized (this) {
+            final ColorSpace[] defaultColorSpaces = new ColorSpace[0];
+            updateDisplayInfoLocked();
+            if (!isWideColorGamut()) {
+                return defaultColorSpaces;
+            }
+
+            final int[] colorModes = getSupportedColorModes();
+            final List<ColorSpace> colorSpaces = new ArrayList<>();
+            for (int colorMode : colorModes) {
+                // Refer to DisplayInfo#isWideColorGamut.
+                switch (colorMode) {
+                    case COLOR_MODE_DCI_P3:
+                        colorSpaces.add(ColorSpace.get(ColorSpace.Named.DCI_P3));
+                        break;
+                    case COLOR_MODE_DISPLAY_P3:
+                        colorSpaces.add(ColorSpace.get(ColorSpace.Named.DISPLAY_P3));
+                        break;
+                }
+            }
+            return colorSpaces.toArray(defaultColorSpaces);
+        }
+    }
+
+    /**
      * Gets the app VSYNC offset, in nanoseconds.  This is a positive value indicating
      * the phase offset of the VSYNC events provided by Choreographer relative to the
      * display refresh.  For example, if Choreographer reports that the refresh occurred
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d6d5c57..a918940 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4248,6 +4248,10 @@
         <!-- Add packages here -->
     </string-array>
 
+    <!-- Whether or not wcg (wide color gamut) should be enabled on this device,
+         we only enabled it while the device has ability of mixed color spaces composition -->
+    <bool name="config_enableWcgMode">false</bool>
+
     <!-- When true, enables the whitelisted app to handle bug reports from power menu short press. -->
     <bool name="config_bugReportHandlerEnabled">false</bool>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 011dc39..c1bebec 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3786,6 +3786,9 @@
 
   <java-symbol type="string" name="accessibility_freeform_caption" />
 
+  <!-- For Wide Color Gamut -->
+  <java-symbol type="bool" name="config_enableWcgMode" />
+
   <!-- For contacts provider. -->
   <java-symbol type="string" name="config_rawContactsLocalAccountName" />
   <java-symbol type="string" name="config_rawContactsLocalAccountType" />
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 29a71673..e50d08c 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -108,12 +108,13 @@
             if (mController != null) {
                 mController.addCallback(this /* StateListener */);
             }
-            mEglHelper = new EglHelper();
-            mRenderer = new ImageWallpaperRenderer(context, this /* SurfaceProxy */);
         }
 
         @Override
         public void onCreate(SurfaceHolder surfaceHolder) {
+            mEglHelper = new EglHelper();
+            // Deferred init renderer because we need to get wallpaper by display context.
+            mRenderer = new ImageWallpaperRenderer(getDisplayContext(), this /* SurfaceProxy */);
             setFixedSizeAllowed(true);
             setOffsetNotificationsEnabled(true);
             updateSurfaceSize();
@@ -177,14 +178,13 @@
                 mRenderer = null;
                 mEglHelper.finish();
                 mEglHelper = null;
-                getSurfaceHolder().getSurface().hwuiDestroy();
             });
         }
 
         @Override
         public void onSurfaceCreated(SurfaceHolder holder) {
             mWorker.getThreadHandler().post(() -> {
-                mEglHelper.init(holder);
+                mEglHelper.init(holder, needSupportWideColorGamut());
                 mRenderer.onSurfaceCreated();
             });
         }
@@ -257,7 +257,7 @@
 
             // Check if we need to recreate egl surface.
             if (mEglHelper.hasEglContext() && !mEglHelper.hasEglSurface()) {
-                if (!mEglHelper.createEglSurface(getSurfaceHolder())) {
+                if (!mEglHelper.createEglSurface(getSurfaceHolder(), needSupportWideColorGamut())) {
                     Log.w(TAG, "recreate egl surface failed!");
                 }
             }
@@ -340,6 +340,10 @@
                     && mController.getState() == StatusBarState.KEYGUARD;
         }
 
+        private boolean needSupportWideColorGamut() {
+            return mRenderer.isWcgContent();
+        }
+
         @Override
         protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
             super.dump(prefix, fd, out, args);
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
index c6812a7..4b28540 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
@@ -51,40 +51,65 @@
 import android.opengl.EGLDisplay;
 import android.opengl.EGLSurface;
 import android.opengl.GLUtils;
+import android.text.TextUtils;
 import android.util.Log;
 import android.view.SurfaceHolder;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
 
 /**
  * A helper class to handle EGL management.
  */
 public class EglHelper {
     private static final String TAG = EglHelper.class.getSimpleName();
+    private static final int OPENGLES_VERSION = 2;
     // Below two constants make drawing at low priority, so other things can preempt our drawing.
     private static final int EGL_CONTEXT_PRIORITY_LEVEL_IMG = 0x3100;
     private static final int EGL_CONTEXT_PRIORITY_LOW_IMG = 0x3103;
     private static final boolean DEBUG = true;
+
+    private static final int EGL_GL_COLORSPACE_KHR = 0x309D;
+    private static final int EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT = 0x3490;
+
     private static final String EGL_IMG_CONTEXT_PRIORITY = "EGL_IMG_context_priority";
 
+    /**
+     * https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_gl_colorspace.txt
+     */
+    private static final String KHR_GL_COLOR_SPACE = "EGL_KHR_gl_colorspace";
+
+    /**
+     * https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_gl_colorspace_display_p3_passthrough.txt
+     */
+    private static final String EXT_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH =
+            "EGL_EXT_gl_colorspace_display_p3_passthrough";
+
     private EGLDisplay mEglDisplay;
     private EGLConfig mEglConfig;
     private EGLContext mEglContext;
     private EGLSurface mEglSurface;
     private final int[] mEglVersion = new int[2];
     private boolean mEglReady;
-    private boolean mContextPrioritySupported;
+    private final Set<String> mExts;
+
+    public EglHelper() {
+        mExts = new HashSet<>();
+        connectDisplay();
+    }
 
     /**
-     * Initialize EGL and prepare EglSurface.
+     * Initialize render context.
      * @param surfaceHolder surface holder.
-     * @return true if EglSurface is ready.
+     * @param wideColorGamut claim if a wcg surface is necessary.
+     * @return true if the render context is ready.
      */
-    public boolean init(SurfaceHolder surfaceHolder) {
-        mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-        if (mEglDisplay == EGL_NO_DISPLAY) {
-            Log.w(TAG, "eglGetDisplay failed: " + GLUtils.getEGLErrorString(eglGetError()));
+    public boolean init(SurfaceHolder surfaceHolder, boolean wideColorGamut) {
+        if (!hasEglDisplay() && !connectDisplay()) {
+            Log.w(TAG, "Can not connect display, abort!");
             return false;
         }
 
@@ -105,25 +130,38 @@
             return false;
         }
 
-        if (!createEglSurface(surfaceHolder)) {
+        if (!createEglSurface(surfaceHolder, wideColorGamut)) {
             Log.w(TAG, "Can't create EGLSurface!");
             return false;
         }
 
-        mContextPrioritySupported = isContextPrioritySuppported();
-
         mEglReady = true;
         return true;
     }
 
-    private boolean isContextPrioritySuppported() {
-        String[] extensions = eglQueryString(mEglDisplay, EGL_EXTENSIONS).split(" ");
-        for (String extension : extensions) {
-            if (extension.equals(EGL_IMG_CONTEXT_PRIORITY)) {
-                return true;
-            }
+    private boolean connectDisplay() {
+        mExts.clear();
+        mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+        if (!hasEglDisplay()) {
+            Log.w(TAG, "eglGetDisplay failed: " + GLUtils.getEGLErrorString(eglGetError()));
+            return false;
         }
-        return false;
+        String queryString = eglQueryString(mEglDisplay, EGL_EXTENSIONS);
+        if (!TextUtils.isEmpty(queryString)) {
+            Collections.addAll(mExts, queryString.split(" "));
+        }
+        return true;
+    }
+
+    private boolean checkExtensionCapability(String extName) {
+        return mExts.contains(extName);
+    }
+
+    private int getWcgCapability() {
+        if (checkExtensionCapability(EXT_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH)) {
+            return EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT;
+        }
+        return 0;
     }
 
     private EGLConfig chooseEglConfig() {
@@ -148,7 +186,7 @@
             EGL_RED_SIZE, 8,
             EGL_GREEN_SIZE, 8,
             EGL_BLUE_SIZE, 8,
-            EGL_ALPHA_SIZE, 8,
+            EGL_ALPHA_SIZE, 0,
             EGL_DEPTH_SIZE, 0,
             EGL_STENCIL_SIZE, 0,
             EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
@@ -160,21 +198,27 @@
     /**
      * Prepare an EglSurface.
      * @param surfaceHolder surface holder.
+     * @param wcg if need to support wcg.
      * @return true if EglSurface is ready.
      */
-    public boolean createEglSurface(SurfaceHolder surfaceHolder) {
+    public boolean createEglSurface(SurfaceHolder surfaceHolder, boolean wcg) {
         if (DEBUG) {
             Log.d(TAG, "createEglSurface start");
         }
 
         if (hasEglDisplay()) {
-            mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, surfaceHolder, null, 0);
+            int[] attrs = null;
+            int wcgCapability = getWcgCapability();
+            if (wcg && checkExtensionCapability(KHR_GL_COLOR_SPACE) && wcgCapability > 0) {
+                attrs = new int[] {EGL_GL_COLORSPACE_KHR, wcgCapability, EGL_NONE};
+            }
+            mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, surfaceHolder, attrs, 0);
         } else {
             Log.w(TAG, "mEglDisplay is null");
             return false;
         }
 
-        if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) {
+        if (!hasEglSurface()) {
             Log.w(TAG, "createWindowSurface failed: " + GLUtils.getEGLErrorString(eglGetError()));
             return false;
         }
@@ -221,12 +265,12 @@
         int[] attrib_list = new int[5];
         int idx = 0;
         attrib_list[idx++] = EGL_CONTEXT_CLIENT_VERSION;
-        attrib_list[idx++] = 2;
-        if (mContextPrioritySupported) {
+        attrib_list[idx++] = OPENGLES_VERSION;
+        if (checkExtensionCapability(EGL_IMG_CONTEXT_PRIORITY)) {
             attrib_list[idx++] = EGL_CONTEXT_PRIORITY_LEVEL_IMG;
             attrib_list[idx++] = EGL_CONTEXT_PRIORITY_LOW_IMG;
         }
-        attrib_list[idx++] = EGL_NONE;
+        attrib_list[idx] = EGL_NONE;
         if (hasEglDisplay()) {
             mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attrib_list, 0);
         } else {
@@ -234,7 +278,7 @@
             return false;
         }
 
-        if (mEglContext == EGL_NO_CONTEXT) {
+        if (!hasEglContext()) {
             Log.w(TAG, "eglCreateContext failed: " + GLUtils.getEGLErrorString(eglGetError()));
             return false;
         }
@@ -260,7 +304,7 @@
      * @return true if EglContext is ready.
      */
     public boolean hasEglContext() {
-        return mEglContext != null;
+        return mEglContext != null && mEglContext != EGL_NO_CONTEXT;
     }
 
     /**
@@ -268,7 +312,7 @@
      * @return true if EglDisplay is ready.
      */
     public boolean hasEglDisplay() {
-        return mEglDisplay != null;
+        return mEglDisplay != null && mEglDisplay != EGL_NO_DISPLAY;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/GLWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/GLWallpaperRenderer.java
index 60ea1cdf..88ab9ef4 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/GLWallpaperRenderer.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/GLWallpaperRenderer.java
@@ -27,6 +27,11 @@
 public interface GLWallpaperRenderer {
 
     /**
+     * Check if the content to render is a WCG content.
+     */
+    boolean isWcgContent();
+
+    /**
      * Called when the surface is created or recreated.
      */
     void onSurfaceCreated();
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java
index 24a4b9e..54eca0e 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java
@@ -116,7 +116,8 @@
             int width = bitmap.getWidth();
             int height = bitmap.getHeight();
 
-            Bitmap grayscale = Bitmap.createBitmap(width, height, bitmap.getConfig());
+            Bitmap grayscale = Bitmap.createBitmap(width, height, bitmap.getConfig(),
+                    false /* hasAlpha */, bitmap.getColorSpace());
             Canvas canvas = new Canvas(grayscale);
             ColorMatrix cm = new ColorMatrix(LUMINOSITY_MATRIX);
             Paint paint = new Paint();
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
index a8371e3..fa8269d 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
@@ -62,6 +62,7 @@
     private boolean mScissorMode;
     private float mXOffset;
     private float mYOffset;
+    private boolean mWcgContent;
 
     public ImageWallpaperRenderer(Context context, SurfaceProxy proxy) {
         mWallpaperManager = context.getSystemService(WallpaperManager.class);
@@ -94,6 +95,11 @@
     }
 
     @Override
+    public boolean isWcgContent() {
+        return mWcgContent;
+    }
+
+    @Override
     public void onSurfaceCreated() {
         glClearColor(0f, 0f, 0f, 1.0f);
         mProgram.useGLProgram(
@@ -112,7 +118,8 @@
             Log.d(TAG, "loadBitmap: mBitmap=" + mBitmap);
         }
         if (mWallpaperManager != null && mBitmap == null) {
-            mBitmap = mWallpaperManager.getBitmap();
+            mBitmap = mWallpaperManager.getBitmap(false /* hardware */);
+            mWcgContent = mWallpaperManager.wallpaperSupportsWcg(WallpaperManager.FLAG_SYSTEM);
             mWallpaperManager.forgetLoadedWallpaper();
             if (mBitmap != null) {
                 float scale = (float) mScissor.height() / mBitmap.getHeight();
@@ -231,6 +238,7 @@
         out.print(prefix); out.print("mYOffset="); out.print(mYOffset);
         out.print(prefix); out.print("threshold="); out.print(mImageProcessHelper.getThreshold());
         out.print(prefix); out.print("mReveal="); out.print(mImageRevealHelper.getReveal());
+        out.print(prefix); out.print("mWcgContent="); out.print(mWcgContent);
         mWallpaper.dump(prefix, fd, out, args);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java
index b4a60d6..a5722e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java
@@ -52,7 +52,7 @@
 
     @Test
     public void testInit_finish() {
-        mEglHelper.init(mSurfaceHolder);
+        mEglHelper.init(mSurfaceHolder, false /* wideColorGamut */);
         mEglHelper.finish();
     }