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

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

Change-Id: I142561c76775ee52f2ffe898b97bbc495db8790b
This commit is contained in:
Mariia Sandrikova
2021-07-30 15:03:03 +00:00
committed by Automerger Merge Worker
4 changed files with 230 additions and 12 deletions

View File

@@ -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

View File

@@ -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?

View File

@@ -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,20 +429,25 @@ class WallpaperController {
Bundle sendWindowWallpaperCommand(
WindowState window, String action, int x, int y, int z, Bundle extras, boolean sync) {
if (window == mWallpaperTarget || window == mPrevWallpaperTarget) {
boolean doWait = sync;
for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
token.sendWindowWallpaperCommand(action, x, y, z, extras, sync);
}
if (doWait) {
// TODO: Need to wait for result.
}
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);
token.sendWindowWallpaperCommand(action, x, y, z, extras, sync);
}
if (doWait) {
// TODO: Need to wait for result.
}
}
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;
}
}
}

View File

@@ -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();
}
/**