Loading services/core/java/com/android/server/wm/AppWindowContainerController.java +1 −1 Original line number Diff line number Diff line Loading @@ -617,7 +617,7 @@ public class AppWindowContainerController } return dc.screenshotApplications(mToken.asBinder(), width, height, false /* includeFullDisplay */, frameScale, Bitmap.Config.RGB_565, false /* wallpaperOnly */, false /* includeDecor */, true /* toAshmem */); false /* wallpaperOnly */, false /* includeDecor */); } finally { Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } Loading services/core/java/com/android/server/wm/DisplayContent.java +60 −40 Original line number Diff line number Diff line Loading @@ -94,6 +94,7 @@ import android.annotation.NonNull; import android.app.ActivityManager.StackId; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.GraphicBuffer; import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.RectF; Loading Loading @@ -2105,12 +2106,55 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo * @param wallpaperOnly true if only the wallpaper layer should be included in the screenshot * @param includeDecor whether to include window decors, like the status or navigation bar * background of the window * @param toAshmem whether to convert the resulting bitmap to ashmem; this should be set to * true if the Bitmap is sent over binder, and false otherwise */ Bitmap screenshotApplications(IBinder appToken, int width, int height, boolean includeFullDisplay, float frameScale, Bitmap.Config config, boolean wallpaperOnly, boolean includeDecor, boolean toAshmem) { boolean wallpaperOnly, boolean includeDecor) { Bitmap bitmap = screenshotApplications(appToken, width, height, includeFullDisplay, frameScale, wallpaperOnly, includeDecor, SurfaceControl::screenshot); if (DEBUG_SCREENSHOT) { // TEST IF IT's ALL BLACK int[] buffer = new int[bitmap.getWidth() * bitmap.getHeight()]; bitmap.getPixels(buffer, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight()); boolean allBlack = true; final int firstColor = buffer[0]; for (int i = 0; i < buffer.length; i++) { if (buffer[i] != firstColor) { allBlack = false; break; } } if (allBlack) { final WindowState appWin = mScreenshotApplicationState.appWin; final int maxLayer = mScreenshotApplicationState.maxLayer; final int minLayer = mScreenshotApplicationState.minLayer; Slog.i(TAG_WM, "Screenshot " + appWin + " was monochrome(" + Integer.toHexString(firstColor) + ")! mSurfaceLayer=" + (appWin != null ? appWin.mWinAnimator.mSurfaceController.getLayer() : "null") + " minLayer=" + minLayer + " maxLayer=" + maxLayer); } } // Create a copy of the screenshot that is immutable and backed in ashmem. // This greatly reduces the overhead of passing the bitmap between processes. Bitmap ret = bitmap.createAshmemBitmap(config); bitmap.recycle(); return ret; } GraphicBuffer screenshotApplicationsToBuffer(IBinder appToken, int width, int height, boolean includeFullDisplay, float frameScale, boolean wallpaperOnly, boolean includeDecor) { return screenshotApplications(appToken, width, height, includeFullDisplay, frameScale, wallpaperOnly, includeDecor, SurfaceControl::screenshotToBuffer); } private <E> E screenshotApplications(IBinder appToken, int width, int height, boolean includeFullDisplay, float frameScale, boolean wallpaperOnly, boolean includeDecor, Screenshoter<E> screenshoter) { int dw = mDisplayInfo.logicalWidth; int dh = mDisplayInfo.logicalHeight; if (dw == 0 || dh == 0) { Loading @@ -2119,7 +2163,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return null; } Bitmap bm = null; E bitmap; mScreenshotApplicationState.reset(appToken == null && !wallpaperOnly); final Rect frame = new Rect(); Loading Loading @@ -2327,48 +2371,15 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo SurfaceControl.openTransaction(); SurfaceControl.closeTransactionSync(); bm = SurfaceControl.screenshot(crop, width, height, minLayer, maxLayer, bitmap = screenshoter.screenshot(crop, width, height, minLayer, maxLayer, inRotation, rot); if (bm == null) { if (bitmap == null) { Slog.w(TAG_WM, "Screenshot failure taking screenshot for (" + dw + "x" + dh + ") to layer " + maxLayer); return 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; final int firstColor = buffer[0]; for (int i = 0; i < buffer.length; i++) { if (buffer[i] != firstColor) { allBlack = false; break; } } if (allBlack) { final WindowState appWin = mScreenshotApplicationState.appWin; final int maxLayer = mScreenshotApplicationState.maxLayer; final int minLayer = mScreenshotApplicationState.minLayer; Slog.i(TAG_WM, "Screenshot " + appWin + " was monochrome(" + Integer.toHexString(firstColor) + ")! mSurfaceLayer=" + (appWin != null ? appWin.mWinAnimator.mSurfaceController.getLayer() : "null") + " minLayer=" + minLayer + " maxLayer=" + maxLayer); } } // Create a copy of the screenshot that is immutable and backed in ashmem. // This greatly reduces the overhead of passing the bitmap between processes. if (toAshmem) { Bitmap ret = bm.createAshmemBitmap(config); bm.recycle(); return ret; } else { return bm; } return bitmap; } // TODO: Can this use createRotationMatrix()? Loading Loading @@ -2721,4 +2732,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return mName; } } /** * Interface to screenshot into various types, i.e. {@link Bitmap} and {@link GraphicBuffer}. */ @FunctionalInterface private interface Screenshoter<E> { E screenshot(Rect sourceCrop, int width, int height, int minLayer, int maxLayer, boolean useIdentityTransform, int rotation); } } services/core/java/com/android/server/wm/TaskSnapshotController.java +3 −9 Original line number Diff line number Diff line Loading @@ -96,17 +96,11 @@ class TaskSnapshotController { if (top == null) { return null; } final Bitmap bmp = top.mDisplayContent.screenshotApplications(top.token, -1, -1, false, 1.0f, ARGB_8888, false, true, false); if (bmp == null) { final GraphicBuffer buffer = top.mDisplayContent.screenshotApplicationsToBuffer(top.token, -1, -1, false, 1.0f, false, true); if (buffer == null) { return null; } // TODO: Already use a GraphicBuffer when snapshotting the content. final GraphicBuffer buffer = GraphicBuffer.create(bmp.getWidth(), bmp.getHeight(), RGBA_8888, USAGE_HW_TEXTURE | USAGE_SW_WRITE_NEVER | USAGE_SW_READ_NEVER); final Canvas c = buffer.lockCanvas(); c.drawBitmap(bmp, 0, 0, null); buffer.unlockCanvasAndPost(c); return new TaskSnapshot(buffer, top.getConfiguration().orientation, top.findMainWindow().mStableInsets); } Loading services/core/java/com/android/server/wm/WindowManagerService.java +4 −7 Original line number Diff line number Diff line Loading @@ -3862,8 +3862,7 @@ public class WindowManagerService extends IWindowManager.Stub Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenshotWallpaper"); return screenshotApplications(null /* appToken */, DEFAULT_DISPLAY, -1 /* width */, -1 /* height */, true /* includeFullDisplay */, 1f /* frameScale */, Bitmap.Config.ARGB_8888, true /* wallpaperOnly */, false /* includeDecor */, true /* toAshmem */); Bitmap.Config.ARGB_8888, true /* wallpaperOnly */, false /* includeDecor */); } finally { Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } Loading @@ -3885,7 +3884,7 @@ public class WindowManagerService extends IWindowManager.Stub Bitmap bm = screenshotApplications(null /* appToken */, DEFAULT_DISPLAY, -1 /* width */, -1 /* height */, true /* includeFullDisplay */, 1f /* frameScale */, Bitmap.Config.ARGB_8888, false /* wallpaperOnly */, false /* includeDecor */, true /* toAshmem */); false /* includeDecor */); try { receiver.send(bm); } catch (RemoteException e) { Loading @@ -3908,12 +3907,10 @@ public class WindowManagerService extends IWindowManager.Stub * @param wallpaperOnly true if only the wallpaper layer should be included in the screenshot * @param includeDecor whether to include window decors, like the status or navigation bar * background of the window * @param toAshmem whether to convert the resulting bitmap to ashmem; this should be set to * true if the Bitmap is sent over binder, and false otherwise */ private Bitmap screenshotApplications(IBinder appToken, int displayId, int width, int height, boolean includeFullDisplay, float frameScale, Bitmap.Config config, boolean wallpaperOnly, boolean includeDecor, boolean toAshmem) { boolean wallpaperOnly, boolean includeDecor) { final DisplayContent displayContent; synchronized(mWindowMap) { displayContent = mRoot.getDisplayContentOrCreate(displayId); Loading @@ -3924,7 +3921,7 @@ public class WindowManagerService extends IWindowManager.Stub } } return displayContent.screenshotApplications(appToken, width, height, includeFullDisplay, frameScale, config, wallpaperOnly, includeDecor, toAshmem); includeFullDisplay, frameScale, config, wallpaperOnly, includeDecor); } /** Loading Loading
services/core/java/com/android/server/wm/AppWindowContainerController.java +1 −1 Original line number Diff line number Diff line Loading @@ -617,7 +617,7 @@ public class AppWindowContainerController } return dc.screenshotApplications(mToken.asBinder(), width, height, false /* includeFullDisplay */, frameScale, Bitmap.Config.RGB_565, false /* wallpaperOnly */, false /* includeDecor */, true /* toAshmem */); false /* wallpaperOnly */, false /* includeDecor */); } finally { Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } Loading
services/core/java/com/android/server/wm/DisplayContent.java +60 −40 Original line number Diff line number Diff line Loading @@ -94,6 +94,7 @@ import android.annotation.NonNull; import android.app.ActivityManager.StackId; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.GraphicBuffer; import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.RectF; Loading Loading @@ -2105,12 +2106,55 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo * @param wallpaperOnly true if only the wallpaper layer should be included in the screenshot * @param includeDecor whether to include window decors, like the status or navigation bar * background of the window * @param toAshmem whether to convert the resulting bitmap to ashmem; this should be set to * true if the Bitmap is sent over binder, and false otherwise */ Bitmap screenshotApplications(IBinder appToken, int width, int height, boolean includeFullDisplay, float frameScale, Bitmap.Config config, boolean wallpaperOnly, boolean includeDecor, boolean toAshmem) { boolean wallpaperOnly, boolean includeDecor) { Bitmap bitmap = screenshotApplications(appToken, width, height, includeFullDisplay, frameScale, wallpaperOnly, includeDecor, SurfaceControl::screenshot); if (DEBUG_SCREENSHOT) { // TEST IF IT's ALL BLACK int[] buffer = new int[bitmap.getWidth() * bitmap.getHeight()]; bitmap.getPixels(buffer, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight()); boolean allBlack = true; final int firstColor = buffer[0]; for (int i = 0; i < buffer.length; i++) { if (buffer[i] != firstColor) { allBlack = false; break; } } if (allBlack) { final WindowState appWin = mScreenshotApplicationState.appWin; final int maxLayer = mScreenshotApplicationState.maxLayer; final int minLayer = mScreenshotApplicationState.minLayer; Slog.i(TAG_WM, "Screenshot " + appWin + " was monochrome(" + Integer.toHexString(firstColor) + ")! mSurfaceLayer=" + (appWin != null ? appWin.mWinAnimator.mSurfaceController.getLayer() : "null") + " minLayer=" + minLayer + " maxLayer=" + maxLayer); } } // Create a copy of the screenshot that is immutable and backed in ashmem. // This greatly reduces the overhead of passing the bitmap between processes. Bitmap ret = bitmap.createAshmemBitmap(config); bitmap.recycle(); return ret; } GraphicBuffer screenshotApplicationsToBuffer(IBinder appToken, int width, int height, boolean includeFullDisplay, float frameScale, boolean wallpaperOnly, boolean includeDecor) { return screenshotApplications(appToken, width, height, includeFullDisplay, frameScale, wallpaperOnly, includeDecor, SurfaceControl::screenshotToBuffer); } private <E> E screenshotApplications(IBinder appToken, int width, int height, boolean includeFullDisplay, float frameScale, boolean wallpaperOnly, boolean includeDecor, Screenshoter<E> screenshoter) { int dw = mDisplayInfo.logicalWidth; int dh = mDisplayInfo.logicalHeight; if (dw == 0 || dh == 0) { Loading @@ -2119,7 +2163,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return null; } Bitmap bm = null; E bitmap; mScreenshotApplicationState.reset(appToken == null && !wallpaperOnly); final Rect frame = new Rect(); Loading Loading @@ -2327,48 +2371,15 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo SurfaceControl.openTransaction(); SurfaceControl.closeTransactionSync(); bm = SurfaceControl.screenshot(crop, width, height, minLayer, maxLayer, bitmap = screenshoter.screenshot(crop, width, height, minLayer, maxLayer, inRotation, rot); if (bm == null) { if (bitmap == null) { Slog.w(TAG_WM, "Screenshot failure taking screenshot for (" + dw + "x" + dh + ") to layer " + maxLayer); return 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; final int firstColor = buffer[0]; for (int i = 0; i < buffer.length; i++) { if (buffer[i] != firstColor) { allBlack = false; break; } } if (allBlack) { final WindowState appWin = mScreenshotApplicationState.appWin; final int maxLayer = mScreenshotApplicationState.maxLayer; final int minLayer = mScreenshotApplicationState.minLayer; Slog.i(TAG_WM, "Screenshot " + appWin + " was monochrome(" + Integer.toHexString(firstColor) + ")! mSurfaceLayer=" + (appWin != null ? appWin.mWinAnimator.mSurfaceController.getLayer() : "null") + " minLayer=" + minLayer + " maxLayer=" + maxLayer); } } // Create a copy of the screenshot that is immutable and backed in ashmem. // This greatly reduces the overhead of passing the bitmap between processes. if (toAshmem) { Bitmap ret = bm.createAshmemBitmap(config); bm.recycle(); return ret; } else { return bm; } return bitmap; } // TODO: Can this use createRotationMatrix()? Loading Loading @@ -2721,4 +2732,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return mName; } } /** * Interface to screenshot into various types, i.e. {@link Bitmap} and {@link GraphicBuffer}. */ @FunctionalInterface private interface Screenshoter<E> { E screenshot(Rect sourceCrop, int width, int height, int minLayer, int maxLayer, boolean useIdentityTransform, int rotation); } }
services/core/java/com/android/server/wm/TaskSnapshotController.java +3 −9 Original line number Diff line number Diff line Loading @@ -96,17 +96,11 @@ class TaskSnapshotController { if (top == null) { return null; } final Bitmap bmp = top.mDisplayContent.screenshotApplications(top.token, -1, -1, false, 1.0f, ARGB_8888, false, true, false); if (bmp == null) { final GraphicBuffer buffer = top.mDisplayContent.screenshotApplicationsToBuffer(top.token, -1, -1, false, 1.0f, false, true); if (buffer == null) { return null; } // TODO: Already use a GraphicBuffer when snapshotting the content. final GraphicBuffer buffer = GraphicBuffer.create(bmp.getWidth(), bmp.getHeight(), RGBA_8888, USAGE_HW_TEXTURE | USAGE_SW_WRITE_NEVER | USAGE_SW_READ_NEVER); final Canvas c = buffer.lockCanvas(); c.drawBitmap(bmp, 0, 0, null); buffer.unlockCanvasAndPost(c); return new TaskSnapshot(buffer, top.getConfiguration().orientation, top.findMainWindow().mStableInsets); } Loading
services/core/java/com/android/server/wm/WindowManagerService.java +4 −7 Original line number Diff line number Diff line Loading @@ -3862,8 +3862,7 @@ public class WindowManagerService extends IWindowManager.Stub Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenshotWallpaper"); return screenshotApplications(null /* appToken */, DEFAULT_DISPLAY, -1 /* width */, -1 /* height */, true /* includeFullDisplay */, 1f /* frameScale */, Bitmap.Config.ARGB_8888, true /* wallpaperOnly */, false /* includeDecor */, true /* toAshmem */); Bitmap.Config.ARGB_8888, true /* wallpaperOnly */, false /* includeDecor */); } finally { Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } Loading @@ -3885,7 +3884,7 @@ public class WindowManagerService extends IWindowManager.Stub Bitmap bm = screenshotApplications(null /* appToken */, DEFAULT_DISPLAY, -1 /* width */, -1 /* height */, true /* includeFullDisplay */, 1f /* frameScale */, Bitmap.Config.ARGB_8888, false /* wallpaperOnly */, false /* includeDecor */, true /* toAshmem */); false /* includeDecor */); try { receiver.send(bm); } catch (RemoteException e) { Loading @@ -3908,12 +3907,10 @@ public class WindowManagerService extends IWindowManager.Stub * @param wallpaperOnly true if only the wallpaper layer should be included in the screenshot * @param includeDecor whether to include window decors, like the status or navigation bar * background of the window * @param toAshmem whether to convert the resulting bitmap to ashmem; this should be set to * true if the Bitmap is sent over binder, and false otherwise */ private Bitmap screenshotApplications(IBinder appToken, int displayId, int width, int height, boolean includeFullDisplay, float frameScale, Bitmap.Config config, boolean wallpaperOnly, boolean includeDecor, boolean toAshmem) { boolean wallpaperOnly, boolean includeDecor) { final DisplayContent displayContent; synchronized(mWindowMap) { displayContent = mRoot.getDisplayContentOrCreate(displayId); Loading @@ -3924,7 +3921,7 @@ public class WindowManagerService extends IWindowManager.Stub } } return displayContent.screenshotApplications(appToken, width, height, includeFullDisplay, frameScale, config, wallpaperOnly, includeDecor, toAshmem); includeFullDisplay, frameScale, config, wallpaperOnly, includeDecor); } /** Loading