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

Commit 979600dc authored by Jacky Kao's avatar Jacky Kao
Browse files

Add a rate limitation of takeScreenshot() API

Adding the rate limitation of takeScreenshot() API is one time
per second to avoid the system health impact.

Bug: 149968870
Test: a11y CTS & unit tests
Change-Id: Iaedafda9b9a5a56941952df54e62d76a92936b1d
parent cb50e94e
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -48,6 +48,10 @@ package android.accessibilityservice {
    ctor public AccessibilityGestureEvent(int, int);
  }

  public abstract class AccessibilityService extends android.app.Service {
    field public static final int ACCESSIBILITY_TAKE_SCREENSHOT_REQUEST_INTERVAL_TIMES_MS = 1000; // 0x3e8
  }

}

package android.animation {
+24 −20
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.TestApi;
import android.app.Service;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -572,6 +573,26 @@ public abstract class AccessibilityService extends Service {
     */
    public static final int SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN = 0x40000000;

    /**
     * The interval time of calling
     * {@link AccessibilityService#takeScreenshot(int, Executor, Consumer)} API.
     * @hide
     */
    @TestApi
    public static final int ACCESSIBILITY_TAKE_SCREENSHOT_REQUEST_INTERVAL_TIMES_MS = 1000;

    /** @hide */
    public static final String KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER =
            "screenshot_hardwareBuffer";

    /** @hide */
    public static final String KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE =
            "screenshot_colorSpace";

    /** @hide */
    public static final String KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP =
            "screenshot_timestamp";

    private int mConnectionId = AccessibilityInteractionClient.NO_ID;

    @UnsupportedAppUsage
@@ -597,17 +618,6 @@ public abstract class AccessibilityService extends Service {

    private FingerprintGestureController mFingerprintGestureController;

    /** @hide */
    public static final String KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER =
            "screenshot_hardwareBuffer";

    /** @hide */
    public static final String KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE =
            "screenshot_colorSpace";

    /** @hide */
    public static final String KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP =
            "screenshot_timestamp";

    /**
     * Callback for {@link android.view.accessibility.AccessibilityEvent}s.
@@ -1926,10 +1936,9 @@ public abstract class AccessibilityService extends Service {
     *                  default display.
     * @param executor Executor on which to run the callback.
     * @param callback The callback invoked when the taking screenshot is done.
     *                 The {@link AccessibilityService.ScreenshotResult} will be null for an
     *                 invalid display.
     *
     * @return {@code true} if the taking screenshot accepted, {@code false} if not.
     * @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.
     */
    public boolean takeScreenshot(int displayId, @NonNull @CallbackExecutor Executor executor,
            @NonNull Consumer<ScreenshotResult> callback) {
@@ -1942,11 +1951,7 @@ public abstract class AccessibilityService extends Service {
            return false;
        }
        try {
            connection.takeScreenshot(displayId, new RemoteCallback((result) -> {
                if (result == null) {
                    sendScreenshotResult(executor, callback, null);
                    return;
                }
            return connection.takeScreenshot(displayId, new RemoteCallback((result) -> {
                final HardwareBuffer hardwareBuffer =
                        result.getParcelable(KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER);
                final ParcelableColorSpace colorSpace =
@@ -1959,7 +1964,6 @@ public abstract class AccessibilityService extends Service {
        } catch (RemoteException re) {
            throw new RuntimeException(re);
        }
        return true;
    }

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

    int getWindowIdForLeashToken(IBinder token);

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

    void setGestureDetectionPassthroughRegion(int displayId, in Region region);

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

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

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

+17 −17
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;

import android.accessibilityservice.AccessibilityGestureEvent;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.accessibilityservice.IAccessibilityServiceConnection;
@@ -177,6 +178,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ

    final SparseArray<IBinder> mOverlayWindowTokens = new SparseArray();

    /** The timestamp of requesting to take screenshot in milliseconds */
    private long mRequestTakeScreenshotTimestampMs;

    public interface SystemSupport {
        /**
@@ -974,44 +977,39 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
    }

    @Override
    public void takeScreenshot(int displayId, RemoteCallback callback) {
    public boolean 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;
        }
        mRequestTakeScreenshotTimestampMs = currentTimestamp;

        synchronized (mLock) {
            if (!hasRightsToCurrentUserLocked()) {
                sendScreenshotResult(true, null, callback);
                return;
                return false;
            }

            if (!mSecurityPolicy.canTakeScreenshotLocked(this)) {
                sendScreenshotResult(true, null, callback);
                throw new SecurityException("Services don't have the capability of taking"
                        + " the screenshot.");
            }
        }

        if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
            sendScreenshotResult(true, null, callback);
            return;
            return false;
        }

        final Display display = DisplayManagerGlobal.getInstance()
                .getRealDisplay(displayId);
        if (display == null) {
            sendScreenshotResult(true, null, callback);
            return;
        }

        sendScreenshotResult(false, display, callback);
            return false;
        }

    private void sendScreenshotResult(boolean noResult, Display display, RemoteCallback callback) {
        final boolean noScreenshot = noResult;
        final long identity = Binder.clearCallingIdentity();
        try {
            mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> {
                if (noScreenshot) {
                    callback.sendResult(null);
                    return;
                }
                final Point displaySize = new Point();
                // TODO (b/145893483): calling new API with the display as a parameter
                // when surface control supported.
@@ -1041,6 +1039,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
        } finally {
            Binder.restoreCallingIdentity(identity);
        }

        return true;
    }

    @Override
Loading