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

Commit f12299be authored by Mariia Sandrikova's avatar Mariia Sandrikova Committed by Automerger Merge Worker
Browse files

Merge "Freeze live wallpaper for letterbox background." into sc-v2-dev am: 809e26fc

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15360874

Change-Id: I142561c76775ee52f2ffe898b97bbc495db8790b
parents 71cb3afb 809e26fc
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -220,6 +220,20 @@ public class WallpaperManager {
     */
    public static final String COMMAND_REAPPLY = "android.wallpaper.reapply";

    /**
     * Command for {@link #sendWallpaperCommand}: reported when the live wallpaper needs to be
     * frozen.
     * @hide
     */
    public static final String COMMAND_FREEZE = "android.wallpaper.freeze";

    /**
     * Command for {@link #sendWallpaperCommand}: reported when the live wallapper doesn't need
     * to be frozen anymore.
     * @hide
     */
    public static final String COMMAND_UNFREEZE = "android.wallpaper.unfreeze";

    /**
     * Extra passed back from setWallpaper() giving the new wallpaper's assigned ID.
     * @hide
+179 −1
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package android.service.wallpaper;

import static android.app.WallpaperManager.COMMAND_FREEZE;
import static android.app.WallpaperManager.COMMAND_UNFREEZE;
import static android.graphics.Matrix.MSCALE_X;
import static android.graphics.Matrix.MSCALE_Y;
import static android.graphics.Matrix.MSKEW_X;
@@ -42,12 +44,14 @@ import android.content.res.TypedArray;
import android.graphics.BLASTBufferQueue;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.GraphicBuffer;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.hardware.HardwareBuffer;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.os.Build;
@@ -56,6 +60,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
@@ -200,6 +205,12 @@ public abstract class WallpaperService extends Service {
        boolean mVisible;
        boolean mReportedVisible;
        boolean mDestroyed;
        // Set to true after receiving WallpaperManager#COMMAND_FREEZE. It's reset back to false
        // after receiving WallpaperManager#COMMAND_UNFREEZE. COMMAND_FREEZE is fully applied once
        // mScreenshotSurfaceControl isn't null. When this happens, then Engine is notified through
        // doVisibilityChanged that main wallpaper surface is no longer visible and the wallpaper
        // host receives onVisibilityChanged(false) callback.
        private boolean mFrozenRequested = false;

        // Current window state.
        boolean mCreated;
@@ -264,6 +275,8 @@ public abstract class WallpaperService extends Service {
        SurfaceControl mSurfaceControl = new SurfaceControl();
        SurfaceControl mBbqSurfaceControl;
        BLASTBufferQueue mBlastBufferQueue;
        private SurfaceControl mScreenshotSurfaceControl;
        private Point mScreenshotSize = new Point();

        final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
            {
@@ -1349,11 +1362,15 @@ public abstract class WallpaperService extends Service {
            if (!mDestroyed) {
                mVisible = visible;
                reportVisibility();
                if (visible) processLocalColors(mPendingXOffset, mPendingXOffsetStep);
                if (mReportedVisible) processLocalColors(mPendingXOffset, mPendingXOffsetStep);
            }
        }

        void reportVisibility() {
            if (mScreenshotSurfaceControl != null && mVisible) {
                if (DEBUG) Log.v(TAG, "Frozen so don't report visibility change");
                return;
            }
            if (!mDestroyed) {
                mDisplayState = mDisplay == null ? Display.STATE_UNKNOWN : mDisplay.getState();
                boolean visible = mVisible && mDisplayState != Display.STATE_OFF;
@@ -1370,6 +1387,10 @@ public abstract class WallpaperService extends Service {
                        updateSurface(true, false, false);
                    }
                    onVisibilityChanged(visible);
                    if (mReportedVisible && mFrozenRequested) {
                        if (DEBUG) Log.v(TAG, "Freezing wallpaper after visibility update");
                        freeze();
                    }
                }
            }
        }
@@ -1830,6 +1851,9 @@ public abstract class WallpaperService extends Service {
        void doCommand(WallpaperCommand cmd) {
            Bundle result;
            if (!mDestroyed) {
                if (COMMAND_FREEZE.equals(cmd.action) || COMMAND_UNFREEZE.equals(cmd.action)) {
                    updateFrozenState(/* frozenRequested= */ !COMMAND_UNFREEZE.equals(cmd.action));
                }
                result = onCommand(cmd.action, cmd.x, cmd.y, cmd.z,
                        cmd.extras, cmd.sync);
            } else {
@@ -1844,6 +1868,159 @@ public abstract class WallpaperService extends Service {
            }
        }

        private void updateFrozenState(boolean frozenRequested) {
            if (mIWallpaperEngine.mWallpaperManager.getWallpaperInfo() == null
                    // Procees the unfreeze command in case the wallaper became static while
                    // being paused.
                    && frozenRequested) {
                if (DEBUG) Log.v(TAG, "Ignoring the freeze command for static wallpapers");
                return;
            }
            mFrozenRequested = frozenRequested;
            boolean isFrozen = mScreenshotSurfaceControl != null;
            if (mFrozenRequested == isFrozen) {
                return;
            }
            if (mFrozenRequested) {
                freeze();
            } else {
                unfreeze();
            }
        }

        private void freeze() {
            if (!mReportedVisible || mDestroyed) {
                // Screenshot can't be taken until visibility is reported to the wallpaper host.
                return;
            }
            if (!showScreenshotOfWallpaper()) {
                return;
            }
            // Prevent a wallpaper host from rendering wallpaper behind a screeshot.
            doVisibilityChanged(false);
            // Remember that visibility is requested since it's not guaranteed that
            // mWindow#dispatchAppVisibility will be called when letterboxed application with
            // wallpaper background transitions to the Home screen.
            mVisible = true;
        }

        private void unfreeze() {
            cleanUpScreenshotSurfaceControl();
            if (mVisible) {
                doVisibilityChanged(true);
            }
        }

        private void cleanUpScreenshotSurfaceControl() {
            // TODO(b/194399558): Add crossfade transition.
            if (mScreenshotSurfaceControl != null) {
                new SurfaceControl.Transaction()
                        .remove(mScreenshotSurfaceControl)
                        .show(mBbqSurfaceControl)
                        .apply();
                mScreenshotSurfaceControl = null;
            }
        }

        void scaleAndCropScreenshot() {
            if (mScreenshotSurfaceControl == null) {
                return;
            }
            if (mScreenshotSize.x <= 0 || mScreenshotSize.y <= 0) {
                Log.w(TAG, "Unexpected screenshot size: " + mScreenshotSize);
                return;
            }
            // Don't scale down and using the same scaling factor for both dimensions to
            // avoid stretching wallpaper image.
            float scaleFactor = Math.max(1, Math.max(
                    ((float) mSurfaceSize.x) / mScreenshotSize.x,
                    ((float) mSurfaceSize.y) / mScreenshotSize.y));
            int diffX =  ((int) (mScreenshotSize.x * scaleFactor)) - mSurfaceSize.x;
            int diffY =  ((int) (mScreenshotSize.y * scaleFactor)) - mSurfaceSize.y;
            if (DEBUG) {
                Log.v(TAG, "Adjusting screenshot: scaleFactor=" + scaleFactor
                        + " diffX=" + diffX + " diffY=" + diffY + " mSurfaceSize=" + mSurfaceSize
                        + " mScreenshotSize=" + mScreenshotSize);
            }
            new SurfaceControl.Transaction()
                        .setMatrix(
                                mScreenshotSurfaceControl,
                                /* dsdx= */ scaleFactor, /* dtdx= */ 0,
                                /* dtdy= */ 0, /* dsdy= */ scaleFactor)
                        .setWindowCrop(
                                mScreenshotSurfaceControl,
                                new Rect(
                                        /* left= */ diffX / 2,
                                        /* top= */ diffY / 2,
                                        /* right= */ diffX / 2 + mScreenshotSize.x,
                                        /* bottom= */ diffY / 2 + mScreenshotSize.y))
                        .setPosition(mScreenshotSurfaceControl, -diffX / 2, -diffY / 2)
                        .apply();
        }

        private boolean showScreenshotOfWallpaper() {
            if (mDestroyed || mSurfaceControl == null || !mSurfaceControl.isValid()) {
                if (DEBUG) Log.v(TAG, "Failed to screenshot wallpaper: surface isn't valid");
                return false;
            }

            final Rect bounds = new Rect(0, 0, mSurfaceSize.x,  mSurfaceSize.y);
            if (bounds.isEmpty()) {
                Log.w(TAG, "Failed to screenshot wallpaper: surface bounds are empty");
                return false;
            }

            if (mScreenshotSurfaceControl != null) {
                Log.e(TAG, "Screenshot is unexpectedly not null");
                // Destroying previous screenshot since it can have different size.
                cleanUpScreenshotSurfaceControl();
            }

            SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
                    SurfaceControl.captureLayers(
                            new SurfaceControl.LayerCaptureArgs.Builder(mSurfaceControl)
                                    // Needed because SurfaceFlinger#validateScreenshotPermissions
                                    // uses this parameter to check whether a caller only attempts
                                    // to screenshot itself when call doesn't come from the system.
                                    .setUid(Process.myUid())
                                    .setChildrenOnly(false)
                                    .setSourceCrop(bounds)
                                    .build());

            if (screenshotBuffer == null) {
                Log.w(TAG, "Failed to screenshot wallpaper: screenshotBuffer is null");
                return false;
            }

            final HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer();

            SurfaceControl.Transaction t = new SurfaceControl.Transaction();

            // TODO(b/194399558): Add crossfade transition.
            mScreenshotSurfaceControl = new SurfaceControl.Builder()
                    .setName("Wallpaper snapshot for engine " + this)
                    .setFormat(hardwareBuffer.getFormat())
                    .setParent(mSurfaceControl)
                    .setSecure(screenshotBuffer.containsSecureLayers())
                    .setCallsite("WallpaperService.Engine.showScreenshotOfWallpaper")
                    .setBLASTLayer()
                    .build();

            mScreenshotSize.set(mSurfaceSize.x, mSurfaceSize.y);

            GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(hardwareBuffer);

            t.setBuffer(mScreenshotSurfaceControl, graphicBuffer);
            t.setColorSpace(mScreenshotSurfaceControl, screenshotBuffer.getColorSpace());
            // Place on top everything else.
            t.setLayer(mScreenshotSurfaceControl, Integer.MAX_VALUE);
            t.show(mScreenshotSurfaceControl);
            t.hide(mBbqSurfaceControl);
            t.apply();

            return true;
        }

        void reportSurfaceDestroyed() {
            if (mSurfaceCreated) {
                mSurfaceCreated = false;
@@ -2166,6 +2343,7 @@ public abstract class WallpaperService extends Service {
                    final boolean reportDraw = message.arg1 != 0;
                    mEngine.updateSurface(true, false, reportDraw);
                    mEngine.doOffsetsChanged(true);
                    mEngine.scaleAndCropScreenshot();
                } break;
                case MSG_WINDOW_MOVED: {
                    // Do nothing. What does it mean for a Wallpaper to move?
+32 −9
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.server.wm;

import static android.app.WallpaperManager.COMMAND_FREEZE;
import static android.app.WallpaperManager.COMMAND_UNFREEZE;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
@@ -79,6 +81,8 @@ class WallpaperController {
    private int mLastWallpaperDisplayOffsetX = Integer.MIN_VALUE;
    private int mLastWallpaperDisplayOffsetY = Integer.MIN_VALUE;
    private final float mMaxWallpaperScale;
    // Whether COMMAND_FREEZE was dispatched.
    private boolean mLastFrozen = false;

    // This is set when we are waiting for a wallpaper to tell us it is done
    // changing its scroll position.
@@ -194,6 +198,7 @@ class WallpaperController {
                if (DEBUG_WALLPAPER) Slog.v(TAG,
                        "Win " + w + ": token animating, looking behind.");
            }
            mFindResults.setIsWallpaperTargetForLetterbox(w.hasWallpaperForLetterboxBackground());
            // Found a target! End search.
            return true;
        }
@@ -424,6 +429,14 @@ class WallpaperController {
    Bundle sendWindowWallpaperCommand(
            WindowState window, String action, int x, int y, int z, Bundle extras, boolean sync) {
        if (window == mWallpaperTarget || window == mPrevWallpaperTarget) {
            sendWindowWallpaperCommand(action, x, y, z, extras, sync);
        }

        return null;
    }

    private void sendWindowWallpaperCommand(
                String action, int x, int y, int z, Bundle extras, boolean sync) {
        boolean doWait = sync;
        for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
            final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
@@ -435,9 +448,6 @@ class WallpaperController {
        }
    }

        return null;
    }

    private void updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync) {
        WindowState target = mWallpaperTarget;
        if (target != null) {
@@ -641,6 +651,13 @@ class WallpaperController {

        updateWallpaperTokens(visible);

        if (visible && mLastFrozen != mFindResults.isWallpaperTargetForLetterbox) {
            mLastFrozen = mFindResults.isWallpaperTargetForLetterbox;
            sendWindowWallpaperCommand(
                    mFindResults.isWallpaperTargetForLetterbox ? COMMAND_FREEZE : COMMAND_UNFREEZE,
                    /* x= */ 0, /* y= */ 0, /* z= */ 0, /* extras= */ null, /* sync= */ false);
        }

        if (DEBUG_WALLPAPER_LIGHT)  Slog.d(TAG, "New wallpaper: target=" + mWallpaperTarget
                + " prev=" + mPrevWallpaperTarget);
    }
@@ -838,6 +855,7 @@ class WallpaperController {
        boolean useTopWallpaperAsTarget = false;
        WindowState wallpaperTarget = null;
        boolean resetTopWallpaper = false;
        boolean isWallpaperTargetForLetterbox = false;

        void setTopWallpaper(WindowState win) {
            topWallpaper = win;
@@ -851,11 +869,16 @@ class WallpaperController {
            useTopWallpaperAsTarget = topWallpaperAsTarget;
        }

        void setIsWallpaperTargetForLetterbox(boolean isWallpaperTargetForLetterbox) {
            this.isWallpaperTargetForLetterbox = isWallpaperTargetForLetterbox;
        }

        void reset() {
            topWallpaper = null;
            wallpaperTarget = null;
            useTopWallpaperAsTarget = false;
            resetTopWallpaper = false;
            isWallpaperTargetForLetterbox = false;
        }
    }
}
+5 −2
Original line number Diff line number Diff line
@@ -6043,8 +6043,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
    }

    boolean hasWallpaper() {
        return (mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0
                || (mActivityRecord != null && mActivityRecord.hasWallpaperBackgroudForLetterbox());
        return (mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0 || hasWallpaperForLetterboxBackground();
    }

    boolean hasWallpaperForLetterboxBackground() {
        return mActivityRecord != null && mActivityRecord.hasWallpaperBackgroudForLetterbox();
    }

    /**