SurfaceFlinger: Add sourceCrop to screenshot

Adds a sourceCrop Rect parameter to screenshot commands, which allows
clients to capture only a portion of the screen instead of the whole
screen.

Bug: 15137922
Change-Id: I629447573cd34ffb96334cde7ba02490b9ea06d8
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 2efe4d3..6296dd1 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -141,7 +141,7 @@
 
     ScreenshotClient screenshot;
     sp<IBinder> display = SurfaceComposerClient::getBuiltInDisplay(displayId);
-    if (display != NULL && screenshot.update(display, false) == NO_ERROR) {
+    if (display != NULL && screenshot.update(display, Rect(), false) == NO_ERROR) {
         base = screenshot.getPixels();
         w = screenshot.getWidth();
         h = screenshot.getHeight();
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index c15ce44..5cd3d62 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -38,11 +38,11 @@
     private static native void nativeDestroy(long nativeObject);
 
     private static native Bitmap nativeScreenshot(IBinder displayToken,
-            int width, int height, int minLayer, int maxLayer, boolean allLayers,
-            boolean useIdentityTransform);
+            Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
+            boolean allLayers, boolean useIdentityTransform);
     private static native void nativeScreenshot(IBinder displayToken, Surface consumer,
-            int width, int height, int minLayer, int maxLayer, boolean allLayers,
-            boolean useIdentityTransform);
+            Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
+            boolean allLayers, boolean useIdentityTransform);
 
     private static native void nativeOpenTransaction();
     private static native void nativeCloseTransaction();
@@ -597,8 +597,8 @@
     public static void screenshot(IBinder display, Surface consumer,
             int width, int height, int minLayer, int maxLayer,
             boolean useIdentityTransform) {
-        screenshot(display, consumer, width, height, minLayer, maxLayer, false,
-                useIdentityTransform);
+        screenshot(display, consumer, new Rect(), width, height, minLayer, maxLayer,
+                false, useIdentityTransform);
     }
 
     /**
@@ -613,7 +613,7 @@
      */
     public static void screenshot(IBinder display, Surface consumer,
             int width, int height) {
-        screenshot(display, consumer, width, height, 0, 0, true, false);
+        screenshot(display, consumer, new Rect(), width, height, 0, 0, true, false);
     }
 
     /**
@@ -623,7 +623,7 @@
      * @param consumer The {@link Surface} to take the screenshot into.
      */
     public static void screenshot(IBinder display, Surface consumer) {
-        screenshot(display, consumer, 0, 0, 0, 0, true, false);
+        screenshot(display, consumer, new Rect(), 0, 0, 0, 0, true, false);
     }
 
     /**
@@ -634,6 +634,8 @@
      * the versions that use a {@link Surface} instead, such as
      * {@link SurfaceControl#screenshot(IBinder, Surface)}.
      *
+     * @param sourceCrop The portion of the screen to capture into the Bitmap;
+     * caller may pass in 'new Rect()' if no cropping is desired.
      * @param width The desired width of the returned bitmap; the raw
      * screen will be scaled down to this size.
      * @param height The desired height of the returned bitmap; the raw
@@ -649,13 +651,13 @@
      * 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(int width, int height, int minLayer, int maxLayer,
-            boolean useIdentityTransform) {
+    public static Bitmap screenshot(Rect sourceCrop, int width, int height,
+            int minLayer, int maxLayer, boolean useIdentityTransform) {
         // TODO: should take the display as a parameter
         IBinder displayToken = SurfaceControl.getBuiltInDisplay(
                 SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN);
-        return nativeScreenshot(displayToken, width, height, minLayer, maxLayer, false,
-                useIdentityTransform);
+        return nativeScreenshot(displayToken, sourceCrop, width, height,
+                minLayer, maxLayer, false, useIdentityTransform);
     }
 
     /**
@@ -674,10 +676,10 @@
         // TODO: should take the display as a parameter
         IBinder displayToken = SurfaceControl.getBuiltInDisplay(
                 SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN);
-        return nativeScreenshot(displayToken, width, height, 0, 0, true, false);
+        return nativeScreenshot(displayToken, new Rect(), width, height, 0, 0, true, false);
     }
 
-    private static void screenshot(IBinder display, Surface consumer,
+    private static void screenshot(IBinder display, Surface consumer, Rect sourceCrop,
             int width, int height, int minLayer, int maxLayer, boolean allLayers,
             boolean useIdentityTransform) {
         if (display == null) {
@@ -686,7 +688,7 @@
         if (consumer == null) {
             throw new IllegalArgumentException("consumer must not be null");
         }
-        nativeScreenshot(display, consumer, width, height, minLayer, maxLayer, allLayers,
-                useIdentityTransform);
+        nativeScreenshot(display, consumer, sourceCrop, width, height,
+                minLayer, maxLayer, allLayers, useIdentityTransform);
     }
 }
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 5a935a9..4594cc3 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -61,6 +61,13 @@
     jfieldID secure;
 } gPhysicalDisplayInfoClassInfo;
 
+static struct {
+    jfieldID bottom;
+    jfieldID left;
+    jfieldID right;
+    jfieldID top;
+} gRectClassInfo;
+
 // Implements SkMallocPixelRef::ReleaseProc, to delete the screenshot on unref.
 void DeleteScreenshot(void* addr, void* context) {
     SkASSERT(addr == ((ScreenshotClient*) context)->getPixels());
@@ -104,25 +111,32 @@
     ctrl->decStrong((void *)nativeCreate);
 }
 
-static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz, jobject displayTokenObj,
-        jint width, jint height, jint minLayer, jint maxLayer, bool allLayers,
-        bool useIdentityTransform) {
+static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz,
+        jobject displayTokenObj, jobject sourceCropObj, jint width, jint height,
+        jint minLayer, jint maxLayer, bool allLayers, bool useIdentityTransform) {
     sp<IBinder> displayToken = ibinderForJavaObject(env, displayTokenObj);
     if (displayToken == NULL) {
         return NULL;
     }
 
+    int left = env->GetIntField(sourceCropObj, gRectClassInfo.left);
+    int top = env->GetIntField(sourceCropObj, gRectClassInfo.top);
+    int right = env->GetIntField(sourceCropObj, gRectClassInfo.right);
+    int bottom = env->GetIntField(sourceCropObj, gRectClassInfo.bottom);
+    Rect sourceCrop(left, top, right, bottom);
+
     ScreenshotClient* screenshot = new ScreenshotClient();
     status_t res;
     if (width > 0 && height > 0) {
         if (allLayers) {
-            res = screenshot->update(displayToken, width, height, useIdentityTransform);
-        } else {
-            res = screenshot->update(displayToken, width, height, minLayer, maxLayer,
+            res = screenshot->update(displayToken, sourceCrop, width, height,
                     useIdentityTransform);
+        } else {
+            res = screenshot->update(displayToken, sourceCrop, width, height,
+                    minLayer, maxLayer, useIdentityTransform);
         }
     } else {
-        res = screenshot->update(displayToken, useIdentityTransform);
+        res = screenshot->update(displayToken, sourceCrop, useIdentityTransform);
     }
     if (res != NO_ERROR) {
         delete screenshot;
@@ -174,20 +188,25 @@
             GraphicsJNI::kBitmapCreateFlag_Premultiplied, NULL);
 }
 
-static void nativeScreenshot(JNIEnv* env, jclass clazz,
-        jobject displayTokenObj, jobject surfaceObj,
-        jint width, jint height, jint minLayer, jint maxLayer, bool allLayers,
-        bool useIdentityTransform) {
+static void nativeScreenshot(JNIEnv* env, jclass clazz, jobject displayTokenObj,
+        jobject surfaceObj, jobject sourceCropObj, jint width, jint height,
+        jint minLayer, jint maxLayer, bool allLayers, bool useIdentityTransform) {
     sp<IBinder> displayToken = ibinderForJavaObject(env, displayTokenObj);
     if (displayToken != NULL) {
         sp<Surface> consumer = android_view_Surface_getSurface(env, surfaceObj);
         if (consumer != NULL) {
+            int left = env->GetIntField(sourceCropObj, gRectClassInfo.left);
+            int top = env->GetIntField(sourceCropObj, gRectClassInfo.top);
+            int right = env->GetIntField(sourceCropObj, gRectClassInfo.right);
+            int bottom = env->GetIntField(sourceCropObj, gRectClassInfo.bottom);
+            Rect sourceCrop(left, top, right, bottom);
+
             if (allLayers) {
                 minLayer = 0;
                 maxLayer = -1;
             }
-            ScreenshotClient::capture(
-                    displayToken, consumer->getIGraphicBufferProducer(),
+            ScreenshotClient::capture(displayToken,
+                    consumer->getIGraphicBufferProducer(), sourceCrop,
                     width, height, uint32_t(minLayer), uint32_t(maxLayer),
                     useIdentityTransform);
         }
@@ -563,9 +582,9 @@
             (void*)nativeRelease },
     {"nativeDestroy", "(J)V",
             (void*)nativeDestroy },
-    {"nativeScreenshot", "(Landroid/os/IBinder;IIIIZZ)Landroid/graphics/Bitmap;",
+    {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/graphics/Rect;IIIIZZ)Landroid/graphics/Bitmap;",
             (void*)nativeScreenshotBitmap },
-    {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/view/Surface;IIIIZZ)V",
+    {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/view/Surface;Landroid/graphics/Rect;IIIIZZ)V",
             (void*)nativeScreenshot },
     {"nativeOpenTransaction", "()V",
             (void*)nativeOpenTransaction },
@@ -640,6 +659,12 @@
     gPhysicalDisplayInfoClassInfo.yDpi = env->GetFieldID(clazz, "yDpi", "F");
     gPhysicalDisplayInfoClassInfo.secure = env->GetFieldID(clazz, "secure", "Z");
 
+    jclass rectClazz = env->FindClass("android/graphics/Rect");
+    gRectClassInfo.bottom = env->GetFieldID(rectClazz, "bottom", "I");
+    gRectClassInfo.left = env->GetFieldID(rectClazz, "left", "I");
+    gRectClassInfo.right = env->GetFieldID(rectClazz, "right", "I");
+    gRectClassInfo.top = env->GetFieldID(rectClazz, "top", "I");
+
     jclass frameStatsClazz = env->FindClass("android/view/FrameStats");
     jfieldID undefined_time_nano_field =  env->GetStaticFieldID(frameStatsClazz, "UNDEFINED_TIME_NANO", "J");
     nsecs_t undefined_time_nano = env->GetStaticLongField(frameStatsClazz, undefined_time_nano_field);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 1a0dd82..97a42a4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5801,7 +5801,9 @@
                 // whether the screenshot should use the identity transformation matrix
                 // (e.g., enable it when taking a screenshot for recents, since we might be in
                 // the middle of the rotation animation, but don't want a rotated recent image).
-                rawss = SurfaceControl.screenshot(dw, dh, minLayer, maxLayer, false);
+                // TODO: Replace 'new Rect()' with the portion of the screen to capture for the
+                // screenshot.
+                rawss = SurfaceControl.screenshot(new Rect(), dw, dh, minLayer, maxLayer, false);
             }
         } while (!screenshotReady && retryCount <= MAX_SCREENSHOT_RETRIES);
         if (retryCount > MAX_SCREENSHOT_RETRIES)  Slog.i(TAG, "Screenshot max retries " +