Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d8609336 authored by Ahan Wu's avatar Ahan Wu
Browse files

Unit tests for wide color gamut of ImageWallpaper

We have merged a cl for wide color gamut support of ImageWallpaper.
This cl is the unit tests for this feature.

Bug: 142845271
Test: atest com.android.systemui.glwallpaper.ImageWallpaperRendererTest
 --rerun-until-failure 20
Test: atest com.android.systemui.glwallpaper.EglHelperTest
 --rerun-until-failure 20
Change-Id: Ib22ee8c074b1f8d9e37e990589ca833c3d93892c
parent ef3abba3
Loading
Loading
Loading
Loading
+33 −12
Original line number Diff line number Diff line
@@ -598,7 +598,8 @@ public class WallpaperManager {
     *     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 @@ public class WallpaperManager {
     * 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 @@ public class WallpaperManager {
     */
    @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 @@ public class WallpaperManager {
     */
    @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 @@ public class WallpaperManager {
        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 @@ public class WallpaperManager {
     * @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 @@ public class WallpaperManager {
    }

    /**
     * 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 @@ public class WallpaperManager {
            }
        }

        @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) {
+15 −6
Original line number Diff line number Diff line
@@ -153,11 +153,11 @@ public class EglHelper {
        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 @@ public class EglHelper {
            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 @@ public class EglHelper {
        return true;
    }

    EGLSurface askCreatingEglWindowSurface(SurfaceHolder holder, int[] attrs, int offset) {
        return eglCreateWindowSurface(mEglDisplay, mEglConfig, holder, attrs, offset);
    }

    /**
     * Destroy EglSurface.
     */
@@ -242,7 +246,7 @@ public class EglHelper {
        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 class EglHelper {
    public void destroyEglContext() {
        if (hasEglContext()) {
            eglDestroyContext(mEglDisplay, mEglContext);
            mEglContext = null;
            mEglContext = EGL_NO_CONTEXT;
        }
    }

@@ -340,11 +344,16 @@ public class EglHelper {
            destroyEglContext();
        }
        if (hasEglDisplay()) {
            eglTerminate(mEglDisplay);
            terminateEglDisplay();
        }
        mEglReady = false;
    }

    void terminateEglDisplay() {
        eglTerminate(mEglDisplay);
        mEglDisplay = EGL_NO_DISPLAY;
    }

    /**
     * Called to dump current state.
     * @param prefix prefix.
+76 −12
Original line number Diff line number Diff line
@@ -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();
    }
}
+106 −0
Original line number Diff line number Diff line
/*
 * 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();
        }
    }

}