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

Commit 658dd804 authored by Jacky Kao's avatar Jacky Kao Committed by Android (Google) Code Review
Browse files

Merge "Reply error through callback for takeScreenshot()" into rvc-dev

parents 707263d9 6c8bdbb5
Loading
Loading
Loading
Loading
+10 −1
Original line number Diff line number Diff line
@@ -2879,7 +2879,11 @@ package android.accessibilityservice {
    method public void setGestureDetectionPassthroughRegion(int, @NonNull android.graphics.Region);
    method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo);
    method public void setTouchExplorationPassthroughRegion(int, @NonNull android.graphics.Region);
    method public boolean takeScreenshot(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.accessibilityservice.AccessibilityService.ScreenshotResult>);
    method public void takeScreenshot(int, @NonNull java.util.concurrent.Executor, @NonNull android.accessibilityservice.AccessibilityService.TakeScreenshotCallback);
    field public static final int ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR = 1; // 0x1
    field public static final int ERROR_TAKE_SCREENSHOT_INTERVAL_TIME_SHORT = 3; // 0x3
    field public static final int ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY = 4; // 0x4
    field public static final int ERROR_TAKE_SCREENSHOT_NO_ACCESSIBILITY_ACCESS = 2; // 0x2
    field public static final int GESTURE_2_FINGER_DOUBLE_TAP = 20; // 0x14
    field public static final int GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD = 40; // 0x28
    field public static final int GESTURE_2_FINGER_SINGLE_TAP = 19; // 0x13
@@ -2980,6 +2984,11 @@ package android.accessibilityservice {
    method public void onShowModeChanged(@NonNull android.accessibilityservice.AccessibilityService.SoftKeyboardController, int);
  }
  public static interface AccessibilityService.TakeScreenshotCallback {
    method public void onFailure(int);
    method public void onSuccess(@NonNull android.accessibilityservice.AccessibilityService.ScreenshotResult);
  }
  public class AccessibilityServiceInfo implements android.os.Parcelable {
    ctor public AccessibilityServiceInfo();
    method public static String capabilityToString(int);
+84 −19
Original line number Diff line number Diff line
@@ -33,7 +33,6 @@ import android.graphics.ColorSpace;
import android.graphics.ParcelableColorSpace;
import android.graphics.Region;
import android.hardware.HardwareBuffer;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
@@ -575,6 +574,46 @@ public abstract class AccessibilityService extends Service {
     */
    public static final int SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN = 0x40000000;

    /**
     * Annotations for error codes of taking screenshot.
     * @hide
     */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(prefix = { "TAKE_SCREENSHOT_" }, value = {
            ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR,
            ERROR_TAKE_SCREENSHOT_NO_ACCESSIBILITY_ACCESS,
            ERROR_TAKE_SCREENSHOT_INTERVAL_TIME_SHORT,
            ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY
    })
    public @interface ScreenshotErrorCode {}

    /**
     * The status of taking screenshot is success.
     * @hide
     */
    public static final int TAKE_SCREENSHOT_SUCCESS = 0;

    /**
     * The status of taking screenshot is failure and the reason is internal error.
     */
    public static final int ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR = 1;

    /**
     * The status of taking screenshot is failure and the reason is no accessibility access.
     */
    public static final int ERROR_TAKE_SCREENSHOT_NO_ACCESSIBILITY_ACCESS = 2;

    /**
     * The status of taking screenshot is failure and the reason is that too little time has
     * elapsed since the last screenshot.
     */
    public static final int ERROR_TAKE_SCREENSHOT_INTERVAL_TIME_SHORT = 3;

    /**
     * The status of taking screenshot is failure and the reason is invalid display Id.
     */
    public static final int ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY = 4;

    /**
     * The interval time of calling
     * {@link AccessibilityService#takeScreenshot(int, Executor, Consumer)} API.
@@ -583,6 +622,10 @@ public abstract class AccessibilityService extends Service {
    @TestApi
    public static final int ACCESSIBILITY_TAKE_SCREENSHOT_REQUEST_INTERVAL_TIMES_MS = 1000;

    /** @hide */
    public static final String KEY_ACCESSIBILITY_SCREENSHOT_STATUS =
            "screenshot_status";

    /** @hide */
    public static final String KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER =
            "screenshot_hardwareBuffer";
@@ -1945,14 +1988,13 @@ public abstract class AccessibilityService extends Service {
     * @param displayId The logic display id, must be {@link Display#DEFAULT_DISPLAY} for
     *                  default display.
     * @param executor Executor on which to run the callback.
     * @param callback The callback invoked when the taking screenshot is done.
     * @param callback The callback invoked when taking screenshot has succeeded or failed.
     *                 See {@link TakeScreenshotCallback} for details.
     *
     * @return {@code true} if the taking screenshot accepted, {@code false} if too little time
     * has elapsed since the last screenshot, invalid display or internal errors.
     * @throws IllegalArgumentException if displayId is not {@link Display#DEFAULT_DISPLAY}.
     */
    public boolean takeScreenshot(int displayId, @NonNull @CallbackExecutor Executor executor,
            @NonNull Consumer<ScreenshotResult> callback) {
    public void takeScreenshot(int displayId, @NonNull @CallbackExecutor Executor executor,
            @NonNull TakeScreenshotCallback callback) {
        Preconditions.checkNotNull(executor, "executor cannot be null");
        Preconditions.checkNotNull(callback, "callback cannot be null");

@@ -1964,18 +2006,24 @@ public abstract class AccessibilityService extends Service {
                AccessibilityInteractionClient.getInstance().getConnection(
                        mConnectionId);
        if (connection == null) {
            return false;
            sendScreenshotFailure(ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR, executor, callback);
            return;
        }
        try {
            return connection.takeScreenshot(displayId, new RemoteCallback((result) -> {
            connection.takeScreenshot(displayId, new RemoteCallback((result) -> {
                final int status = result.getInt(KEY_ACCESSIBILITY_SCREENSHOT_STATUS);
                if (status != TAKE_SCREENSHOT_SUCCESS) {
                    sendScreenshotFailure(status, executor, callback);
                    return;
                }
                final HardwareBuffer hardwareBuffer =
                        result.getParcelable(KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER);
                final ParcelableColorSpace colorSpace =
                        result.getParcelable(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE);
                ScreenshotResult screenshot = new ScreenshotResult(hardwareBuffer,
                final ScreenshotResult screenshot = new ScreenshotResult(hardwareBuffer,
                        colorSpace.getColorSpace(),
                        result.getLong(KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP));
                sendScreenshotResult(executor, callback, screenshot);
                sendScreenshotSuccess(screenshot, executor, callback);
            }));
        } catch (RemoteException re) {
            throw new RuntimeException(re);
@@ -2374,15 +2422,32 @@ public abstract class AccessibilityService extends Service {
        }
    }

    private void sendScreenshotResult(Executor executor, Consumer<ScreenshotResult> callback,
            ScreenshotResult screenshot) {
        final ScreenshotResult result = screenshot;
        final long identity = Binder.clearCallingIdentity();
        try {
            executor.execute(() -> callback.accept(result));
        } finally {
            Binder.restoreCallingIdentity(identity);
    private void sendScreenshotSuccess(ScreenshotResult screenshot, Executor executor,
            TakeScreenshotCallback callback) {
        executor.execute(() -> callback.onSuccess(screenshot));
    }

    private void sendScreenshotFailure(@ScreenshotErrorCode int errorCode, Executor executor,
            TakeScreenshotCallback callback) {
        executor.execute(() -> callback.onFailure(errorCode));
    }

    /**
     * Interface used to report status of taking screenshot.
     */
    public interface TakeScreenshotCallback {
        /** Called when taking screenshot has completed successfully.
         *
         * @param screenshot The content of screenshot.
         */
        void onSuccess(@NonNull ScreenshotResult screenshot);

        /** Called when taking screenshot has failed. {@code errorCode} will identify the
         * reason of failure.
         *
         * @param errorCode The error code of this operation.
         */
        void onFailure(@ScreenshotErrorCode int errorCode);
    }

    /**
+1 −1
Original line number Diff line number Diff line
@@ -110,7 +110,7 @@ interface IAccessibilityServiceConnection {

    int getWindowIdForLeashToken(IBinder token);

    boolean takeScreenshot(int displayId, in RemoteCallback callback);
    void takeScreenshot(int displayId, in RemoteCallback callback);

    void setGestureDetectionPassthroughRegion(int displayId, in Region region);

+1 −3
Original line number Diff line number Diff line
@@ -156,9 +156,7 @@ public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceCon
        return -1;
    }

    public boolean takeScreenshot(int displayId, RemoteCallback callback) {
        return false;
    }
    public void takeScreenshot(int displayId, RemoteCallback callback) {}

    public void setTouchExplorationPassthroughRegion(int displayId, Region region) {}

+48 −20
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server.accessibility;

import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE;
import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER;
import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_STATUS;
import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP;
import static android.accessibilityservice.AccessibilityServiceInfo.DEFAULT;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
@@ -66,6 +67,7 @@ import android.view.Display;
import android.view.KeyEvent;
import android.view.MagnificationSpec;
import android.view.SurfaceControl;
import android.view.SurfaceControl.ScreenshotGraphicBuffer;
import android.view.View;
import android.view.WindowInfo;
import android.view.accessibility.AccessibilityCache;
@@ -977,18 +979,22 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
    }

    @Override
    public boolean takeScreenshot(int displayId, RemoteCallback callback) {
    public void takeScreenshot(int displayId, RemoteCallback callback) {
        final long currentTimestamp = SystemClock.uptimeMillis();
        if (mRequestTakeScreenshotTimestampMs != 0
                && (currentTimestamp - mRequestTakeScreenshotTimestampMs)
                <= AccessibilityService.ACCESSIBILITY_TAKE_SCREENSHOT_REQUEST_INTERVAL_TIMES_MS) {
            return false;
            sendScreenshotFailure(AccessibilityService.ERROR_TAKE_SCREENSHOT_INTERVAL_TIME_SHORT,
                    callback);
            return;
        }
        mRequestTakeScreenshotTimestampMs = currentTimestamp;

        synchronized (mLock) {
            if (!hasRightsToCurrentUserLocked()) {
                return false;
                sendScreenshotFailure(AccessibilityService.ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR,
                        callback);
                return;
            }

            if (!mSecurityPolicy.canTakeScreenshotLocked(this)) {
@@ -998,18 +1004,24 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
        }

        if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
            return false;
            sendScreenshotFailure(
                    AccessibilityService.ERROR_TAKE_SCREENSHOT_NO_ACCESSIBILITY_ACCESS,
                    callback);
            return;
        }

        final Display display = DisplayManagerGlobal.getInstance()
                .getRealDisplay(displayId);
        if (display == null) {
            return false;
            sendScreenshotFailure(AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY,
                    callback);
            return;
        }

        final long identity = Binder.clearCallingIdentity();
        try {
            mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> {
        sendScreenshotSuccess(display, callback);
    }

    private ScreenshotGraphicBuffer takeScreenshotBuffer(Display display) {
        final Point displaySize = new Point();
        // TODO (b/145893483): calling new API with the display as a parameter
        // when surface control supported.
@@ -1018,23 +1030,31 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
        final int rotation = display.getRotation();
        display.getRealSize(displaySize);

                final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer =
                        SurfaceControl.screenshotToBufferWithSecureLayersUnsafe(token, crop,
                                displaySize.x, displaySize.y, false,
                                rotation);
        return SurfaceControl.screenshotToBuffer(token, crop, displaySize.x, displaySize.y,
                false, rotation);
    }

    private void sendScreenshotSuccess(Display display, RemoteCallback callback) {
        final long identity = Binder.clearCallingIdentity();
        try {
            mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> {
                final ScreenshotGraphicBuffer screenshotBuffer = takeScreenshotBuffer(display);
                final GraphicBuffer graphicBuffer = screenshotBuffer.getGraphicBuffer();
                try (HardwareBuffer hardwareBuffer =
                             HardwareBuffer.createFromGraphicBuffer(graphicBuffer)) {
                    final ParcelableColorSpace colorSpace =
                            new ParcelableColorSpace(screenshotBuffer.getColorSpace());

                    // Send back the result.
                    final Bundle payload = new Bundle();
                    payload.putInt(KEY_ACCESSIBILITY_SCREENSHOT_STATUS,
                            AccessibilityService.TAKE_SCREENSHOT_SUCCESS);
                    payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER,
                            hardwareBuffer);
                    payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE, colorSpace);
                    payload.putLong(KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP,
                            SystemClock.uptimeMillis());

                    // Send back the result.
                    callback.sendResult(payload);
                    hardwareBuffer.close();
                }
@@ -1042,8 +1062,16 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
    }

        return true;
    private void sendScreenshotFailure(@AccessibilityService.ScreenshotErrorCode int errorCode,
            RemoteCallback callback) {
        mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> {
            final Bundle payload = new Bundle();
            payload.putInt(KEY_ACCESSIBILITY_SCREENSHOT_STATUS, errorCode);
            // Send back the result.
            callback.sendResult(payload);
        }, null).recycleOnUse());
    }

    @Override
Loading