diff options
-rw-r--r-- | services/java/com/android/server/wm/WindowManagerService.java | 284 |
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) != |