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

Commit 09789e41 authored by Jacky Kao's avatar Jacky Kao
Browse files

Implementing new API : takeScreenshot()

Implementing the new API for A11y services to take the screenshot
of the specified display.
Bitmap takeScreenshot(int displayId)
1. The main codes is moved from the UiAutomationConnection class and
let the UiAutomation change to use this API.
2. Add a capability at metadata to check whether the A11y services could use this
API or not.
3. This API is a async one for A11y services, but is a sync one for
UiAutomation.

Bug: 10931661
Test: a11y CTS & unit tests
Change-Id: I478bd93c60d4742bef20ae0e423ca9de1bda55d2
parent 7fd8e937
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -386,6 +386,7 @@ package android {
    field public static final int canRequestFingerprintGestures = 16844109; // 0x101054d
    field public static final int canRequestTouchExplorationMode = 16843735; // 0x10103d7
    field public static final int canRetrieveWindowContent = 16843653; // 0x1010385
    field public static final int canTakeScreenshot = 16844304; // 0x1010610
    field public static final int candidatesTextStyleSpans = 16843312; // 0x1010230
    field public static final int cantSaveState = 16844142; // 0x101056e
    field @Deprecated public static final int capitalize = 16843113; // 0x1010169
@@ -2864,6 +2865,7 @@ package android.accessibilityservice {
    method protected void onServiceConnected();
    method public final boolean performGlobalAction(int);
    method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo);
    method public boolean takeScreenshot(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.graphics.Bitmap>);
    field public static final int GESTURE_SWIPE_DOWN = 2; // 0x2
    field public static final int GESTURE_SWIPE_DOWN_AND_LEFT = 15; // 0xf
    field public static final int GESTURE_SWIPE_DOWN_AND_RIGHT = 16; // 0x10
@@ -2960,6 +2962,7 @@ package android.accessibilityservice {
    field public static final int CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES = 64; // 0x40
    field public static final int CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 2; // 0x2
    field public static final int CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT = 1; // 0x1
    field public static final int CAPABILITY_CAN_TAKE_SCREENSHOT = 128; // 0x80
    field @NonNull public static final android.os.Parcelable.Creator<android.accessibilityservice.AccessibilityServiceInfo> CREATOR;
    field public static final int DEFAULT = 1; // 0x1
    field public static final int FEEDBACK_ALL_MASK = -1; // 0xffffffff
+55 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package android.accessibilityservice;

import android.accessibilityservice.GestureDescription.MotionEventGenerator;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -26,12 +27,15 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ParceledListSlice;
import android.graphics.Bitmap;
import android.graphics.Region;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Log;
@@ -48,10 +52,13 @@ import android.view.accessibility.AccessibilityWindowInfo;

import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.Preconditions;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.function.Consumer;

/**
 * Accessibility services should only be used to assist users with disabilities in using
@@ -483,6 +490,9 @@ public abstract class AccessibilityService extends Service {

    private FingerprintGestureController mFingerprintGestureController;

    /** @hide */
    public static final String KEY_ACCESSIBILITY_SCREENSHOT = "screenshot";

    /**
     * Callback for {@link android.view.accessibility.AccessibilityEvent}s.
     *
@@ -1760,6 +1770,51 @@ public abstract class AccessibilityService extends Service {
        return super.getSystemService(name);
    }

    /**
     * Takes a screenshot of the specified display and returns it by {@link Bitmap.Config#HARDWARE}
     * format.
     * <p>
     * <strong>Note:</strong> In order to take screenshot your service has
     * to declare the capability to take screenshot by setting the
     * {@link android.R.styleable#AccessibilityService_canTakeScreenshot}
     * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
     * Besides, This API is only supported for default display now
     * {@link Display#DEFAULT_DISPLAY}.
     * </p>
     *
     * @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.
     *
     * @return {@code true} if the taking screenshot accepted, {@code false} if not.
     */
    public boolean takeScreenshot(int displayId, @NonNull @CallbackExecutor Executor executor,
            @NonNull Consumer<Bitmap> callback) {
        Preconditions.checkNotNull(executor, "executor cannot be null");
        Preconditions.checkNotNull(callback, "callback cannot be null");
        final IAccessibilityServiceConnection connection =
                AccessibilityInteractionClient.getInstance().getConnection(
                        mConnectionId);
        if (connection == null) {
            return false;
        }
        try {
            connection.takeScreenshotWithCallback(displayId, new RemoteCallback((result) -> {
                final Bitmap screenshot = result.getParcelable(KEY_ACCESSIBILITY_SCREENSHOT);
                final long identity = Binder.clearCallingIdentity();
                try {
                    executor.execute(() -> callback.accept(screenshot));
                } finally {
                    Binder.restoreCallingIdentity(identity);
                }
            }));
        } catch (RemoteException re) {
            throw new RuntimeException(re);
        }
        return true;
    }

    /**
     * Implement to return the implementation of the internal accessibility
     * service interface.
+19 −0
Original line number Diff line number Diff line
@@ -86,6 +86,7 @@ import java.util.List;
 * @attr ref android.R.styleable#AccessibilityService_settingsActivity
 * @attr ref android.R.styleable#AccessibilityService_nonInteractiveUiTimeout
 * @attr ref android.R.styleable#AccessibilityService_interactiveUiTimeout
 * @attr ref android.R.styleable#AccessibilityService_canTakeScreenshot
 * @see AccessibilityService
 * @see android.view.accessibility.AccessibilityEvent
 * @see android.view.accessibility.AccessibilityManager
@@ -136,6 +137,12 @@ public class AccessibilityServiceInfo implements Parcelable {
     */
    public static final int CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES = 0x00000040;

    /**
     * Capability: This accessibility service can take screenshot.
     * @see android.R.styleable#AccessibilityService_canTakeScreenshot
     */
    public static final int CAPABILITY_CAN_TAKE_SCREENSHOT = 0x00000080;

    private static SparseArray<CapabilityInfo> sAvailableCapabilityInfos;

    /**
@@ -625,6 +632,10 @@ public class AccessibilityServiceInfo implements Parcelable {
                    .AccessibilityService_canRequestFingerprintGestures, false)) {
                mCapabilities |= CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES;
            }
            if (asAttributes.getBoolean(com.android.internal.R.styleable
                    .AccessibilityService_canTakeScreenshot, false)) {
                mCapabilities |= CAPABILITY_CAN_TAKE_SCREENSHOT;
            }
            TypedValue peekedValue = asAttributes.peekValue(
                    com.android.internal.R.styleable.AccessibilityService_description);
            if (peekedValue != null) {
@@ -794,6 +805,7 @@ public class AccessibilityServiceInfo implements Parcelable {
     * @see #CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS
     * @see #CAPABILITY_CAN_CONTROL_MAGNIFICATION
     * @see #CAPABILITY_CAN_PERFORM_GESTURES
     * @see #CAPABILITY_CAN_TAKE_SCREENSHOT
     */
    public int getCapabilities() {
        return mCapabilities;
@@ -810,6 +822,7 @@ public class AccessibilityServiceInfo implements Parcelable {
     * @see #CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS
     * @see #CAPABILITY_CAN_CONTROL_MAGNIFICATION
     * @see #CAPABILITY_CAN_PERFORM_GESTURES
     * @see #CAPABILITY_CAN_TAKE_SCREENSHOT
     *
     * @hide
     */
@@ -1253,6 +1266,8 @@ public class AccessibilityServiceInfo implements Parcelable {
                return "CAPABILITY_CAN_PERFORM_GESTURES";
            case CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES:
                return "CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES";
            case CAPABILITY_CAN_TAKE_SCREENSHOT:
                return "CAPABILITY_CAN_TAKE_SCREENSHOT";
            default:
                return "UNKNOWN";
        }
@@ -1314,6 +1329,10 @@ public class AccessibilityServiceInfo implements Parcelable {
                    new CapabilityInfo(CAPABILITY_CAN_PERFORM_GESTURES,
                            R.string.capability_title_canPerformGestures,
                            R.string.capability_desc_canPerformGestures));
            sAvailableCapabilityInfos.put(CAPABILITY_CAN_TAKE_SCREENSHOT,
                    new CapabilityInfo(CAPABILITY_CAN_TAKE_SCREENSHOT,
                            R.string.capability_title_canTakeScreenshot,
                            R.string.capability_desc_canTakeScreenshot));
            if ((context == null) || fingerprintAvailable(context)) {
                sAvailableCapabilityInfos.put(CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES,
                        new CapabilityInfo(CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES,
+6 −0
Original line number Diff line number Diff line
@@ -18,8 +18,10 @@ package android.accessibilityservice;

import android.accessibilityservice.AccessibilityServiceInfo;
import android.content.pm.ParceledListSlice;
import android.graphics.Bitmap;
import android.graphics.Region;
import android.os.Bundle;
import android.os.RemoteCallback;
import android.view.MagnificationSpec;
import android.view.MotionEvent;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -104,4 +106,8 @@ interface IAccessibilityServiceConnection {
    IBinder getOverlayWindowToken(int displayid);

    int getWindowIdForLeashToken(IBinder token);

    Bitmap takeScreenshot(int displayId);

    void takeScreenshotWithCallback(int displayId, in RemoteCallback callback);
}
+0 −1
Original line number Diff line number Diff line
@@ -39,7 +39,6 @@ interface IUiAutomationConnection {
    boolean injectInputEvent(in InputEvent event, boolean sync);
    void syncInputTransactions();
    boolean setRotation(int rotation);
    Bitmap takeScreenshot(in Rect crop, int rotation);
    boolean clearWindowContentFrameStats(int windowId);
    WindowContentFrameStats getWindowContentFrameStats(int windowId);
    void clearWindowAnimationFrameStats();
Loading