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

Commit 1a1a8d4c authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "UiAutomation supports new flag, FLAG_DO_NOT_USE_ACCESSIBILITY"

parents 56a78405 40c9baad
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -6532,6 +6532,7 @@ package android.app {
    method public android.graphics.Bitmap takeScreenshot();
    method public void waitForIdle(long, long) throws java.util.concurrent.TimeoutException;
    field public static final int FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES = 1; // 0x1
    field public static final int FLAG_DONT_USE_ACCESSIBILITY = 2; // 0x2
    field public static final int ROTATION_FREEZE_0 = 0; // 0x0
    field public static final int ROTATION_FREEZE_180 = 2; // 0x2
    field public static final int ROTATION_FREEZE_270 = 3; // 0x3
+4 −2
Original line number Diff line number Diff line
@@ -97,7 +97,8 @@ public class Instrumentation {
     * @hide
     */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef({0, UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES})
    @IntDef({0, UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES,
            UiAutomation.FLAG_DONT_USE_ACCESSIBILITY})
    public @interface UiAutomationFlags {};


@@ -2170,7 +2171,8 @@ public class Instrumentation {
     * </p>
     *
     * @param flags The flags to be passed to the UiAutomation, for example
     *        {@link UiAutomation#FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES}.
     *        {@link UiAutomation#FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES},
     *        {@link UiAutomation#FLAG_DONT_USE_ACCESSIBILITY}.
     *
     * @return The UI automation instance.
     *
+77 −52
Original line number Diff line number Diff line
@@ -56,6 +56,7 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.function.pooled.PooledLambda;

import libcore.io.IoUtils;
@@ -142,13 +143,22 @@ public final class UiAutomation {
    }

    /**
     * UiAutomation supresses accessibility services by default. This flag specifies that
     * UiAutomation suppresses accessibility services by default. This flag specifies that
     * existing accessibility services should continue to run, and that new ones may start.
     * This flag is set when obtaining the UiAutomation from
     * {@link Instrumentation#getUiAutomation(int)}.
     */
    public static final int FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES = 0x00000001;

    /**
     * UiAutomation uses the accessibility subsystem by default. This flag provides an option to
     * eliminate the overhead of engaging the accessibility subsystem for tests that do not need to
     * interact with the user interface. Setting this flag disables methods that rely on
     * accessibility. This flag is set when obtaining the UiAutomation from
     * {@link Instrumentation#getUiAutomation(int)}.
     */
    public static final int FLAG_DONT_USE_ACCESSIBILITY = 0x00000002;

    private final Object mLock = new Object();

    private final ArrayList<AccessibilityEvent> mEventQueue = new ArrayList<AccessibilityEvent>();
@@ -267,11 +277,14 @@ public final class UiAutomation {
    /**
     * Connects this UiAutomation to the accessibility introspection APIs.
     *
     * @param flags Any flags to apply to the automation as it gets connected
     * @param flags Any flags to apply to the automation as it gets connected while
     *              {@link UiAutomation#FLAG_DONT_USE_ACCESSIBILITY} would keep the
     *              connection disconnected and not to register UiAutomation service.
     * @param timeoutMillis The wait timeout in milliseconds
     *
     * @throws IllegalStateException If the connection to the accessibility subsystem is already
     *            established.
     * @throws TimeoutException If not connected within the timeout
     *
     * @hide
     */
    public void connectWithTimeout(int flags, long timeoutMillis) throws TimeoutException {
@@ -292,6 +305,12 @@ public final class UiAutomation {
            // Calling out without a lock held.
            mUiAutomationConnection.connect(mClient, flags);
            mFlags = flags;
            // If UiAutomation is not allowed to use the accessibility subsystem, the
            // connection state should keep disconnected and not to start the client connection.
            if (!useAccessibility()) {
                mConnectionState = ConnectionState.DISCONNECTED;
                return;
            }
        } catch (RemoteException re) {
            throw new RuntimeException("Error while connecting " + this, re);
        }
@@ -340,7 +359,7 @@ public final class UiAutomation {
                throw new IllegalStateException(
                        "Cannot call disconnect() while connecting " + this);
            }
            if (mConnectionState == ConnectionState.DISCONNECTED) {
            if (useAccessibility() && mConnectionState == ConnectionState.DISCONNECTED) {
                return;
            }
            mConnectionState = ConnectionState.DISCONNECTED;
@@ -354,10 +373,12 @@ public final class UiAutomation {
        } catch (RemoteException re) {
            throw new RuntimeException("Error while disconnecting " + this, re);
        } finally {
            if (mRemoteCallbackThread != null) {
                mRemoteCallbackThread.quit();
                mRemoteCallbackThread = null;
            }
        }
    }

    /**
     * The id of the {@link IAccessibilityInteractionConnection} for querying
@@ -389,9 +410,13 @@ public final class UiAutomation {
     * The callbacks are delivered on the main application thread.
     *
     * @param listener The callback.
     *
     * @throws IllegalStateException If the connection to the accessibility subsystem is not
     *            established.
     */
    public void setOnAccessibilityEventListener(OnAccessibilityEventListener listener) {
        synchronized (mLock) {
            throwIfNotConnectedLocked();
            mOnAccessibilityEventListener = listener;
        }
    }
@@ -423,9 +448,6 @@ public final class UiAutomation {
     * @see #dropShellPermissionIdentity()
     */
    public void adoptShellPermissionIdentity() {
        synchronized (mLock) {
            throwIfNotConnectedLocked();
        }
        try {
            // Calling out without a lock held.
            mUiAutomationConnection.adoptShellPermissionIdentity(Process.myUid(), null);
@@ -451,9 +473,6 @@ public final class UiAutomation {
     * @see #dropShellPermissionIdentity()
     */
    public void adoptShellPermissionIdentity(@Nullable String... permissions) {
        synchronized (mLock) {
            throwIfNotConnectedLocked();
        }
        try {
            // Calling out without a lock held.
            mUiAutomationConnection.adoptShellPermissionIdentity(Process.myUid(), permissions);
@@ -470,9 +489,6 @@ public final class UiAutomation {
     * @see #adoptShellPermissionIdentity()
     */
    public void dropShellPermissionIdentity() {
        synchronized (mLock) {
            throwIfNotConnectedLocked();
        }
        try {
            // Calling out without a lock held.
            mUiAutomationConnection.dropShellPermissionIdentity();
@@ -489,6 +505,8 @@ public final class UiAutomation {
     * @param action The action to perform.
     * @return Whether the action was successfully performed.
     *
     * @throws IllegalStateException If the connection to the accessibility subsystem is not
     *            established.
     * @see android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_BACK
     * @see android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_HOME
     * @see android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_NOTIFICATIONS
@@ -526,10 +544,15 @@ public final class UiAutomation {
     *              {@link AccessibilityNodeInfo#FOCUS_ACCESSIBILITY}.
     * @return The node info of the focused view or null.
     *
     * @throws IllegalStateException If the connection to the accessibility subsystem is not
     *            established.
     * @see AccessibilityNodeInfo#FOCUS_INPUT
     * @see AccessibilityNodeInfo#FOCUS_ACCESSIBILITY
     */
    public AccessibilityNodeInfo findFocus(int focus) {
        synchronized (mLock) {
            throwIfNotConnectedLocked();
        }
        return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId,
                AccessibilityWindowInfo.ANY_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID, focus);
    }
@@ -541,6 +564,8 @@ public final class UiAutomation {
     *
     * @return The accessibility service info.
     *
     * @throws IllegalStateException If the connection to the accessibility subsystem is not
     *            established.
     * @see AccessibilityServiceInfo
     */
    public final AccessibilityServiceInfo getServiceInfo() {
@@ -567,6 +592,8 @@ public final class UiAutomation {
     *
     * @param info The info.
     *
     * @throws IllegalStateException If the connection to the accessibility subsystem is not
     *            established.
     * @see AccessibilityServiceInfo
     */
    public final void setServiceInfo(AccessibilityServiceInfo info) {
@@ -602,6 +629,8 @@ public final class UiAutomation {
     * </p>
     *
     * @return The windows if there are windows such, otherwise an empty list.
     * @throws IllegalStateException If the connection to the accessibility subsystem is not
     *            established.
     */
    public List<AccessibilityWindowInfo> getWindows() {
        final int connectionId;
@@ -630,6 +659,8 @@ public final class UiAutomation {
     *
     * @return The windows of all displays if there are windows and the service is can retrieve
     *         them, otherwise an empty list. The key of SparseArray is display ID.
     * @throws IllegalStateException If the connection to the accessibility subsystem is not
     *            established.
     */
    @NonNull
    public SparseArray<List<AccessibilityWindowInfo>> getWindowsOnAllDisplays() {
@@ -647,6 +678,8 @@ public final class UiAutomation {
     * Gets the root {@link AccessibilityNodeInfo} in the active window.
     *
     * @return The root info.
     * @throws IllegalStateException If the connection to the accessibility subsystem is not
     *            established.
     */
    public AccessibilityNodeInfo getRootInActiveWindow() {
        final int connectionId;
@@ -664,14 +697,12 @@ public final class UiAutomation {
     * <p>
     * <strong>Note:</strong> It is caller's responsibility to recycle the event.
     * </p>
     *
     * @param event The event to inject.
     * @param sync Whether to inject the event synchronously.
     * @return Whether event injection succeeded.
     */
    public boolean injectInputEvent(InputEvent event, boolean sync) {
        synchronized (mLock) {
            throwIfNotConnectedLocked();
        }
        try {
            if (DEBUG) {
                Log.i(LOG_TAG, "Injecting: " + event + " sync: " + sync);
@@ -692,9 +723,6 @@ public final class UiAutomation {
     */
    @TestApi
    public void syncInputTransactions() {
        synchronized (mLock) {
            throwIfNotConnectedLocked();
        }
        try {
            // Calling out without a lock held.
            mUiAutomationConnection.syncInputTransactions();
@@ -720,9 +748,6 @@ public final class UiAutomation {
     * @see #ROTATION_UNFREEZE
     */
    public boolean setRotation(int rotation) {
        synchronized (mLock) {
            throwIfNotConnectedLocked();
        }
        switch (rotation) {
            case ROTATION_FREEZE_0:
            case ROTATION_FREEZE_90:
@@ -752,11 +777,14 @@ public final class UiAutomation {
     * <p>
     * <strong>Note:</strong> It is caller's responsibility to recycle the returned event.
     * </p>
     *
     * @param command The command to execute.
     * @param filter Filter that recognizes the expected event.
     * @param timeoutMillis The wait timeout in milliseconds.
     *
     * @throws TimeoutException If the expected event is not received within the timeout.
     * @throws IllegalStateException If the connection to the accessibility subsystem is not
     *            established.
     */
    public AccessibilityEvent executeAndWaitForEvent(Runnable command,
            AccessibilityEventFilter filter, long timeoutMillis) throws TimeoutException {
@@ -845,6 +873,8 @@ public final class UiAutomation {
     *
     * @throws TimeoutException If no idle state was detected within
     *            <code>globalTimeoutMillis.</code>
     * @throws IllegalStateException If the connection to the accessibility subsystem is not
     *            established.
     */
    public void waitForIdle(long idleTimeoutMillis, long globalTimeoutMillis)
            throws TimeoutException {
@@ -888,9 +918,6 @@ public final class UiAutomation {
     * @return The screenshot bitmap on success, null otherwise.
     */
    public Bitmap takeScreenshot() {
        synchronized (mLock) {
            throwIfNotConnectedLocked();
        }
        Display display = DisplayManagerGlobal.getInstance()
                .getRealDisplay(Display.DEFAULT_DISPLAY);
        Point displaySize = new Point();
@@ -927,9 +954,6 @@ public final class UiAutomation {
     * @see ActivityManager#isUserAMonkey()
     */
    public void setRunAsMonkey(boolean enable) {
        synchronized (mLock) {
            throwIfNotConnectedLocked();
        }
        try {
            ActivityManager.getService().setUserIsMonkey(enable);
        } catch (RemoteException re) {
@@ -946,6 +970,8 @@ public final class UiAutomation {
     * @return Whether the window is present and its frame statistics
     *         were cleared.
     *
     * @throws IllegalStateException If the connection to the accessibility subsystem is not
     *            established.
     * @see android.view.WindowContentFrameStats
     * @see #getWindowContentFrameStats(int)
     * @see #getWindows()
@@ -991,6 +1017,8 @@ public final class UiAutomation {
     * @param windowId The window id.
     * @return The window frame statistics, or null if the window is not present.
     *
     * @throws IllegalStateException If the connection to the accessibility subsystem is not
     *            established.
     * @see android.view.WindowContentFrameStats
     * @see #clearWindowContentFrameStats(int)
     * @see #getWindows()
@@ -1022,9 +1050,6 @@ public final class UiAutomation {
     * @see android.R.styleable#WindowAnimation
     */
    public void clearWindowAnimationFrameStats() {
        synchronized (mLock) {
            throwIfNotConnectedLocked();
        }
        try {
            if (DEBUG) {
                Log.i(LOG_TAG, "Clearing window animation frame stats");
@@ -1064,9 +1089,6 @@ public final class UiAutomation {
     * @see android.R.styleable#WindowAnimation
     */
    public WindowAnimationFrameStats getWindowAnimationFrameStats() {
        synchronized (mLock) {
            throwIfNotConnectedLocked();
        }
        try {
            if (DEBUG) {
                Log.i(LOG_TAG, "Getting window animation frame stats");
@@ -1081,6 +1103,7 @@ public final class UiAutomation {

    /**
     * Grants a runtime permission to a package.
     *
     * @param packageName The package to which to grant.
     * @param permission The permission to grant.
     * @throws SecurityException if unable to grant the permission.
@@ -1104,15 +1127,13 @@ public final class UiAutomation {

    /**
     * Grants a runtime permission to a package for a user.
     *
     * @param packageName The package to which to grant.
     * @param permission The permission to grant.
     * @throws SecurityException if unable to grant the permission.
     */
    public void grantRuntimePermissionAsUser(String packageName, String permission,
            UserHandle userHandle) {
        synchronized (mLock) {
            throwIfNotConnectedLocked();
        }
        try {
            if (DEBUG) {
                Log.i(LOG_TAG, "Granting runtime permission");
@@ -1127,6 +1148,7 @@ public final class UiAutomation {

    /**
     * Revokes a runtime permission from a package.
     *
     * @param packageName The package to which to grant.
     * @param permission The permission to grant.
     * @throws SecurityException if unable to revoke the permission.
@@ -1150,15 +1172,13 @@ public final class UiAutomation {

    /**
     * Revokes a runtime permission from a package.
     *
     * @param packageName The package to which to grant.
     * @param permission The permission to grant.
     * @throws SecurityException if unable to revoke the permission.
     */
    public void revokeRuntimePermissionAsUser(String packageName, String permission,
            UserHandle userHandle) {
        synchronized (mLock) {
            throwIfNotConnectedLocked();
        }
        try {
            if (DEBUG) {
                Log.i(LOG_TAG, "Revoking runtime permission");
@@ -1186,9 +1206,6 @@ public final class UiAutomation {
     * @see #adoptShellPermissionIdentity()
     */
    public ParcelFileDescriptor executeShellCommand(String command) {
        synchronized (mLock) {
            throwIfNotConnectedLocked();
        }
        warnIfBetterCommand(command);

        ParcelFileDescriptor source = null;
@@ -1229,9 +1246,6 @@ public final class UiAutomation {
     */
    @TestApi
    public ParcelFileDescriptor[] executeShellCommandRw(String command) {
        synchronized (mLock) {
            throwIfNotConnectedLocked();
        }
        warnIfBetterCommand(command);

        ParcelFileDescriptor source_read = null;
@@ -1276,15 +1290,21 @@ public final class UiAutomation {
        return stringBuilder.toString();
    }

    @GuardedBy("mLock")
    private void throwIfConnectedLocked() {
        if (mConnectionState == ConnectionState.CONNECTED) {
            throw new IllegalStateException("UiAutomation connected, " + this);
        }
    }

    @GuardedBy("mLock")
    private void throwIfNotConnectedLocked() {
        if (mConnectionState != ConnectionState.CONNECTED) {
            throw new IllegalStateException("UiAutomation not connected, " + this);
            final String msg = useAccessibility()
                    ? "UiAutomation not connected, "
                    : "UiAutomation not connected: Accessibility-dependent method called with "
                            + "FLAG_DONT_USE_ACCESSIBILITY set, ";
            throw new IllegalStateException(msg + this);
        }
    }

@@ -1298,11 +1318,16 @@ public final class UiAutomation {
        }
    }

    private boolean useAccessibility() {
        return (mFlags & UiAutomation.FLAG_DONT_USE_ACCESSIBILITY) == 0;
    }

    private class IAccessibilityServiceClientImpl extends IAccessibilityServiceClientWrapper {

        public IAccessibilityServiceClientImpl(Looper looper, int generationId) {
            super(null, looper, new Callbacks() {
                private final int mGenerationId = generationId;

                /**
                 * True if UiAutomation doesn't interact with this client anymore.
                 * Used by methods below to stop sending notifications or changing members
+1 −0
Original line number Diff line number Diff line
@@ -6532,6 +6532,7 @@ package android.app {
    method public android.graphics.Bitmap takeScreenshot();
    method public void waitForIdle(long, long) throws java.util.concurrent.TimeoutException;
    field public static final int FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES = 1; // 0x1
    field public static final int FLAG_DONT_USE_ACCESSIBILITY = 2; // 0x2
    field public static final int ROTATION_FREEZE_0 = 0; // 0x0
    field public static final int ROTATION_FREEZE_180 = 2; // 0x2
    field public static final int ROTATION_FREEZE_270 = 3; // 0x3
+28 −15
Original line number Diff line number Diff line
@@ -72,7 +72,8 @@ class UiAutomationManager {
            };

    /**
     * Register a UiAutomation. Only one may be registered at a time.
     * Register a UiAutomation if it uses the accessibility subsystem. Only one may be registered
     * at a time.
     *
     * @param owner A binder object owned by the process that owns the UiAutomation to be
     *              registered.
@@ -80,6 +81,7 @@ class UiAutomationManager {
     * @param accessibilityServiceInfo The UiAutomation's service info
     * @param flags The UiAutomation's flags
     * @param id The id for the service connection
     * @see UiAutomation#FLAG_DONT_USE_ACCESSIBILITY
     */
    void registerUiTestAutomationServiceLocked(IBinder owner,
            IAccessibilityServiceClient serviceClient,
@@ -88,13 +90,14 @@ class UiAutomationManager {
            AccessibilitySecurityPolicy securityPolicy,
            AbstractAccessibilityServiceConnection.SystemSupport systemSupport,
            WindowManagerInternal windowManagerInternal,
            SystemActionPerformer systemActionPerfomer,
            SystemActionPerformer systemActionPerformer,
            AccessibilityWindowManager awm, int flags) {
        synchronized (mLock) {
            accessibilityServiceInfo.setComponentName(COMPONENT_NAME);

            if (mUiAutomationService != null) {
                throw new IllegalStateException("UiAutomationService " + serviceClient
                throw new IllegalStateException(
                        "UiAutomationService " + mUiAutomationService.mServiceInterface
                                + "already registered!");
            }

@@ -106,12 +109,17 @@ class UiAutomationManager {
                return;
            }

            mUiAutomationFlags = flags;
            mSystemSupport = systemSupport;
            // Ignore registering UiAutomation if it is not allowed to use the accessibility
            // subsystem.
            if (!useAccessibility()) {
                return;
            }
            mUiAutomationService = new UiAutomationService(context, accessibilityServiceInfo, id,
                    mainHandler, mLock, securityPolicy, systemSupport, windowManagerInternal,
                    systemActionPerfomer, awm);
                    systemActionPerformer, awm);
            mUiAutomationServiceOwner = owner;
            mUiAutomationFlags = flags;
            mUiAutomationServiceInfo = accessibilityServiceInfo;
            mUiAutomationService.mServiceInterface = serviceClient;
            mUiAutomationService.onAdded();
@@ -130,15 +138,15 @@ class UiAutomationManager {

    void unregisterUiTestAutomationServiceLocked(IAccessibilityServiceClient serviceClient) {
        synchronized (mLock) {
            if ((mUiAutomationService == null)
            if (useAccessibility()
                    && ((mUiAutomationService == null)
                    || (serviceClient == null)
                    || (mUiAutomationService.mServiceInterface == null)
                    || (serviceClient.asBinder()
                    != mUiAutomationService.mServiceInterface.asBinder())) {
                    != mUiAutomationService.mServiceInterface.asBinder()))) {
                throw new IllegalStateException("UiAutomationService " + serviceClient
                        + " not registered!");
            }

            destroyUiAutomationService();
        }
    }
@@ -150,14 +158,19 @@ class UiAutomationManager {
    }

    boolean isUiAutomationRunningLocked() {
        return (mUiAutomationService != null);
        return (mUiAutomationService != null || !useAccessibility());
    }

    boolean suppressingAccessibilityServicesLocked() {
        return (mUiAutomationService != null) && ((mUiAutomationFlags
        return (mUiAutomationService != null || !useAccessibility())
                && ((mUiAutomationFlags
                & UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES) == 0);
    }

    boolean useAccessibility() {
        return ((mUiAutomationFlags & UiAutomation.FLAG_DONT_USE_ACCESSIBILITY) == 0);
    }

    boolean isTouchExplorationEnabledLocked() {
        return (mUiAutomationService != null)
                && mUiAutomationService.mRequestTouchExplorationMode;
@@ -209,14 +222,14 @@ class UiAutomationManager {
                mUiAutomationService.onRemoved();
                mUiAutomationService.resetLocked();
                mUiAutomationService = null;
                mUiAutomationFlags = 0;
                if (mUiAutomationServiceOwner != null) {
                    mUiAutomationServiceOwner.unlinkToDeath(
                            mUiAutomationServiceOwnerDeathRecipient, 0);
                    mUiAutomationServiceOwner = null;
                }
                mSystemSupport.onClientChangeLocked(false);
            }
            mUiAutomationFlags = 0;
            mSystemSupport.onClientChangeLocked(false);
        }
    }

@@ -227,9 +240,9 @@ class UiAutomationManager {
                int id, Handler mainHandler, Object lock,
                AccessibilitySecurityPolicy securityPolicy,
                SystemSupport systemSupport, WindowManagerInternal windowManagerInternal,
                SystemActionPerformer systemActionPerfomer, AccessibilityWindowManager awm) {
                SystemActionPerformer systemActionPerformer, AccessibilityWindowManager awm) {
            super(context, COMPONENT_NAME, accessibilityServiceInfo, id, mainHandler, lock,
                    securityPolicy, systemSupport, windowManagerInternal, systemActionPerfomer,
                    securityPolicy, systemSupport, windowManagerInternal, systemActionPerformer,
                    awm);
            mMainHandler = mainHandler;
        }
Loading