Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ca2eb018 authored by chaviw's avatar chaviw
Browse files

Remove rotation and use flag useIdentityTransform for screenshots.

There's a lot of confusing logic where 90 and 270 rotation values need
to be flipped to ensure the screenshot is taken the correct orientation.
There's also confusion what useIdentityTransform means, especially if a
non 0 rotation value is sent.

The cases screenshot cares about is the following:
1. Take screenshot in current display orientation
2. Take screenshot with 0 rotation so the caller can handle rotating the
screenshot themselves.

With these two cases in mind, remove the rotation value passed in for
screenshots. If useIdentityTransform is true, it will rotate the
screenshot so it's in the 0 orientation. If useIdentityTransform is
false, it will use the current display rotation.

This simplifies the caller logic since they no longer have to find the
current display rotation to ensure the screenshot is taken in the
current rotation. The callers can just request the screenshot with
useIdentityTransform set to false.

Test: adb shell screencap
Test: Power + volume screenshot
Test: Screen rotation
Fixes: 135942984
Change-Id: I3435ee8b5dac05e910ec1e695f398c5dcdcff9e9
parent 4c365410
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -39,7 +39,7 @@ interface IUiAutomationConnection {
    boolean injectInputEvent(in InputEvent event, boolean sync);
    void syncInputTransactions();
    boolean setRotation(int rotation);
    Bitmap takeScreenshot(in Rect crop, int rotation);
    Bitmap takeScreenshot(in Rect crop);
    boolean clearWindowContentFrameStats(int windowId);
    WindowContentFrameStats getWindowContentFrameStats(int windowId);
    void clearWindowAnimationFrameStats();
+1 −1
Original line number Diff line number Diff line
@@ -903,7 +903,7 @@ public final class UiAutomation {
        try {
            // Calling out without a lock held.
            screenShot = mUiAutomationConnection.takeScreenshot(
                    new Rect(0, 0, displaySize.x, displaySize.y), rotation);
                    new Rect(0, 0, displaySize.x, displaySize.y));
            if (screenShot == null) {
                return null;
            }
+10 −2
Original line number Diff line number Diff line
@@ -180,7 +180,7 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub {
    }

    @Override
    public Bitmap takeScreenshot(Rect crop, int rotation) {
    public Bitmap takeScreenshot(Rect crop) {
        synchronized (mLock) {
            throwIfCalledByNotTrustedUidLocked();
            throwIfShutdownLocked();
@@ -190,7 +190,15 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub {
        try {
            int width = crop.width();
            int height = crop.height();
            return SurfaceControl.screenshot(crop, width, height, rotation);
            final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
            final SurfaceControl.DisplayCaptureArgs captureArgs =
                    new SurfaceControl.DisplayCaptureArgs.Builder(displayToken)
                            .setSourceCrop(crop)
                            .setSize(width, height)
                            .build();
            final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
                    SurfaceControl.captureDisplay(captureArgs);
            return screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
+29 −138
Original line number Diff line number Diff line
@@ -22,8 +22,6 @@ import static android.graphics.Matrix.MSKEW_X;
import static android.graphics.Matrix.MSKEW_Y;
import static android.graphics.Matrix.MTRANS_X;
import static android.graphics.Matrix.MTRANS_Y;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import static android.view.SurfaceControlProto.HASH_CODE;
import static android.view.SurfaceControlProto.NAME;

@@ -590,6 +588,26 @@ public final class SurfaceControl implements Parcelable {
        public boolean containsSecureLayers() {
            return mContainsSecureLayers;
        }

        /**
         * Copy content of ScreenshotHardwareBuffer into a hardware bitmap and return it.
         * Note: If you want to modify the Bitmap in software, you will need to copy the Bitmap
         * into
         * a software Bitmap using {@link Bitmap#copy(Bitmap.Config, boolean)}
         *
         * CAVEAT: This can be extremely slow; avoid use unless absolutely necessary; prefer to
         * directly
         * use the {@link HardwareBuffer} directly.
         *
         * @return Bitmap generated from the {@link HardwareBuffer}
         */
        public Bitmap asBitmap() {
            if (mHardwareBuffer == null) {
                Log.w(TAG, "Failed to take screenshot. Null screenshot object");
                return null;
            }
            return Bitmap.wrapHardwareBuffer(mHardwareBuffer, mColorSpace);
        }
    }

    /**
@@ -597,7 +615,7 @@ public final class SurfaceControl implements Parcelable {
     * are shared between {@link DisplayCaptureArgs} and {@link LayerCaptureArgs}
     * @hide
     */
    public abstract static class CaptureArgs {
    private abstract static class CaptureArgs {
        private final int mPixelFormat;
        private final Rect mSourceCrop = new Rect();
        private final float mFrameScale;
@@ -615,7 +633,7 @@ public final class SurfaceControl implements Parcelable {
         *
         * @param <T> A builder that extends {@link Builder}
         */
        public abstract static class Builder<T extends Builder<T>> {
        abstract static class Builder<T extends Builder<T>> {
            private int mPixelFormat = PixelFormat.RGBA_8888;
            private final Rect mSourceCrop = new Rect();
            private float mFrameScale = 1;
@@ -675,7 +693,6 @@ public final class SurfaceControl implements Parcelable {
        private final int mWidth;
        private final int mHeight;
        private final boolean mUseIdentityTransform;
        private final int mRotation;

        private DisplayCaptureArgs(Builder builder) {
            super(builder);
@@ -683,7 +700,6 @@ public final class SurfaceControl implements Parcelable {
            mWidth = builder.mWidth;
            mHeight = builder.mHeight;
            mUseIdentityTransform = builder.mUseIdentityTransform;
            mRotation = builder.mRotation;
        }

        /**
@@ -694,7 +710,6 @@ public final class SurfaceControl implements Parcelable {
            private int mWidth;
            private int mHeight;
            private boolean mUseIdentityTransform;
            private @Surface.Rotation int mRotation = Surface.ROTATION_0;

            /**
             * Construct a new {@link LayerCaptureArgs} with the set parameters. The builder
@@ -736,26 +751,16 @@ public final class SurfaceControl implements Parcelable {
            }

            /**
             * Replace whatever transformation (rotation, scaling, translation) the surface
             * layers are currently using with the identity transformation while taking the
             * screenshot.
             * Replace the rotation transform of the display with the identity transformation while
             * taking the screenshot. This ensures the screenshot is taken in the ROTATION_0
             * orientation. Set this value to false if the screenshot should be taken in the
             * current screen orientation.
             */
            public Builder setUseIdentityTransform(boolean useIdentityTransform) {
                mUseIdentityTransform = useIdentityTransform;
                return this;
            }

            /**
             * 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.
             */
            public Builder setRotation(@Surface.Rotation int rotation) {
                mRotation = rotation;
                return this;
            }

            @Override
            Builder getThis() {
                return this;
@@ -2221,129 +2226,15 @@ public final class SurfaceControl implements Parcelable {
    }

    /**
     * @see SurfaceControl#screenshot(Rect, int, int, boolean, int)}
     * @hide
     */
    @UnsupportedAppUsage
    public static Bitmap screenshot(Rect sourceCrop, int width, int height, int rotation) {
        return screenshot(sourceCrop, width, height, false, rotation);
    }

    /**
     * Copy the current screen contents into a hardware bitmap and return it.
     * Note: If you want to modify the Bitmap in software, you will need to copy the Bitmap into
     * a software Bitmap using {@link Bitmap#copy(Bitmap.Config, boolean)}
     *
     * CAVEAT: Versions of screenshot that return a {@link Bitmap} can be extremely slow; avoid use
     * unless absolutely necessary; prefer the versions that use a {@link HardwareBuffer} such as
     * {@link SurfaceControl#screenshotToBuffer(IBinder, Rect, int, int, boolean, int)}.
     *
     * @see SurfaceControl#screenshotToBuffer(IBinder, Rect, int, int, boolean, int)}
     * @hide
     */
    @UnsupportedAppUsage
    public static Bitmap screenshot(Rect sourceCrop, int width, int height,
            boolean useIdentityTransform, int rotation) {
        // TODO: should take the display as a parameter
        final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
        if (displayToken == null) {
            Log.w(TAG, "Failed to take screenshot because internal display is disconnected");
            return null;
        }

        if (rotation == ROTATION_90 || rotation == ROTATION_270) {
            rotation = (rotation == ROTATION_90) ? ROTATION_270 : ROTATION_90;
        }

        SurfaceControl.rotateCropForSF(sourceCrop, rotation);
        final ScreenshotHardwareBuffer buffer = screenshotToBuffer(displayToken, sourceCrop, width,
                height, useIdentityTransform, rotation);

        if (buffer == null) {
            Log.w(TAG, "Failed to take screenshot");
            return null;
        }
        return Bitmap.wrapHardwareBuffer(buffer.getHardwareBuffer(), buffer.getColorSpace());
    }

    /**
     * Captures all the surfaces in a display and returns a {@link HardwareBuffer} with the content.
     *
     * @param display              The display to take the screenshot of.
     * @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; caller may pass in 0 if no scaling is
     *                             desired.
     * @param height               The desired height of the returned bitmap; the raw screen will
     *                             be scaled down to this size; caller may pass in 0 if no scaling
     *                             is desired.
     * @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 HardwareBuffer that contains the captured content.
     * @hide
     */
    public static ScreenshotHardwareBuffer screenshotToBuffer(IBinder display, Rect sourceCrop,
            int width, int height, boolean useIdentityTransform, int rotation) {
        if (display == null) {
            throw new IllegalArgumentException("displayToken must not be null");
        }

        DisplayCaptureArgs captureArgs = new DisplayCaptureArgs.Builder(display)
                .setSourceCrop(sourceCrop)
                .setSize(width, height)
                .setUseIdentityTransform(useIdentityTransform)
                .setRotation(rotation)
                .build();

        return nativeCaptureDisplay(captureArgs);
    }

    /**
     * Like screenshotToBuffer, but if the caller is AID_SYSTEM, allows
     * for the capture of secure layers. This is used for the screen rotation
     * animation where the system server takes screenshots but does
     * not persist them or allow them to leave the server. However in other
     * cases in the system server, we mostly want to omit secure layers
     * like when we take a screenshot on behalf of the assistant.
     * Captures all the surfaces in a display and returns a {@link ScreenshotHardwareBuffer} with
     * the content.
     *
     * @hide
     */
    public static ScreenshotHardwareBuffer screenshotToBufferWithSecureLayersUnsafe(IBinder display,
            Rect sourceCrop, int width, int height, boolean useIdentityTransform,
            int rotation) {
        if (display == null) {
            throw new IllegalArgumentException("displayToken must not be null");
        }

        DisplayCaptureArgs captureArgs = new DisplayCaptureArgs.Builder(display)
                .setSourceCrop(sourceCrop)
                .setSize(width, height)
                .setUseIdentityTransform(useIdentityTransform)
                .setRotation(rotation)
                .setCaptureSecureLayers(true)
                .build();

    public static ScreenshotHardwareBuffer captureDisplay(DisplayCaptureArgs captureArgs) {
        return nativeCaptureDisplay(captureArgs);
    }

    private static void rotateCropForSF(Rect crop, int rot) {
        if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
            int tmp = crop.top;
            crop.top = crop.left;
            crop.left = tmp;
            tmp = crop.right;
            crop.right = crop.bottom;
            crop.bottom = tmp;
        }
    }

    /**
     * Captures a layer and its children and returns a {@link HardwareBuffer} with the content.
     *
+0 −5
Original line number Diff line number Diff line
@@ -116,7 +116,6 @@ static struct {
    jfieldID width;
    jfieldID height;
    jfieldID useIdentityTransform;
    jfieldID rotation;
} gDisplayCaptureArgsClassInfo;

static struct {
@@ -325,8 +324,6 @@ static DisplayCaptureArgs displayCaptureArgsFromObject(JNIEnv* env,
    captureArgs.useIdentityTransform =
            env->GetBooleanField(displayCaptureArgsObject,
                                 gDisplayCaptureArgsClassInfo.useIdentityTransform);
    captureArgs.rotation = ui::toRotation(
            env->GetIntField(displayCaptureArgsObject, gDisplayCaptureArgsClassInfo.rotation));
    return captureArgs;
}

@@ -1848,8 +1845,6 @@ int register_android_view_SurfaceControl(JNIEnv* env)
            GetFieldIDOrDie(env, displayCaptureArgsClazz, "mHeight", "I");
    gDisplayCaptureArgsClassInfo.useIdentityTransform =
            GetFieldIDOrDie(env, displayCaptureArgsClazz, "mUseIdentityTransform", "Z");
    gDisplayCaptureArgsClassInfo.rotation =
            GetFieldIDOrDie(env, displayCaptureArgsClazz, "mRotation", "I");

    jclass layerCaptureArgsClazz =
            FindClassOrDie(env, "android/view/SurfaceControl$LayerCaptureArgs");
Loading