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

Commit fb6396d1 authored by Jacky Kao's avatar Jacky Kao Committed by Automerger Merge Worker
Browse files

Merge "Reply error through callback for takeScreenshot()" into rvc-dev am: 658dd804

Change-Id: Ieeaf65810b4bcbccbf43d9d0207000b00f4c9481
parents 1924ad61 658dd804
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