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

Commit 287d8283 authored by Ahan Wu's avatar Ahan Wu
Browse files

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
parent 355062a8
Loading
Loading
Loading
Loading
+117 −25
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import android.content.Context;
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.BitmapFactory;
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.os.SystemProperties;
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.io.InputStream;
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 class WallpaperManager {
     */
    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 class WallpaperManager {
        }

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

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

            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;
                        }
                        return BitmapFactory.decodeFileDescriptor(
                                fd.getFileDescriptor(), null, options);
                    } catch (OutOfMemoryError e) {

                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);
                        }
                        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 @@ public class WallpaperManager {
        }
    }

    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 @@ public class WallpaperManager {
        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);
    }

    /**
@@ -530,6 +557,22 @@ public class WallpaperManager {
        return sGlobals.mService;
    }

    /**
     * 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.
@@ -546,7 +589,7 @@ public class WallpaperManager {
     *     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 @@ public class WallpaperManager {
     * 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 @@ public class WallpaperManager {
     */
    @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,13 +860,34 @@ public class WallpaperManager {
     */
    @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);
        }
        return null;
    }

    /**
     * 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}.
     *
@@ -852,7 +916,8 @@ public class WallpaperManager {
     * @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 @@ public class WallpaperManager {
        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;
+50 −0
Original line number Diff line number Diff line
@@ -43,7 +43,9 @@ import android.util.Log;

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 @@ public final class Display {
    /** @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.
@@ -959,6 +978,37 @@ public final class Display {
        }
    }

    /**
     * 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
+4 −0
Original line number Diff line number Diff line
@@ -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>

+3 −0
Original line number Diff line number Diff line
@@ -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" />
+9 −5
Original line number Diff line number Diff line
@@ -108,12 +108,13 @@ public class ImageWallpaper extends WallpaperService {
            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 @@ public class ImageWallpaper extends WallpaperService {
                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 @@ public class ImageWallpaper extends WallpaperService {

            // 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 @@ public class ImageWallpaper extends WallpaperService {
                    && 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);
Loading