summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--services/java/com/android/server/wm/WindowManagerService.java284
1 files changed, 181 insertions, 103 deletions
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 56f4de5..b46fb2f 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -76,6 +76,7 @@ import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.Point;
@@ -282,6 +283,8 @@ public class WindowManagerService extends IWindowManager.Stub
private static final String SYSTEM_SECURE = "ro.secure";
private static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
+ private static final int MAX_SCREENSHOT_RETRIES = 3;
+
final private KeyguardDisableHandler mKeyguardDisableHandler;
private final boolean mHeadless;
@@ -5277,134 +5280,191 @@ public class WindowManagerService extends IWindowManager.Stub
throw new SecurityException("Requires READ_FRAME_BUFFER permission");
}
- Bitmap rawss;
+ Bitmap rawss = null;
int maxLayer = 0;
final Rect frame = new Rect();
- float scale;
+ float scale = 0;
int dw, dh;
- int rot;
-
- synchronized(mWindowMap) {
- long ident = Binder.clearCallingIdentity();
-
- final DisplayContent displayContent = getDisplayContentLocked(displayId);
- if (displayContent == null) {
- return null;
- }
- final DisplayInfo displayInfo = displayContent.getDisplayInfo();
- dw = displayInfo.logicalWidth;
- dh = displayInfo.logicalHeight;
+ int rot = Surface.ROTATION_0;
- int aboveAppLayer = mPolicy.windowTypeToLayerLw(TYPE_APPLICATION)
- * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
- aboveAppLayer += TYPE_LAYER_MULTIPLIER;
+ boolean screenshotReady;
+ int minLayer;
+ if (appToken == null) {
+ screenshotReady = true;
+ minLayer = 0;
+ } else {
+ screenshotReady = false;
+ minLayer = Integer.MAX_VALUE;
+ }
- boolean isImeTarget = mInputMethodTarget != null
- && mInputMethodTarget.mAppToken != null
- && mInputMethodTarget.mAppToken.appToken != null
- && mInputMethodTarget.mAppToken.appToken.asBinder() == appToken;
+ int retryCount = 0;
+ WindowState appWin = null;
- // Figure out the part of the screen that is actually the app.
- boolean including = false;
- final WindowList windows = displayContent.getWindowList();
- for (int i = windows.size() - 1; i >= 0; i--) {
- WindowState ws = windows.get(i);
- if (!ws.mHasSurface) {
- continue;
+ do {
+ if (retryCount++ > 0) {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
}
- if (ws.mLayer >= aboveAppLayer) {
- continue;
+ }
+ synchronized(mWindowMap) {
+ final DisplayContent displayContent = getDisplayContentLocked(displayId);
+ if (displayContent == null) {
+ return null;
}
- // When we will skip windows: when we are not including
- // ones behind a window we didn't skip, and we are actually
- // taking a screenshot of a specific app.
- if (!including && appToken != null) {
- // Also, we can possibly skip this window if it is not
- // an IME target or the application for the screenshot
- // is not the current IME target.
- if (!ws.mIsImWindow || !isImeTarget) {
- // And finally, this window is of no interest if it
- // is not associated with the screenshot app.
- if (ws.mAppToken == null || ws.mAppToken.token != appToken) {
- continue;
+ final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ dw = displayInfo.logicalWidth;
+ dh = displayInfo.logicalHeight;
+
+ int aboveAppLayer = mPolicy.windowTypeToLayerLw(TYPE_APPLICATION)
+ * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
+ aboveAppLayer += TYPE_LAYER_MULTIPLIER;
+
+ boolean isImeTarget = mInputMethodTarget != null
+ && mInputMethodTarget.mAppToken != null
+ && mInputMethodTarget.mAppToken.appToken != null
+ && mInputMethodTarget.mAppToken.appToken.asBinder() == appToken;
+
+ // Figure out the part of the screen that is actually the app.
+ boolean including = false;
+ appWin = null;
+ final WindowList windows = displayContent.getWindowList();
+ for (int i = windows.size() - 1; i >= 0; i--) {
+ WindowState ws = windows.get(i);
+ if (!ws.mHasSurface) {
+ continue;
+ }
+ if (ws.mLayer >= aboveAppLayer) {
+ continue;
+ }
+ // When we will skip windows: when we are not including
+ // ones behind a window we didn't skip, and we are actually
+ // taking a screenshot of a specific app.
+ if (!including && appToken != null) {
+ // Also, we can possibly skip this window if it is not
+ // an IME target or the application for the screenshot
+ // is not the current IME target.
+ if (!ws.mIsImWindow || !isImeTarget) {
+ // And finally, this window is of no interest if it
+ // is not associated with the screenshot app.
+ if (ws.mAppToken == null || ws.mAppToken.token != appToken) {
+ continue;
+ }
+ appWin = ws;
}
}
- }
- // We keep on including windows until we go past a full-screen
- // window.
- including = !ws.mIsImWindow && !ws.isFullscreen(dw, dh);
+ // We keep on including windows until we go past a full-screen
+ // window.
+ boolean fullscreen = ws.isFullscreen(dw, dh);
+ including = !ws.mIsImWindow && !fullscreen;
- if (maxLayer < ws.mWinAnimator.mSurfaceLayer) {
- maxLayer = ws.mWinAnimator.mSurfaceLayer;
- }
+ final WindowStateAnimator winAnim = ws.mWinAnimator;
+ if (maxLayer < winAnim.mSurfaceLayer) {
+ maxLayer = winAnim.mSurfaceLayer;
+ }
- // Don't include wallpaper in bounds calculation
- if (!ws.mIsWallpaper) {
- final Rect wf = ws.mFrame;
- final Rect cr = ws.mContentInsets;
- int left = wf.left + cr.left;
- int top = wf.top + cr.top;
- int right = wf.right - cr.right;
- int bottom = wf.bottom - cr.bottom;
- frame.union(left, top, right, bottom);
+ // Don't include wallpaper in bounds calculation
+ if (!ws.mIsWallpaper) {
+ final Rect wf = ws.mFrame;
+ final Rect cr = ws.mContentInsets;
+ int left = wf.left + cr.left;
+ int top = wf.top + cr.top;
+ int right = wf.right - cr.right;
+ int bottom = wf.bottom - cr.bottom;
+ frame.union(left, top, right, bottom);
+ }
+
+ if (ws.mAppToken != null && ws.mAppToken.token == appToken) {
+ if (minLayer > ws.mWinAnimator.mSurfaceLayer) {
+ minLayer = ws.mWinAnimator.mSurfaceLayer;
+ }
+ if (ws.isDisplayedLw()) {
+ screenshotReady = true;
+ }
+ if (fullscreen) {
+ // No point in continuing down through windows.
+ break;
+ }
+ }
}
- }
- Binder.restoreCallingIdentity(ident);
- // Constrain frame to the screen size.
- frame.intersect(0, 0, dw, dh);
+ if (appToken != null && appWin == null) {
+ // Can't find a window to snapshot.
+ if (DEBUG_SCREENSHOT) Slog.i(TAG,
+ "Screenshot: Couldn't find a surface matching " + appToken);
+ return null;
+ }
+ if (!screenshotReady) {
+ // Delay and hope that window gets drawn.
+ if (DEBUG_SCREENSHOT) Slog.i(TAG, "Screenshot: No image ready for " + appToken
+ + ", " + appWin + " drawState=" + appWin.mWinAnimator.mDrawState);
+ continue;
+ }
- if (frame.isEmpty() || maxLayer == 0) {
- return null;
- }
+ // Constrain frame to the screen size.
+ frame.intersect(0, 0, dw, dh);
- // The screenshot API does not apply the current screen rotation.
- rot = getDefaultDisplayContentLocked().getDisplay().getRotation();
- int fw = frame.width();
- int fh = frame.height();
-
- // Constrain thumbnail to smaller of screen width or height. Assumes aspect
- // of thumbnail is the same as the screen (in landscape) or square.
- float targetWidthScale = width / (float) fw;
- float targetHeightScale = height / (float) fh;
- if (dw <= dh) {
- scale = targetWidthScale;
- // If aspect of thumbnail is the same as the screen (in landscape),
- // select the slightly larger value so we fill the entire bitmap
- if (targetHeightScale > scale && (int) (targetHeightScale * fw) == width) {
- scale = targetHeightScale;
+ if (frame.isEmpty() || maxLayer == 0) {
+ if (DEBUG_SCREENSHOT) Slog.i(TAG, "Screenshot of " + appToken
+ + ": returning null frame=" + frame.toShortString() + " maxLayer="
+ + maxLayer);
+ return null;
}
- } else {
- scale = targetHeightScale;
- // If aspect of thumbnail is the same as the screen (in landscape),
- // select the slightly larger value so we fill the entire bitmap
- if (targetWidthScale > scale && (int) (targetWidthScale * fh) == height) {
+
+ // The screenshot API does not apply the current screen rotation.
+ rot = getDefaultDisplayContentLocked().getDisplay().getRotation();
+ int fw = frame.width();
+ int fh = frame.height();
+
+ // Constrain thumbnail to smaller of screen width or height. Assumes aspect
+ // of thumbnail is the same as the screen (in landscape) or square.
+ float targetWidthScale = width / (float) fw;
+ float targetHeightScale = height / (float) fh;
+ if (dw <= dh) {
scale = targetWidthScale;
+ // If aspect of thumbnail is the same as the screen (in landscape),
+ // select the slightly larger value so we fill the entire bitmap
+ if (targetHeightScale > scale && (int) (targetHeightScale * fw) == width) {
+ scale = targetHeightScale;
+ }
+ } else {
+ scale = targetHeightScale;
+ // If aspect of thumbnail is the same as the screen (in landscape),
+ // select the slightly larger value so we fill the entire bitmap
+ if (targetWidthScale > scale && (int) (targetWidthScale * fh) == height) {
+ scale = targetWidthScale;
+ }
}
- }
- // The screen shot will contain the entire screen.
- dw = (int)(dw*scale);
- dh = (int)(dh*scale);
- if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
- int tmp = dw;
- dw = dh;
- dh = tmp;
- rot = (rot == Surface.ROTATION_90) ? Surface.ROTATION_270 : Surface.ROTATION_90;
- }
- if (DEBUG_SCREENSHOT) {
- Slog.i(TAG, "Screenshot: " + dw + "x" + dh + " from 0 to " + maxLayer);
- for (int i = 0; i < windows.size(); i++) {
- WindowState win = windows.get(i);
- Slog.i(TAG, win + ": " + win.mLayer
- + " animLayer=" + win.mWinAnimator.mAnimLayer
- + " surfaceLayer=" + win.mWinAnimator.mSurfaceLayer);
+ // The screen shot will contain the entire screen.
+ dw = (int)(dw*scale);
+ dh = (int)(dh*scale);
+ if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
+ int tmp = dw;
+ dw = dh;
+ dh = tmp;
+ rot = (rot == Surface.ROTATION_90) ? Surface.ROTATION_270 : Surface.ROTATION_90;
+ }
+ if (DEBUG_SCREENSHOT) {
+ Slog.i(TAG, "Screenshot: " + dw + "x" + dh + " from " + minLayer + " to "
+ + maxLayer + " appToken=" + appToken);
+ for (int i = 0; i < windows.size(); i++) {
+ WindowState win = windows.get(i);
+ Slog.i(TAG, win + ": " + win.mLayer
+ + " animLayer=" + win.mWinAnimator.mAnimLayer
+ + " surfaceLayer=" + win.mWinAnimator.mSurfaceLayer);
+ }
}
+ rawss = SurfaceControl.screenshot(dw, dh, minLayer, maxLayer);
}
- rawss = SurfaceControl.screenshot(dw, dh, 0, maxLayer);
+ } while (!screenshotReady && retryCount <= MAX_SCREENSHOT_RETRIES);
+ if (DEBUG_SCREENSHOT && retryCount > MAX_SCREENSHOT_RETRIES) {
+ Slog.i(TAG, "Screenshot max retries " + retryCount + " of " + appToken + " appWin="
+ + (appWin == null ? "null" : (appWin + " drawState="
+ + appWin.mWinAnimator.mDrawState)));
}
if (rawss == null) {
@@ -5421,6 +5481,23 @@ public class WindowManagerService extends IWindowManager.Stub
canvas.drawBitmap(rawss, matrix, null);
canvas.setBitmap(null);
+ if (DEBUG_SCREENSHOT) {
+ // TEST IF IT's ALL BLACK
+ int[] buffer = new int[bm.getWidth() * bm.getHeight()];
+ bm.getPixels(buffer, 0, bm.getWidth(), 0, 0, bm.getWidth(), bm.getHeight());
+ boolean allBlack = true;
+ for (int i = 0; i < buffer.length; i++) {
+ if (buffer[i] != Color.BLACK) {
+ allBlack = false;
+ break;
+ }
+ }
+ if (allBlack) {
+ Slog.i(TAG, "Screenshot " + appWin + " was all black! mSurfaceLayer=" +
+ (appWin != null ? appWin.mWinAnimator.mSurfaceLayer : "null"));
+ }
+ }
+
rawss.recycle();
return bm;
}
@@ -7408,6 +7485,7 @@ public class WindowManagerService extends IWindowManager.Stub
performLayoutAndPlaceSurfacesLocked();
}
+ @Override
public void setOverscan(int displayId, int left, int top, int right, int bottom) {
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.WRITE_SECURE_SETTINGS) !=