diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 1e28e33c290fe..40745292542e7 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -39,7 +39,7 @@ public class SurfaceControl { private static native Bitmap nativeScreenshot(IBinder displayToken, Rect sourceCrop, int width, int height, int minLayer, int maxLayer, - boolean allLayers, boolean useIdentityTransform); + boolean allLayers, boolean useIdentityTransform, int rotation); private static native void nativeScreenshot(IBinder displayToken, Surface consumer, Rect sourceCrop, int width, int height, int minLayer, int maxLayer, boolean allLayers, boolean useIdentityTransform); @@ -688,17 +688,23 @@ public class SurfaceControl { * @param useIdentityTransform Replace whatever transformation (rotation, * scaling, translation) the surface layers are currently using with the * identity transformation while taking the screenshot. + * @param rotation Apply a custom clockwise rotation to the screenshot, i.e. + * Surface.ROTATION_0,90,180,270. Surfaceflinger will always take + * screenshots in its native portrait orientation by default, so this is + * useful for returning screenshots that are independent of device + * orientation. * @return Returns a Bitmap containing the screen contents, or null * if an error occurs. Make sure to call Bitmap.recycle() as soon as * possible, once its content is not needed anymore. */ public static Bitmap screenshot(Rect sourceCrop, int width, int height, - int minLayer, int maxLayer, boolean useIdentityTransform) { + int minLayer, int maxLayer, boolean useIdentityTransform, + int rotation) { // TODO: should take the display as a parameter IBinder displayToken = SurfaceControl.getBuiltInDisplay( SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN); return nativeScreenshot(displayToken, sourceCrop, width, height, - minLayer, maxLayer, false, useIdentityTransform); + minLayer, maxLayer, false, useIdentityTransform, rotation); } /** @@ -717,7 +723,8 @@ public class SurfaceControl { // TODO: should take the display as a parameter IBinder displayToken = SurfaceControl.getBuiltInDisplay( SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN); - return nativeScreenshot(displayToken, new Rect(), width, height, 0, 0, true, false); + return nativeScreenshot(displayToken, new Rect(), width, height, 0, 0, true, + false, Surface.ROTATION_0); } private static void screenshot(IBinder display, Surface consumer, Rect sourceCrop, diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 8f30f5d0430aa..06c22aedda7f3 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -117,7 +117,8 @@ static void nativeDestroy(JNIEnv* env, jclass clazz, jlong nativeObject) { static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz, jobject displayTokenObj, jobject sourceCropObj, jint width, jint height, - jint minLayer, jint maxLayer, bool allLayers, bool useIdentityTransform) { + jint minLayer, jint maxLayer, bool allLayers, bool useIdentityTransform, + int rotation) { sp displayToken = ibinderForJavaObject(env, displayTokenObj); if (displayToken == NULL) { return NULL; @@ -131,17 +132,13 @@ static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz, SkAutoTDelete screenshot(new ScreenshotClient()); status_t res; - if (width > 0 && height > 0) { - if (allLayers) { - res = screenshot->update(displayToken, sourceCrop, width, height, - useIdentityTransform); - } else { - res = screenshot->update(displayToken, sourceCrop, width, height, - minLayer, maxLayer, useIdentityTransform); - } - } else { - res = screenshot->update(displayToken, sourceCrop, useIdentityTransform); + if (allLayers) { + minLayer = 0; + maxLayer = -1UL; } + + res = screenshot->update(displayToken, sourceCrop, width, height, + minLayer, maxLayer, useIdentityTransform, static_cast(rotation)); if (res != NO_ERROR) { return NULL; } @@ -588,7 +585,7 @@ static JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeRelease }, {"nativeDestroy", "(J)V", (void*)nativeDestroy }, - {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/graphics/Rect;IIIIZZ)Landroid/graphics/Bitmap;", + {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/graphics/Rect;IIIIZZI)Landroid/graphics/Bitmap;", (void*)nativeScreenshotBitmap }, {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/view/Surface;Landroid/graphics/Rect;IIIIZZ)V", (void*)nativeScreenshot }, diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 434e48f613f99..10ab6b545e0d2 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -5931,7 +5931,7 @@ public class WindowManagerService extends IWindowManager.Stub @Override public Bitmap screenshotApplications(IBinder appToken, int displayId, int width, int height, boolean force565) { - if (!checkCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER, + if (!checkCallingPermission(Manifest.permission.READ_FRAME_BUFFER, "screenshotApplications()")) { throw new SecurityException("Requires READ_FRAME_BUFFER permission"); } @@ -5951,7 +5951,7 @@ public class WindowManagerService extends IWindowManager.Stub return null; } - Bitmap rawss = null; + Bitmap bm = null; int maxLayer = 0; final Rect frame = new Rect(); @@ -6092,10 +6092,8 @@ public class WindowManagerService extends IWindowManager.Stub // The screenshot API does not apply the current screen rotation. rot = getDefaultDisplayContentLocked().getDisplay().getRotation(); + if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) { - final int tmp = width; - width = height; - height = tmp; rot = (rot == Surface.ROTATION_90) ? Surface.ROTATION_270 : Surface.ROTATION_90; } @@ -6121,9 +6119,9 @@ public class WindowManagerService extends IWindowManager.Stub if (DEBUG_SCREENSHOT && inRotation) Slog.v(TAG, "Taking screenshot while rotating"); - rawss = SurfaceControl.screenshot(crop, width, height, minLayer, maxLayer, - inRotation); - if (rawss == null) { + bm = SurfaceControl.screenshot(crop, width, height, minLayer, maxLayer, + inRotation, rot); + if (bm == null) { Slog.w(TAG, "Screenshot failure taking screenshot for (" + dw + "x" + dh + ") to layer " + maxLayer); return null; @@ -6133,17 +6131,6 @@ public class WindowManagerService extends IWindowManager.Stub break; } - Bitmap bm = Bitmap.createBitmap(width, height, force565 ? - Config.RGB_565 : rawss.getConfig()); - if (DEBUG_SCREENSHOT) { - bm.eraseColor(0xFF000000); - } - Matrix matrix = new Matrix(); - ScreenRotationAnimation.createRotationMatrix(rot, width, height, matrix); - Canvas canvas = new Canvas(bm); - 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()]; @@ -6164,9 +6151,12 @@ public class WindowManagerService extends IWindowManager.Stub } } - rawss.recycle(); - - return bm; + // Copy the screenshot bitmap to another buffer so that the gralloc backed + // bitmap will not have a long lifetime. Gralloc memory can be pinned or + // duplicated and might have a higher cost than a skia backed buffer. + Bitmap ret = bm.copy(bm.getConfig(),true); + bm.recycle(); + return ret; } /**