Merge "Unit tests for wide color gamut of ImageWallpaper"
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 6f1effd..5f74d2e 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -598,7 +598,8 @@
      *     is not able to access the wallpaper.
      */
     public Drawable getDrawable() {
-        Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM, mCmProxy);
+        final ColorManagementProxy cmProxy = getColorManagementProxy();
+        Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM, cmProxy);
         if (bm != null) {
             Drawable dr = new BitmapDrawable(mContext.getResources(), bm);
             dr.setDither(false);
@@ -829,7 +830,8 @@
      * null pointer if these is none.
      */
     public Drawable peekDrawable() {
-        Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false, FLAG_SYSTEM, mCmProxy);
+        final ColorManagementProxy cmProxy = getColorManagementProxy();
+        Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false, FLAG_SYSTEM, cmProxy);
         if (bm != null) {
             Drawable dr = new BitmapDrawable(mContext.getResources(), bm);
             dr.setDither(false);
@@ -853,7 +855,8 @@
      */
     @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE)
     public Drawable getFastDrawable() {
-        Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM, mCmProxy);
+        final ColorManagementProxy cmProxy = getColorManagementProxy();
+        Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM, cmProxy);
         if (bm != null) {
             return new FastBitmapDrawable(bm);
         }
@@ -869,7 +872,8 @@
      */
     @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE)
     public Drawable peekFastDrawable() {
-        Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false, FLAG_SYSTEM, mCmProxy);
+        final ColorManagementProxy cmProxy = getColorManagementProxy();
+        Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false, FLAG_SYSTEM, cmProxy);
         if (bm != null) {
             return new FastBitmapDrawable(bm);
         }
@@ -892,10 +896,11 @@
         if (!shouldEnableWideColorGamut()) {
             return false;
         }
-        Bitmap bitmap = sGlobals.peekWallpaperBitmap(mContext, false, which, mCmProxy);
+        final ColorManagementProxy cmProxy = getColorManagementProxy();
+        Bitmap bitmap = sGlobals.peekWallpaperBitmap(mContext, false, which, cmProxy);
         return bitmap != null && bitmap.getColorSpace() != null
                 && bitmap.getColorSpace() != ColorSpace.get(ColorSpace.Named.SRGB)
-                && mCmProxy.isSupportedColorSpace(bitmap.getColorSpace());
+                && cmProxy.isSupportedColorSpace(bitmap.getColorSpace());
     }
 
     /**
@@ -928,8 +933,8 @@
      * @hide
      */
     public Bitmap getBitmapAsUser(int userId, boolean hardware) {
-        return sGlobals.peekWallpaperBitmap(
-                mContext, true, FLAG_SYSTEM, userId, hardware, mCmProxy);
+        final ColorManagementProxy cmProxy = getColorManagementProxy();
+        return sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM, userId, hardware, cmProxy);
     }
 
     /**
@@ -2074,12 +2079,23 @@
     }
 
     /**
-     * A private class to help Globals#getCurrentWallpaperLocked handle color management.
+     * Get the instance of {@link ColorManagementProxy}.
+     *
+     * @return instance of {@link ColorManagementProxy}.
+     * @hide
      */
-    private static class ColorManagementProxy {
+    public ColorManagementProxy getColorManagementProxy() {
+        return mCmProxy;
+    }
+
+    /**
+     * A hidden class to help {@link Globals#getCurrentWallpaperLocked} handle color management.
+     * @hide
+     */
+    public static class ColorManagementProxy {
         private final Set<ColorSpace> mSupportedColorSpaces = new HashSet<>();
 
-        ColorManagementProxy(Context context) {
+        public ColorManagementProxy(@NonNull Context context) {
             // Get a list of supported wide gamut color spaces.
             Display display = context.getDisplay();
             if (display != null) {
@@ -2087,9 +2103,14 @@
             }
         }
 
+        @NonNull
+        public Set<ColorSpace> getSupportedColorSpaces() {
+            return mSupportedColorSpaces;
+        }
+
         boolean isSupportedColorSpace(ColorSpace colorSpace) {
             return colorSpace != null && (colorSpace == ColorSpace.get(ColorSpace.Named.SRGB)
-                    || mSupportedColorSpaces.contains(colorSpace));
+                    || getSupportedColorSpaces().contains(colorSpace));
         }
 
         void doColorManagement(ImageDecoder decoder, ImageDecoder.ImageInfo info) {
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
index 657a308..11e215d 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
@@ -153,11 +153,11 @@
         return true;
     }
 
-    private boolean checkExtensionCapability(String extName) {
+    boolean checkExtensionCapability(String extName) {
         return mExts.contains(extName);
     }
 
-    private int getWcgCapability() {
+    int getWcgCapability() {
         if (checkExtensionCapability(EXT_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH)) {
             return EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT;
         }
@@ -212,7 +212,7 @@
             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);
+            mEglSurface = askCreatingEglWindowSurface(surfaceHolder, attrs, 0 /* offset */);
         } else {
             Log.w(TAG, "Create EglSurface failed: hasEglDisplay=" + hasEglDisplay()
                     + ", has valid surface=" + surfaceHolder.getSurface().isValid());
@@ -235,6 +235,10 @@
         return true;
     }
 
+    EGLSurface askCreatingEglWindowSurface(SurfaceHolder holder, int[] attrs, int offset) {
+        return eglCreateWindowSurface(mEglDisplay, mEglConfig, holder, attrs, offset);
+    }
+
     /**
      * Destroy EglSurface.
      */
@@ -242,7 +246,7 @@
         if (hasEglSurface()) {
             eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
             eglDestroySurface(mEglDisplay, mEglSurface);
-            mEglSurface = null;
+            mEglSurface = EGL_NO_SURFACE;
         }
     }
 
@@ -296,7 +300,7 @@
     public void destroyEglContext() {
         if (hasEglContext()) {
             eglDestroyContext(mEglDisplay, mEglContext);
-            mEglContext = null;
+            mEglContext = EGL_NO_CONTEXT;
         }
     }
 
@@ -340,11 +344,16 @@
             destroyEglContext();
         }
         if (hasEglDisplay()) {
-            eglTerminate(mEglDisplay);
+            terminateEglDisplay();
         }
         mEglReady = false;
     }
 
+    void terminateEglDisplay() {
+        eglTerminate(mEglDisplay);
+        mEglDisplay = EGL_NO_DISPLAY;
+    }
+
     /**
      * Called to dump current state.
      * @param prefix prefix.
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 a5722e2..4b47093 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java
@@ -16,52 +16,116 @@
 
 package com.android.systemui.glwallpaper;
 
-import static org.junit.Assert.*;
-import static org.mockito.Mockito.RETURNS_DEFAULTS;
-import static org.mockito.Mockito.mock;
+import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atMost;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.graphics.PixelFormat;
 import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
+import android.view.Surface;
+import android.view.SurfaceControl;
 import android.view.SurfaceHolder;
+import android.view.SurfaceSession;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
-
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
 public class EglHelperTest extends SysuiTestCase {
 
-    @Mock
+    @Spy
     private EglHelper mEglHelper;
+
     @Mock
     private SurfaceHolder mSurfaceHolder;
 
     @Before
     public void setUp() throws Exception {
-        mEglHelper = mock(EglHelper.class, RETURNS_DEFAULTS);
-        mSurfaceHolder = mock(SurfaceHolder.class, RETURNS_DEFAULTS);
+        MockitoAnnotations.initMocks(this);
+        prepareSurface();
+    }
+
+    @After
+    public void tearDown() {
+        mSurfaceHolder.getSurface().destroy();
+        mSurfaceHolder = null;
+    }
+
+    private void prepareSurface() {
+        final SurfaceSession session = new SurfaceSession();
+        final SurfaceControl control = new SurfaceControl.Builder(session)
+                .setName("Test")
+                .setBufferSize(100, 100)
+                .setFormat(PixelFormat.RGB_888)
+                .build();
+        final Surface surface = new Surface();
+        surface.copyFrom(control);
+        when(mSurfaceHolder.getSurface()).thenReturn(surface);
+        assertThat(mSurfaceHolder.getSurface()).isNotNull();
+        assertThat(mSurfaceHolder.getSurface().isValid()).isTrue();
     }
 
     @Test
     public void testInit_finish() {
         mEglHelper.init(mSurfaceHolder, false /* wideColorGamut */);
+        assertThat(mEglHelper.hasEglDisplay()).isTrue();
+        assertThat(mEglHelper.hasEglContext()).isTrue();
+        assertThat(mEglHelper.hasEglSurface()).isTrue();
+        verify(mEglHelper).askCreatingEglWindowSurface(
+                any(SurfaceHolder.class), eq(null), anyInt());
+
+        mEglHelper.finish();
+        assertThat(mEglHelper.hasEglSurface()).isFalse();
+        assertThat(mEglHelper.hasEglContext()).isFalse();
+        assertThat(mEglHelper.hasEglDisplay()).isFalse();
+    }
+
+    @Test
+    public void testInit_finish_wide_gamut() {
+        // In EglHelper, EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT = 0x3490;
+        doReturn(0x3490).when(mEglHelper).getWcgCapability();
+        // In EglHelper, KHR_GL_COLOR_SPACE = "EGL_KHR_gl_colorspace";
+        doReturn(true).when(mEglHelper).checkExtensionCapability("EGL_KHR_gl_colorspace");
+        ArgumentCaptor<int[]> ac = ArgumentCaptor.forClass(int[].class);
+        // {EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT, EGL_NONE}
+        final int[] expectedArgument = new int[] {0x309D, 0x3490, 0x3038};
+
+        mEglHelper.init(mSurfaceHolder, true /* wideColorGamut */);
+        verify(mEglHelper)
+                .askCreatingEglWindowSurface(any(SurfaceHolder.class), ac.capture(), anyInt());
+        assertThat(ac.getValue()).isNotNull();
+        assertThat(ac.getValue()).isEqualTo(expectedArgument);
         mEglHelper.finish();
     }
 
     @Test
     public void testFinish_shouldNotCrash() {
-        assertFalse(mEglHelper.hasEglDisplay());
-        assertFalse(mEglHelper.hasEglSurface());
-        assertFalse(mEglHelper.hasEglContext());
+        mEglHelper.terminateEglDisplay();
+        assertThat(mEglHelper.hasEglDisplay()).isFalse();
+        assertThat(mEglHelper.hasEglSurface()).isFalse();
+        assertThat(mEglHelper.hasEglContext()).isFalse();
 
         mEglHelper.finish();
+        verify(mEglHelper, never()).destroyEglContext();
+        verify(mEglHelper, never()).destroyEglSurface();
+        verify(mEglHelper, atMost(1)).terminateEglDisplay();
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageWallpaperRendererTest.java b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageWallpaperRendererTest.java
new file mode 100644
index 0000000..d881fd5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageWallpaperRendererTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.glwallpaper;
+
+import static com.android.systemui.glwallpaper.GLWallpaperRenderer.SurfaceProxy;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.app.WallpaperManager;
+import android.app.WallpaperManager.ColorManagementProxy;
+import android.graphics.Bitmap;
+import android.graphics.ColorSpace;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class ImageWallpaperRendererTest extends SysuiTestCase {
+
+    private WallpaperManager mWpmSpy;
+    private SurfaceProxy mSurfaceProxy;
+
+    @Before
+    public void setUp() throws Exception {
+        final WallpaperManager wpm = mContext.getSystemService(WallpaperManager.class);
+        mWpmSpy = spy(wpm);
+        mContext.addMockSystemService(WallpaperManager.class, mWpmSpy);
+
+        mSurfaceProxy = new SurfaceProxy() {
+            @Override
+            public void requestRender() {
+                // NO-op
+            }
+
+            @Override
+            public void preRender() {
+                // No-op
+            }
+
+            @Override
+            public void postRender() {
+                // No-op
+            }
+        };
+    }
+
+    @Test
+    public void testWcgContent() throws IOException {
+        final Bitmap srgbBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
+        final Bitmap p3Bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888,
+                false /* hasAlpha */, ColorSpace.get(ColorSpace.Named.DISPLAY_P3));
+
+        final ColorManagementProxy proxy = new ColorManagementProxy(mContext);
+        final ColorManagementProxy cmProxySpy = spy(proxy);
+        final Set<ColorSpace> supportedWideGamuts = new HashSet<>();
+        supportedWideGamuts.add(ColorSpace.get(ColorSpace.Named.DISPLAY_P3));
+
+        try {
+            doReturn(true).when(mWpmSpy).shouldEnableWideColorGamut();
+            doReturn(cmProxySpy).when(mWpmSpy).getColorManagementProxy();
+            doReturn(supportedWideGamuts).when(cmProxySpy).getSupportedColorSpaces();
+
+            mWpmSpy.setBitmap(p3Bitmap);
+            ImageWallpaperRenderer rendererP3 = new ImageWallpaperRenderer(mContext, mSurfaceProxy);
+            assertThat(rendererP3.isWcgContent()).isTrue();
+
+            mWpmSpy.setBitmap(srgbBitmap);
+            ImageWallpaperRenderer renderer = new ImageWallpaperRenderer(mContext, mSurfaceProxy);
+            assertThat(renderer.isWcgContent()).isFalse();
+        } finally {
+            srgbBitmap.recycle();
+            p3Bitmap.recycle();
+        }
+    }
+
+}