summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java106
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java63
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java51
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java67
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java5
5 files changed, 191 insertions, 101 deletions
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 59bece051810..7b5e8b8139b4 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -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;
- }
-
- 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;
+ Bitmap screenshotDisplayLocked(Bitmap.Config config) {
+ if (!mService.mPolicy.isScreenOn()) {
+ if (DEBUG_SCREENSHOT) {
+ Slog.i(TAG_WM, "Attempted to take screenshot while display was off.");
}
+ return null;
+ }
- // 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;
- }
+ int dw = mDisplayInfo.logicalWidth;
+ int dh = mDisplayInfo.logicalHeight;
- // 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;
+ if (dw <= 0 || dh <= 0) {
+ return null;
}
- }
- private boolean shouldScreenshotWallpaper() {
- MutableBoolean screenshotReady = new MutableBoolean(false);
+ final Rect frame = new Rect(0, 0, dw, dh);
- forAllWindows(w -> {
- if (!w.mIsWallpaper) {
- return false;
- }
+ // The screenshot API does not apply the current screen rotation.
+ int rot = mDisplay.getRotation();
- // Found the wallpaper window
- final WindowStateAnimator winAnim = w.mWinAnimator;
+ if (rot == ROTATION_90 || rot == ROTATION_270) {
+ rot = (rot == ROTATION_90) ? ROTATION_270 : ROTATION_90;
+ }
- if (winAnim.getShown() && winAnim.mLastAlpha > 0f) {
- screenshotReady.value = true;
- }
+ // SurfaceFlinger is not aware of orientation, so convert our logical
+ // crop to SurfaceFlinger's portrait orientation.
+ convertCropForSurfaceFlinger(frame, rot, dw, dh);
- return true;
- }, true /* traverseTopToBottom */);
+ 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;
+ }
- return screenshotReady.value;
+ // 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()?
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 2873b6db1a20..c509980e1e9e 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -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) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 78c04e8e6a2c..bdd64d504607 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -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) {
@@ -3664,28 +3673,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.
* @param rotation The desired rotation to freeze to, or -1 to use the
diff --git a/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java
new file mode 100644
index 000000000000..71ead204c9df
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -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);
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 91d5ea463bbe..2890cbd5b6f5 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -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;
@@ -288,7 +291,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));
}