Merge "Fix wallpaper screenshot" into pi-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
76439d832f
@@ -137,7 +137,6 @@ import android.os.SystemClock;
|
||||
import android.os.Trace;
|
||||
import android.util.ArraySet;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.MutableBoolean;
|
||||
import android.util.Slog;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
import android.view.Display;
|
||||
@@ -156,7 +155,6 @@ import com.android.internal.view.IInputMethodClient;
|
||||
import com.android.server.policy.WindowManagerPolicy;
|
||||
import com.android.server.wm.utils.RotationCache;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
@@ -2960,83 +2958,55 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
|
||||
* In portrait mode, it grabs the full screenshot.
|
||||
*
|
||||
* @param config of the output bitmap
|
||||
* @param wallpaperOnly true if only the wallpaper layer should be included in the screenshot
|
||||
*/
|
||||
Bitmap screenshotDisplay(Bitmap.Config config, boolean wallpaperOnly) {
|
||||
synchronized (mService.mWindowMap) {
|
||||
if (!mService.mPolicy.isScreenOn()) {
|
||||
if (DEBUG_SCREENSHOT) {
|
||||
Slog.i(TAG_WM, "Attempted to take screenshot while display was off.");
|
||||
}
|
||||
return null;
|
||||
Bitmap screenshotDisplayLocked(Bitmap.Config config) {
|
||||
if (!mService.mPolicy.isScreenOn()) {
|
||||
if (DEBUG_SCREENSHOT) {
|
||||
Slog.i(TAG_WM, "Attempted to take screenshot while display was off.");
|
||||
}
|
||||
|
||||
if (wallpaperOnly && !shouldScreenshotWallpaper()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int dw = mDisplayInfo.logicalWidth;
|
||||
int dh = mDisplayInfo.logicalHeight;
|
||||
|
||||
if (dw <= 0 || dh <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Rect frame = new Rect(0, 0, dw, dh);
|
||||
|
||||
// The screenshot API does not apply the current screen rotation.
|
||||
int rot = mDisplay.getRotation();
|
||||
|
||||
if (rot == ROTATION_90 || rot == ROTATION_270) {
|
||||
rot = (rot == ROTATION_90) ? ROTATION_270 : ROTATION_90;
|
||||
}
|
||||
|
||||
// SurfaceFlinger is not aware of orientation, so convert our logical
|
||||
// crop to SurfaceFlinger's portrait orientation.
|
||||
convertCropForSurfaceFlinger(frame, rot, dw, dh);
|
||||
|
||||
final ScreenRotationAnimation screenRotationAnimation =
|
||||
mService.mAnimator.getScreenRotationAnimationLocked(DEFAULT_DISPLAY);
|
||||
final boolean inRotation = screenRotationAnimation != null &&
|
||||
screenRotationAnimation.isAnimating();
|
||||
if (DEBUG_SCREENSHOT && inRotation) Slog.v(TAG_WM, "Taking screenshot while rotating");
|
||||
|
||||
// TODO(b/68392460): We should screenshot Task controls directly
|
||||
// but it's difficult at the moment as the Task doesn't have the
|
||||
// correct size set.
|
||||
final Bitmap bitmap = SurfaceControl.screenshot(frame, dw, dh, 0, 1, inRotation, rot);
|
||||
if (bitmap == null) {
|
||||
Slog.w(TAG_WM, "Failed to take screenshot");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create a copy of the screenshot that is immutable and backed in ashmem.
|
||||
// This greatly reduces the overhead of passing the bitmap between processes.
|
||||
final Bitmap ret = bitmap.createAshmemBitmap(config);
|
||||
bitmap.recycle();
|
||||
return ret;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldScreenshotWallpaper() {
|
||||
MutableBoolean screenshotReady = new MutableBoolean(false);
|
||||
int dw = mDisplayInfo.logicalWidth;
|
||||
int dh = mDisplayInfo.logicalHeight;
|
||||
|
||||
forAllWindows(w -> {
|
||||
if (!w.mIsWallpaper) {
|
||||
return false;
|
||||
}
|
||||
if (dw <= 0 || dh <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Found the wallpaper window
|
||||
final WindowStateAnimator winAnim = w.mWinAnimator;
|
||||
final Rect frame = new Rect(0, 0, dw, dh);
|
||||
|
||||
if (winAnim.getShown() && winAnim.mLastAlpha > 0f) {
|
||||
screenshotReady.value = true;
|
||||
}
|
||||
// The screenshot API does not apply the current screen rotation.
|
||||
int rot = mDisplay.getRotation();
|
||||
|
||||
return true;
|
||||
}, true /* traverseTopToBottom */);
|
||||
if (rot == ROTATION_90 || rot == ROTATION_270) {
|
||||
rot = (rot == ROTATION_90) ? ROTATION_270 : ROTATION_90;
|
||||
}
|
||||
|
||||
return screenshotReady.value;
|
||||
// SurfaceFlinger is not aware of orientation, so convert our logical
|
||||
// crop to SurfaceFlinger's portrait orientation.
|
||||
convertCropForSurfaceFlinger(frame, rot, dw, dh);
|
||||
|
||||
final ScreenRotationAnimation screenRotationAnimation =
|
||||
mService.mAnimator.getScreenRotationAnimationLocked(DEFAULT_DISPLAY);
|
||||
final boolean inRotation = screenRotationAnimation != null &&
|
||||
screenRotationAnimation.isAnimating();
|
||||
if (DEBUG_SCREENSHOT && inRotation) Slog.v(TAG_WM, "Taking screenshot while rotating");
|
||||
|
||||
// TODO(b/68392460): We should screenshot Task controls directly
|
||||
// but it's difficult at the moment as the Task doesn't have the
|
||||
// correct size set.
|
||||
final Bitmap bitmap = SurfaceControl.screenshot(frame, dw, dh, 0, 1, inRotation, rot);
|
||||
if (bitmap == null) {
|
||||
Slog.w(TAG_WM, "Failed to take screenshot");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create a copy of the screenshot that is immutable and backed in ashmem.
|
||||
// This greatly reduces the overhead of passing the bitmap between processes.
|
||||
final Bitmap ret = bitmap.createAshmemBitmap(config);
|
||||
bitmap.recycle();
|
||||
return ret;
|
||||
}
|
||||
|
||||
// TODO: Can this use createRotationMatrix()?
|
||||
|
||||
@@ -27,12 +27,16 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
|
||||
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
|
||||
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
|
||||
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
|
||||
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
|
||||
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER;
|
||||
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
|
||||
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
|
||||
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
|
||||
import static com.android.server.wm.WindowManagerService.H.WALLPAPER_DRAW_PENDING_TIMEOUT;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.GraphicBuffer;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.os.Debug;
|
||||
import android.os.IBinder;
|
||||
@@ -41,6 +45,7 @@ import android.os.SystemClock;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Slog;
|
||||
import android.view.DisplayInfo;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.WindowManager;
|
||||
import android.view.animation.Animation;
|
||||
|
||||
@@ -95,6 +100,12 @@ class WallpaperController {
|
||||
private static final int WALLPAPER_DRAW_TIMEOUT = 2;
|
||||
private int mWallpaperDrawState = WALLPAPER_DRAW_NORMAL;
|
||||
|
||||
/**
|
||||
* Temporary storage for taking a screenshot of the wallpaper.
|
||||
* @see #screenshotWallpaperLocked()
|
||||
*/
|
||||
private WindowState mTmpTopWallpaper;
|
||||
|
||||
private final FindWallpaperTargetResult mFindResults = new FindWallpaperTargetResult();
|
||||
|
||||
private final ToBooleanFunction<WindowState> mFindWallpaperTargetFunction = w -> {
|
||||
@@ -679,6 +690,58 @@ class WallpaperController {
|
||||
mWallpaperTokens.remove(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a screenshot of the wallpaper if it's visible.
|
||||
*
|
||||
* @return Bitmap of the wallpaper
|
||||
*/
|
||||
Bitmap screenshotWallpaperLocked() {
|
||||
if (!mService.mPolicy.isScreenOn()) {
|
||||
if (DEBUG_SCREENSHOT) {
|
||||
Slog.i(TAG_WM, "Attempted to take screenshot while display was off.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
final WindowState wallpaperWindowState = getTopVisibleWallpaper();
|
||||
if (wallpaperWindowState == null) {
|
||||
if (DEBUG_SCREENSHOT) {
|
||||
Slog.i(TAG_WM, "No visible wallpaper to screenshot");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
final Rect bounds = wallpaperWindowState.getBounds();
|
||||
bounds.offsetTo(0, 0);
|
||||
|
||||
GraphicBuffer wallpaperBuffer = SurfaceControl.captureLayers(
|
||||
wallpaperWindowState.getSurfaceControl().getHandle(), bounds, 1 /* frameScale */);
|
||||
|
||||
if (wallpaperBuffer == null) {
|
||||
Slog.w(TAG_WM, "Failed to screenshot wallpaper");
|
||||
return null;
|
||||
}
|
||||
return Bitmap.createHardwareBitmap(wallpaperBuffer);
|
||||
}
|
||||
|
||||
private WindowState getTopVisibleWallpaper() {
|
||||
mTmpTopWallpaper = null;
|
||||
|
||||
for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
|
||||
final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
|
||||
token.forAllWindows(w -> {
|
||||
final WindowStateAnimator winAnim = w.mWinAnimator;
|
||||
if (winAnim != null && winAnim.getShown() && winAnim.mLastAlpha > 0f) {
|
||||
mTmpTopWallpaper = w;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}, true /* traverseTopToBottom */);
|
||||
}
|
||||
|
||||
return mTmpTopWallpaper;
|
||||
}
|
||||
|
||||
void dump(PrintWriter pw, String prefix) {
|
||||
pw.print(prefix); pw.print("mWallpaperTarget="); pw.println(mWallpaperTarget);
|
||||
if (mPrevWallpaperTarget != null) {
|
||||
|
||||
@@ -28,8 +28,6 @@ import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER
|
||||
import static android.content.Intent.ACTION_USER_REMOVED;
|
||||
import static android.content.Intent.EXTRA_USER_HANDLE;
|
||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
import static android.os.Process.ROOT_UID;
|
||||
import static android.os.Process.SHELL_UID;
|
||||
import static android.os.Process.SYSTEM_UID;
|
||||
import static android.os.Process.myPid;
|
||||
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
|
||||
@@ -3607,14 +3605,14 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
|
||||
@Override
|
||||
public Bitmap screenshotWallpaper() {
|
||||
if (!checkCallingPermission(READ_FRAME_BUFFER,
|
||||
"screenshotWallpaper()")) {
|
||||
if (!checkCallingPermission(READ_FRAME_BUFFER, "screenshotWallpaper()")) {
|
||||
throw new SecurityException("Requires READ_FRAME_BUFFER permission");
|
||||
}
|
||||
try {
|
||||
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "screenshotWallpaper");
|
||||
return screenshotApplications(DEFAULT_DISPLAY, Bitmap.Config.ARGB_8888,
|
||||
true /* wallpaperOnly */);
|
||||
synchronized (mWindowMap) {
|
||||
return mRoot.mWallpaperController.screenshotWallpaperLocked();
|
||||
}
|
||||
} finally {
|
||||
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
|
||||
}
|
||||
@@ -3627,14 +3625,25 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
*/
|
||||
@Override
|
||||
public boolean requestAssistScreenshot(final IAssistDataReceiver receiver) {
|
||||
if (!checkCallingPermission(READ_FRAME_BUFFER,
|
||||
"requestAssistScreenshot()")) {
|
||||
if (!checkCallingPermission(READ_FRAME_BUFFER, "requestAssistScreenshot()")) {
|
||||
throw new SecurityException("Requires READ_FRAME_BUFFER permission");
|
||||
}
|
||||
|
||||
final Bitmap bm;
|
||||
synchronized (mWindowMap) {
|
||||
final DisplayContent displayContent = mRoot.getDisplayContent(DEFAULT_DISPLAY);
|
||||
if (displayContent == null) {
|
||||
if (DEBUG_SCREENSHOT) {
|
||||
Slog.i(TAG_WM, "Screenshot returning null. No Display for displayId="
|
||||
+ DEFAULT_DISPLAY);
|
||||
}
|
||||
bm = null;
|
||||
} else {
|
||||
bm = displayContent.screenshotDisplayLocked(Bitmap.Config.ARGB_8888);
|
||||
}
|
||||
}
|
||||
|
||||
FgThread.getHandler().post(() -> {
|
||||
Bitmap bm = screenshotApplications(DEFAULT_DISPLAY, Bitmap.Config.ARGB_8888,
|
||||
false /* wallpaperOnly */);
|
||||
try {
|
||||
receiver.onHandleAssistScreenshot(bm);
|
||||
} catch (RemoteException e) {
|
||||
@@ -3663,28 +3672,6 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a snapshot of the screen. In landscape mode this grabs the whole screen.
|
||||
* In portrait mode, it grabs the full screenshot.
|
||||
*
|
||||
* @param displayId the Display to take a screenshot of.
|
||||
* @param config of the output bitmap
|
||||
* @param wallpaperOnly true if only the wallpaper layer should be included in the screenshot
|
||||
*/
|
||||
private Bitmap screenshotApplications(int displayId, Bitmap.Config config,
|
||||
boolean wallpaperOnly) {
|
||||
final DisplayContent displayContent;
|
||||
synchronized(mWindowMap) {
|
||||
displayContent = mRoot.getDisplayContent(displayId);
|
||||
if (displayContent == null) {
|
||||
if (DEBUG_SCREENSHOT) Slog.i(TAG_WM, "Screenshot returning null. No Display for "
|
||||
+ "displayId=" + displayId);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return displayContent.screenshotDisplay(config, wallpaperOnly);
|
||||
}
|
||||
|
||||
/**
|
||||
* Freeze rotation changes. (Enable "rotation lock".)
|
||||
* Persists across reboots.
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.android.server.wm;
|
||||
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
|
||||
|
||||
import static junit.framework.TestCase.assertNotNull;
|
||||
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.IBinder;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* Tests for the {@link WallpaperController} class.
|
||||
*
|
||||
* Build/Install/Run:
|
||||
* atest com.android.server.wm.WallpaperControllerTests
|
||||
*/
|
||||
@SmallTest
|
||||
@Presubmit
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class WallpaperControllerTests extends WindowTestsBase {
|
||||
@Test
|
||||
public void testWallpaperScreenshot() {
|
||||
WindowSurfaceController windowSurfaceController = mock(WindowSurfaceController.class);
|
||||
|
||||
synchronized (sWm.mWindowMap) {
|
||||
// No wallpaper
|
||||
final DisplayContent dc = createNewDisplay();
|
||||
Bitmap wallpaperBitmap = sWm.mRoot.mWallpaperController.screenshotWallpaperLocked();
|
||||
assertNull(wallpaperBitmap);
|
||||
|
||||
// No wallpaper WSA Surface
|
||||
WindowToken wallpaperWindowToken = new WallpaperWindowToken(sWm, mock(IBinder.class),
|
||||
true, dc, true /* ownerCanManageAppTokens */);
|
||||
WindowState wallpaperWindow = createWindow(null /* parent */, TYPE_WALLPAPER,
|
||||
wallpaperWindowToken, "wallpaperWindow");
|
||||
wallpaperBitmap = mWallpaperController.screenshotWallpaperLocked();
|
||||
assertNull(wallpaperBitmap);
|
||||
|
||||
// Wallpaper with not visible WSA surface.
|
||||
wallpaperWindow.mWinAnimator.mSurfaceController = windowSurfaceController;
|
||||
wallpaperWindow.mWinAnimator.mLastAlpha = 1;
|
||||
wallpaperBitmap = mWallpaperController.screenshotWallpaperLocked();
|
||||
assertNull(wallpaperBitmap);
|
||||
|
||||
when(windowSurfaceController.getShown()).thenReturn(true);
|
||||
|
||||
// Wallpaper with WSA alpha set to 0.
|
||||
wallpaperWindow.mWinAnimator.mLastAlpha = 0;
|
||||
wallpaperBitmap = mWallpaperController.screenshotWallpaperLocked();
|
||||
assertNull(wallpaperBitmap);
|
||||
|
||||
// Wallpaper window with WSA Surface
|
||||
wallpaperWindow.mWinAnimator.mLastAlpha = 1;
|
||||
wallpaperBitmap = mWallpaperController.screenshotWallpaperLocked();
|
||||
assertNotNull(wallpaperBitmap);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -84,6 +84,7 @@ class WindowTestsBase {
|
||||
WindowState mChildAppWindowAbove;
|
||||
WindowState mChildAppWindowBelow;
|
||||
HashSet<WindowState> mCommonWindows;
|
||||
WallpaperController mWallpaperController;
|
||||
|
||||
@Mock
|
||||
static WindowState.PowerManagerWrapper mPowerManagerWrapper;
|
||||
@@ -105,6 +106,8 @@ class WindowTestsBase {
|
||||
sWm = TestWindowManagerPolicy.getWindowManagerService(context);
|
||||
beforeCreateDisplay();
|
||||
|
||||
mWallpaperController = new WallpaperController(sWm);
|
||||
|
||||
context.getDisplay().getDisplayInfo(mDisplayInfo);
|
||||
mDisplayContent = createNewDisplay();
|
||||
sWm.mDisplayEnabled = true;
|
||||
@@ -293,7 +296,7 @@ class WindowTestsBase {
|
||||
final int displayId = sNextDisplayId++;
|
||||
final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
|
||||
mDisplayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
|
||||
return new DisplayContent(display, sWm, new WallpaperController(sWm),
|
||||
return new DisplayContent(display, sWm, mWallpaperController,
|
||||
mock(DisplayWindowController.class));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user