• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.app;
18 
19 import static android.view.Display.DEFAULT_DISPLAY;
20 
21 import android.accessibilityservice.AccessibilityGestureEvent;
22 import android.accessibilityservice.AccessibilityService;
23 import android.accessibilityservice.AccessibilityService.Callbacks;
24 import android.accessibilityservice.AccessibilityService.IAccessibilityServiceClientWrapper;
25 import android.accessibilityservice.AccessibilityServiceInfo;
26 import android.accessibilityservice.IAccessibilityServiceClient;
27 import android.accessibilityservice.IAccessibilityServiceConnection;
28 import android.accessibilityservice.MagnificationConfig;
29 import android.annotation.IntDef;
30 import android.annotation.NonNull;
31 import android.annotation.Nullable;
32 import android.annotation.SuppressLint;
33 import android.annotation.TestApi;
34 import android.compat.annotation.UnsupportedAppUsage;
35 import android.content.Context;
36 import android.content.pm.PackageManager;
37 import android.graphics.Bitmap;
38 import android.graphics.Point;
39 import android.graphics.Rect;
40 import android.graphics.Region;
41 import android.hardware.HardwareBuffer;
42 import android.hardware.display.DisplayManagerGlobal;
43 import android.os.Build;
44 import android.os.Handler;
45 import android.os.HandlerThread;
46 import android.os.IBinder;
47 import android.os.Looper;
48 import android.os.ParcelFileDescriptor;
49 import android.os.Process;
50 import android.os.RemoteException;
51 import android.os.SystemClock;
52 import android.os.UserHandle;
53 import android.os.UserManager;
54 import android.util.ArraySet;
55 import android.util.DebugUtils;
56 import android.util.Log;
57 import android.util.SparseArray;
58 import android.view.Display;
59 import android.view.InputEvent;
60 import android.view.KeyEvent;
61 import android.view.MotionEvent;
62 import android.view.Surface;
63 import android.view.SurfaceControl;
64 import android.view.View;
65 import android.view.ViewRootImpl;
66 import android.view.Window;
67 import android.view.WindowAnimationFrameStats;
68 import android.view.WindowContentFrameStats;
69 import android.view.accessibility.AccessibilityCache;
70 import android.view.accessibility.AccessibilityEvent;
71 import android.view.accessibility.AccessibilityInteractionClient;
72 import android.view.accessibility.AccessibilityNodeInfo;
73 import android.view.accessibility.AccessibilityWindowInfo;
74 import android.view.accessibility.IAccessibilityInteractionConnection;
75 import android.view.inputmethod.EditorInfo;
76 import android.window.ScreenCapture;
77 import android.window.ScreenCapture.ScreenshotHardwareBuffer;
78 
79 import com.android.internal.annotations.GuardedBy;
80 import com.android.internal.annotations.VisibleForTesting;
81 import com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback;
82 import com.android.internal.inputmethod.RemoteAccessibilityInputConnection;
83 import com.android.internal.util.Preconditions;
84 import com.android.internal.util.function.pooled.PooledLambda;
85 
86 import libcore.io.IoUtils;
87 
88 import java.io.IOException;
89 import java.lang.annotation.Retention;
90 import java.lang.annotation.RetentionPolicy;
91 import java.util.ArrayList;
92 import java.util.List;
93 import java.util.Set;
94 import java.util.concurrent.TimeoutException;
95 
96 /**
97  * Class for interacting with the device's UI by simulation user actions and
98  * introspection of the screen content. It relies on the platform accessibility
99  * APIs to introspect the screen and to perform some actions on the remote view
100  * tree. It also allows injecting of arbitrary raw input events simulating user
101  * interaction with keyboards and touch devices. One can think of a UiAutomation
102  * as a special type of {@link android.accessibilityservice.AccessibilityService}
103  * which does not provide hooks for the service life cycle and exposes other
104  * APIs that are useful for UI test automation.
105  * <p>
106  * The APIs exposed by this class are low-level to maximize flexibility when
107  * developing UI test automation tools and libraries. Generally, a UiAutomation
108  * client should be using a higher-level library or implement high-level functions.
109  * For example, performing a tap on the screen requires construction and injecting
110  * of a touch down and up events which have to be delivered to the system by a
111  * call to {@link #injectInputEvent(InputEvent, boolean)}.
112  * </p>
113  * <p>
114  * The APIs exposed by this class operate across applications enabling a client
115  * to write tests that cover use cases spanning over multiple applications. For
116  * example, going to the settings application to change a setting and then
117  * interacting with another application whose behavior depends on that setting.
118  * </p>
119  */
120 public final class UiAutomation {
121 
122     private static final String LOG_TAG = UiAutomation.class.getSimpleName();
123 
124     private static final boolean DEBUG = false;
125     private static final boolean VERBOSE = false;
126 
127     private static final int CONNECTION_ID_UNDEFINED = -1;
128 
129     private static final long CONNECT_TIMEOUT_MILLIS = 5000;
130 
131     /** Rotation constant: Unfreeze rotation (rotating the device changes its rotation state). */
132     public static final int ROTATION_UNFREEZE = -2;
133 
134     /** Rotation constant: Freeze rotation to its current state. */
135     public static final int ROTATION_FREEZE_CURRENT = -1;
136 
137     /** Rotation constant: Freeze rotation to 0 degrees (natural orientation) */
138     public static final int ROTATION_FREEZE_0 = Surface.ROTATION_0;
139 
140     /** Rotation constant: Freeze rotation to 90 degrees . */
141     public static final int ROTATION_FREEZE_90 = Surface.ROTATION_90;
142 
143     /** Rotation constant: Freeze rotation to 180 degrees . */
144     public static final int ROTATION_FREEZE_180 = Surface.ROTATION_180;
145 
146     /** Rotation constant: Freeze rotation to 270 degrees . */
147     public static final int ROTATION_FREEZE_270 = Surface.ROTATION_270;
148 
149     @Retention(RetentionPolicy.SOURCE)
150     @IntDef(value = {
151             ConnectionState.DISCONNECTED,
152             ConnectionState.CONNECTING,
153             ConnectionState.CONNECTED,
154             ConnectionState.FAILED
155     })
156     private @interface ConnectionState {
157         /** The initial state before {@link #connect} or after {@link #disconnect} is called. */
158         int DISCONNECTED = 0;
159         /**
160          * The temporary state after {@link #connect} is called. Will transition to
161          * {@link #CONNECTED} or {@link #FAILED} depending on whether {@link #connect} succeeds or
162          * not.
163          */
164         int CONNECTING = 1;
165         /** The state when {@link #connect} has succeeded. */
166         int CONNECTED = 2;
167         /** The state when {@link #connect} has failed. */
168         int FAILED = 3;
169     }
170 
171     /**
172      * UiAutomation suppresses accessibility services by default. This flag specifies that
173      * existing accessibility services should continue to run, and that new ones may start.
174      * This flag is set when obtaining the UiAutomation from
175      * {@link Instrumentation#getUiAutomation(int)}.
176      */
177     public static final int FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES = 0x00000001;
178 
179     /**
180      * UiAutomation uses the accessibility subsystem by default. This flag provides an option to
181      * eliminate the overhead of engaging the accessibility subsystem for tests that do not need to
182      * interact with the user interface. Setting this flag disables methods that rely on
183      * accessibility. This flag is set when obtaining the UiAutomation from
184      * {@link Instrumentation#getUiAutomation(int)}.
185      */
186     public static final int FLAG_DONT_USE_ACCESSIBILITY = 0x00000002;
187 
188     /**
189      * UiAutomation sets {@link AccessibilityServiceInfo#isAccessibilityTool()} true by default.
190      * This flag provides the option to set this field false for tests exercising that property.
191      *
192      * @hide
193      */
194     @TestApi
195     public static final int FLAG_NOT_ACCESSIBILITY_TOOL = 0x00000004;
196 
197     /**
198      * Returned by {@link #getAdoptedShellPermissions} to indicate that all permissions have been
199      * adopted using {@link #adoptShellPermissionIdentity}.
200      *
201      * @hide
202      */
203     @TestApi
204     @NonNull
205     public static final Set<String> ALL_PERMISSIONS = Set.of("_ALL_PERMISSIONS_");
206 
207     private final Object mLock = new Object();
208 
209     private final ArrayList<AccessibilityEvent> mEventQueue = new ArrayList<AccessibilityEvent>();
210 
211     private final Handler mLocalCallbackHandler;
212 
213     private final IUiAutomationConnection mUiAutomationConnection;
214 
215     private final int mDisplayId;
216 
217     private HandlerThread mRemoteCallbackThread;
218 
219     private IAccessibilityServiceClient mClient;
220 
221     private int mConnectionId = CONNECTION_ID_UNDEFINED;
222 
223     private OnAccessibilityEventListener mOnAccessibilityEventListener;
224 
225     // Count the nested clients waiting for data delivery
226     private int mCurrentEventWatchersCount = 0;
227 
228     private long mLastEventTimeMillis;
229 
230     private @ConnectionState int mConnectionState = ConnectionState.DISCONNECTED;
231 
232     private boolean mIsDestroyed;
233 
234     private int mFlags;
235 
236     private int mGenerationId = 0;
237 
238     /**
239      * Listener for observing the {@link AccessibilityEvent} stream.
240      */
241     public static interface OnAccessibilityEventListener {
242 
243         /**
244          * Callback for receiving an {@link AccessibilityEvent}.
245          * <p>
246          * <strong>Note:</strong> This method is <strong>NOT</strong> executed
247          * on the main test thread. The client is responsible for proper
248          * synchronization.
249          * </p>
250          * <p>
251          * <strong>Note:</strong> It is responsibility of the client
252          * to recycle the received events to minimize object creation.
253          * </p>
254          *
255          * @param event The received event.
256          */
onAccessibilityEvent(AccessibilityEvent event)257         public void onAccessibilityEvent(AccessibilityEvent event);
258     }
259 
260     /**
261      * Listener for filtering accessibility events.
262      */
263     public static interface AccessibilityEventFilter {
264 
265         /**
266          * Callback for determining whether an event is accepted or
267          * it is filtered out.
268          *
269          * @param event The event to process.
270          * @return True if the event is accepted, false to filter it out.
271          */
accept(AccessibilityEvent event)272         public boolean accept(AccessibilityEvent event);
273     }
274 
275     /**
276      * Creates a new instance that will handle callbacks from the accessibility
277      * layer on the thread of the provided context main looper and perform requests for privileged
278      * operations on the provided connection, and filtering display-related features to the display
279      * associated with the context (or the user running the test, on devices that
280      * {@link UserManager#isVisibleBackgroundUsersSupported() support visible background users}).
281      *
282      * @param context the context associated with the automation
283      * @param connection The connection for performing privileged operations.
284      *
285      * @hide
286      */
UiAutomation(Context context, IUiAutomationConnection connection)287     public UiAutomation(Context context, IUiAutomationConnection connection) {
288         this(getDisplayId(context), context.getMainLooper(), connection);
289     }
290 
291     /**
292      * Creates a new instance that will handle callbacks from the accessibility
293      * layer on the thread of the provided looper and perform requests for privileged
294      * operations on the provided connection.
295      *
296      * @param looper The looper on which to execute accessibility callbacks.
297      * @param connection The connection for performing privileged operations.
298      *
299      * @deprecated use {@link #UiAutomation(Context, IUiAutomationConnection)} instead
300      *
301      * @hide
302      */
303     @Deprecated
304     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
UiAutomation(Looper looper, IUiAutomationConnection connection)305     public UiAutomation(Looper looper, IUiAutomationConnection connection) {
306         this(DEFAULT_DISPLAY, looper, connection);
307         Log.w(LOG_TAG, "Created with deprecatead constructor, assumes DEFAULT_DISPLAY");
308     }
309 
UiAutomation(int displayId, Looper looper, IUiAutomationConnection connection)310     private UiAutomation(int displayId, Looper looper, IUiAutomationConnection connection) {
311         Preconditions.checkArgument(looper != null, "Looper cannot be null!");
312         Preconditions.checkArgument(connection != null, "Connection cannot be null!");
313 
314         mLocalCallbackHandler = new Handler(looper);
315         mUiAutomationConnection = connection;
316         mDisplayId = displayId;
317 
318         Log.i(LOG_TAG, "Initialized for user " + Process.myUserHandle().getIdentifier()
319                 + " on display " + mDisplayId);
320     }
321 
322     /**
323      * Connects this UiAutomation to the accessibility introspection APIs with default flags
324      * and default timeout.
325      *
326      * @hide
327      */
328     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
connect()329     public void connect() {
330         try {
331             connectWithTimeout(0, CONNECT_TIMEOUT_MILLIS);
332         } catch (TimeoutException e) {
333             throw new RuntimeException(e);
334         }
335     }
336 
337     /**
338      * Connects this UiAutomation to the accessibility introspection APIs with default timeout.
339      *
340      * @hide
341      */
connect(int flags)342     public void connect(int flags) {
343         try {
344             connectWithTimeout(flags, CONNECT_TIMEOUT_MILLIS);
345         } catch (TimeoutException e) {
346             throw new RuntimeException(e);
347         }
348     }
349 
350     /**
351      * Connects this UiAutomation to the accessibility introspection APIs.
352      *
353      * @param flags Any flags to apply to the automation as it gets connected while
354      *              {@link UiAutomation#FLAG_DONT_USE_ACCESSIBILITY} would keep the
355      *              connection disconnected and not to register UiAutomation service.
356      * @param timeoutMillis The wait timeout in milliseconds
357      *
358      * @throws IllegalStateException If the connection to the accessibility subsystem is already
359      *            established.
360      * @throws TimeoutException If not connected within the timeout
361      * @hide
362      */
connectWithTimeout(int flags, long timeoutMillis)363     public void connectWithTimeout(int flags, long timeoutMillis) throws TimeoutException {
364         if (DEBUG) {
365             Log.d(LOG_TAG, "connectWithTimeout: user=" + Process.myUserHandle().getIdentifier()
366                     + ", flags=" + DebugUtils.flagsToString(UiAutomation.class, "FLAG_", flags)
367                     + ", timeout=" + timeoutMillis + "ms");
368         }
369         synchronized (mLock) {
370             throwIfConnectedLocked();
371             if (mConnectionState == ConnectionState.CONNECTING) {
372                 if (DEBUG) Log.d(LOG_TAG, "already connecting");
373                 return;
374             }
375             if (DEBUG) Log.d(LOG_TAG, "setting state to CONNECTING");
376             mConnectionState = ConnectionState.CONNECTING;
377             mRemoteCallbackThread = new HandlerThread("UiAutomation");
378             mRemoteCallbackThread.start();
379             // Increment the generation since we are about to interact with a new client
380             mClient = new IAccessibilityServiceClientImpl(
381                     mRemoteCallbackThread.getLooper(), ++mGenerationId);
382         }
383 
384         try {
385             // Calling out without a lock held.
386             mUiAutomationConnection.connect(mClient, flags);
387             mFlags = flags;
388             // If UiAutomation is not allowed to use the accessibility subsystem, the
389             // connection state should keep disconnected and not to start the client connection.
390             if (!useAccessibility()) {
391                 if (DEBUG) Log.d(LOG_TAG, "setting state to DISCONNECTED");
392                 mConnectionState = ConnectionState.DISCONNECTED;
393                 return;
394             }
395         } catch (RemoteException re) {
396             throw new RuntimeException("Error while connecting " + this, re);
397         }
398 
399         synchronized (mLock) {
400             final long startTimeMillis = SystemClock.uptimeMillis();
401             while (true) {
402                 if (mConnectionState == ConnectionState.CONNECTED) {
403                     break;
404                 }
405                 final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
406                 final long remainingTimeMillis = timeoutMillis - elapsedTimeMillis;
407                 if (remainingTimeMillis <= 0) {
408                     if (DEBUG) Log.d(LOG_TAG, "setting state to FAILED");
409                     mConnectionState = ConnectionState.FAILED;
410                     throw new TimeoutException("Timeout while connecting " + this);
411                 }
412                 try {
413                     mLock.wait(remainingTimeMillis);
414                 } catch (InterruptedException ie) {
415                     /* ignore */
416                 }
417             }
418         }
419     }
420 
421     /**
422      * Get the flags used to connect the service.
423      *
424      * @return The flags used to connect
425      *
426      * @hide
427      */
getFlags()428     public int getFlags() {
429         return mFlags;
430     }
431 
432     /**
433      * Disconnects this UiAutomation from the accessibility introspection APIs.
434      *
435      * @hide
436      */
437     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
disconnect()438     public void disconnect() {
439         synchronized (mLock) {
440             if (mConnectionState == ConnectionState.CONNECTING) {
441                 throw new IllegalStateException(
442                         "Cannot call disconnect() while connecting " + this);
443             }
444             if (useAccessibility() && mConnectionState == ConnectionState.DISCONNECTED) {
445                 return;
446             }
447             mConnectionState = ConnectionState.DISCONNECTED;
448             mConnectionId = CONNECTION_ID_UNDEFINED;
449             // Increment the generation so we no longer interact with the existing client
450             ++mGenerationId;
451         }
452         try {
453             // Calling out without a lock held.
454             mUiAutomationConnection.disconnect();
455         } catch (RemoteException re) {
456             throw new RuntimeException("Error while disconnecting " + this, re);
457         } finally {
458             if (mRemoteCallbackThread != null) {
459                 mRemoteCallbackThread.quit();
460                 mRemoteCallbackThread = null;
461             }
462         }
463     }
464 
465     /**
466      * The id of the {@link IAccessibilityInteractionConnection} for querying
467      * the screen content. This is here for legacy purposes since some tools use
468      * hidden APIs to introspect the screen.
469      *
470      * @hide
471      */
getConnectionId()472     public int getConnectionId() {
473         synchronized (mLock) {
474             throwIfNotConnectedLocked();
475             return mConnectionId;
476         }
477     }
478 
479     /**
480      * Reports if the object has been destroyed
481      *
482      * @return {code true} if the object has been destroyed.
483      *
484      * @hide
485      */
isDestroyed()486     public boolean isDestroyed() {
487         return mIsDestroyed;
488     }
489 
490     /**
491      * Sets a callback for observing the stream of {@link AccessibilityEvent}s.
492      * The callbacks are delivered on the main application thread.
493      *
494      * @param listener The callback.
495      *
496      * @throws IllegalStateException If the connection to the accessibility subsystem is not
497      *            established.
498      */
setOnAccessibilityEventListener(OnAccessibilityEventListener listener)499     public void setOnAccessibilityEventListener(OnAccessibilityEventListener listener) {
500         synchronized (mLock) {
501             throwIfNotConnectedLocked();
502             mOnAccessibilityEventListener = listener;
503         }
504     }
505 
506     /**
507      * Destroy this UiAutomation. After calling this method, attempting to use the object will
508      * result in errors.
509      *
510      * @hide
511      */
512     @TestApi
destroy()513     public void destroy() {
514         disconnect();
515         mIsDestroyed = true;
516     }
517 
518     /**
519      * Clears the accessibility cache.
520      *
521      * @return {@code true} if the cache was cleared
522      * @see AccessibilityService#clearCache()
523      */
clearCache()524     public boolean clearCache() {
525         final int connectionId;
526         synchronized (mLock) {
527             throwIfNotConnectedLocked();
528             connectionId = mConnectionId;
529         }
530         final AccessibilityCache cache = AccessibilityInteractionClient.getCache(connectionId);
531         if (cache == null) {
532             return false;
533         }
534         cache.clear();
535         return true;
536     }
537 
538     /**
539      * Checks if {@code node} is in the accessibility cache.
540      *
541      * @param node the node to check.
542      * @return {@code true} if {@code node} is in the cache.
543      * @hide
544      * @see AccessibilityService#isNodeInCache(AccessibilityNodeInfo)
545      */
546     @TestApi
isNodeInCache(@onNull AccessibilityNodeInfo node)547     public boolean isNodeInCache(@NonNull AccessibilityNodeInfo node) {
548         final int connectionId;
549         synchronized (mLock) {
550             throwIfNotConnectedLocked();
551             connectionId = mConnectionId;
552         }
553         final AccessibilityCache cache = AccessibilityInteractionClient.getCache(connectionId);
554         if (cache == null) {
555             return false;
556         }
557         return cache.isNodeInCache(node);
558     }
559 
560     /**
561      * Provides reference to the cache through a locked connection.
562      *
563      * @return the accessibility cache.
564      * @hide
565      */
getCache()566     public @Nullable AccessibilityCache getCache() {
567         final int connectionId;
568         synchronized (mLock) {
569             throwIfNotConnectedLocked();
570             connectionId = mConnectionId;
571         }
572         return AccessibilityInteractionClient.getCache(connectionId);
573     }
574 
575     /**
576      * Adopt the permission identity of the shell UID for all permissions. This allows
577      * you to call APIs protected permissions which normal apps cannot hold but are
578      * granted to the shell UID. If you already adopted all shell permissions by calling
579      * this method or {@link #adoptShellPermissionIdentity(String...)} a subsequent call will
580      * replace any previous adoption. Note that your permission state becomes that of the shell UID
581      * and it is not a combination of your and the shell UID permissions.
582      * <p>
583      * <strong>Note:<strong/> Calling this method adopts all shell permissions and overrides
584      * any subset of adopted permissions via {@link #adoptShellPermissionIdentity(String...)}.
585      *
586      * @see #adoptShellPermissionIdentity(String...)
587      * @see #dropShellPermissionIdentity()
588      */
adoptShellPermissionIdentity()589     public void adoptShellPermissionIdentity() {
590         try {
591             // Calling out without a lock held.
592             mUiAutomationConnection.adoptShellPermissionIdentity(Process.myUid(), null);
593         } catch (RemoteException re) {
594             throw re.rethrowFromSystemServer();
595         }
596     }
597 
598     /**
599      * Adopt the permission identity of the shell UID only for the provided permissions.
600      * This allows you to call APIs protected permissions which normal apps cannot hold
601      * but are granted to the shell UID. If you already adopted shell permissions by calling
602      * this method, or {@link #adoptShellPermissionIdentity()} a subsequent call will replace any
603      * previous adoption.
604      * <p>
605      * <strong>Note:<strong/> This method behave differently from
606      * {@link #adoptShellPermissionIdentity()}. Only the listed permissions will use the shell
607      * identity and other permissions will still check against the original UID
608      *
609      * @param permissions The permissions to adopt or <code>null</code> to adopt all.
610      *
611      * @see #adoptShellPermissionIdentity()
612      * @see #dropShellPermissionIdentity()
613      */
adoptShellPermissionIdentity(@ullable String... permissions)614     public void adoptShellPermissionIdentity(@Nullable String... permissions) {
615         try {
616             // Calling out without a lock held.
617             mUiAutomationConnection.adoptShellPermissionIdentity(Process.myUid(), permissions);
618         } catch (RemoteException re) {
619             throw re.rethrowFromSystemServer();
620         }
621     }
622 
623     /**
624      * Drop the shell permission identity adopted by a previous call to
625      * {@link #adoptShellPermissionIdentity()}. If you did not adopt the shell permission
626      * identity this method would be a no-op.
627      *
628      * @see #adoptShellPermissionIdentity()
629      */
dropShellPermissionIdentity()630     public void dropShellPermissionIdentity() {
631         try {
632             // Calling out without a lock held.
633             mUiAutomationConnection.dropShellPermissionIdentity();
634         } catch (RemoteException re) {
635             throw re.rethrowFromSystemServer();
636         }
637     }
638 
639     /**
640      * Returns a list of adopted shell permissions using {@link #adoptShellPermissionIdentity},
641      * returns and empty set if no permissions are adopted and {@link #ALL_PERMISSIONS} if all
642      * permissions are adopted.
643      *
644      * @hide
645      */
646     @TestApi
647     @NonNull
getAdoptedShellPermissions()648     public Set<String> getAdoptedShellPermissions() {
649         try {
650             final List<String> permissions = mUiAutomationConnection.getAdoptedShellPermissions();
651             return permissions == null ? ALL_PERMISSIONS : new ArraySet<>(permissions);
652         } catch (RemoteException re) {
653             throw re.rethrowFromSystemServer();
654         }
655     }
656 
657     /**
658      * Adds permission to be overridden to the given state. UiAutomation must be connected to
659      * root user.
660      *
661      * @param uid The UID of the app whose permission will be overridden
662      * @param permission The permission whose state will be overridden
663      * @param result The state to override the permission to
664      *
665      * @see PackageManager#PERMISSION_GRANTED
666      * @see PackageManager#PERMISSION_DENIED
667      *
668      * @hide
669      */
670     @TestApi
671     @SuppressLint("UnflaggedApi")
addOverridePermissionState(int uid, @NonNull String permission, @PackageManager.PermissionResult int result)672     public void addOverridePermissionState(int uid, @NonNull String permission,
673             @PackageManager.PermissionResult int result) {
674         try {
675             mUiAutomationConnection.addOverridePermissionState(uid, permission, result);
676         } catch (RemoteException re) {
677             re.rethrowFromSystemServer();
678         }
679     }
680 
681     /**
682      * Removes overridden permission. UiAutomation must be connected to root user.
683      *
684      * @param uid The UID of the app whose permission is overridden
685      * @param permission The permission whose state will no longer be overridden
686      *
687      * @hide
688      */
689     @TestApi
690     @SuppressLint("UnflaggedApi")
removeOverridePermissionState(int uid, @NonNull String permission)691     public void removeOverridePermissionState(int uid, @NonNull String permission) {
692         try {
693             mUiAutomationConnection.removeOverridePermissionState(uid, permission);
694         } catch (RemoteException re) {
695             re.rethrowFromSystemServer();
696         }
697     }
698 
699     /**
700      * Clears all overridden permissions for the given UID. UiAutomation must be connected to
701      * root user.
702      *
703      * @param uid The UID of the app whose permissions will no longer be overridden
704      *
705      * @hide
706      */
707     @TestApi
708     @SuppressLint("UnflaggedApi")
clearOverridePermissionStates(int uid)709     public void clearOverridePermissionStates(int uid) {
710         try {
711             mUiAutomationConnection.clearOverridePermissionStates(uid);
712         } catch (RemoteException re) {
713             re.rethrowFromSystemServer();
714         }
715     }
716 
717     /**
718      * Clears all overridden permissions on the device. UiAutomation must be connected to root user.
719      *
720      * @hide
721      */
722     @TestApi
723     @SuppressLint("UnflaggedApi")
clearAllOverridePermissionStates()724     public void clearAllOverridePermissionStates() {
725         try {
726             mUiAutomationConnection.clearAllOverridePermissionStates();
727         } catch (RemoteException re) {
728             re.rethrowFromSystemServer();
729         }
730     }
731 
732     /**
733      * Performs a global action. Such an action can be performed at any moment
734      * regardless of the current application or user location in that application.
735      * For example going back, going home, opening recents, etc.
736      *
737      * @param action The action to perform.
738      * @return Whether the action was successfully performed.
739      *
740      * @throws IllegalStateException If the connection to the accessibility subsystem is not
741      *            established.
742      * @see android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_BACK
743      * @see android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_HOME
744      * @see android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_NOTIFICATIONS
745      * @see android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_RECENTS
746      */
performGlobalAction(int action)747     public final boolean performGlobalAction(int action) {
748         final IAccessibilityServiceConnection connection;
749         synchronized (mLock) {
750             throwIfNotConnectedLocked();
751             connection = AccessibilityInteractionClient.getInstance()
752                     .getConnection(mConnectionId);
753         }
754         // Calling out without a lock held.
755         if (connection != null) {
756             try {
757                 return connection.performGlobalAction(action);
758             } catch (RemoteException re) {
759                 Log.w(LOG_TAG, "Error while calling performGlobalAction", re);
760             }
761         }
762         return false;
763     }
764 
765     /**
766      * Find the view that has the specified focus type. The search is performed
767      * across all windows.
768      * <p>
769      * <strong>Note:</strong> In order to access the windows you have to opt-in
770      * to retrieve the interactive windows by setting the
771      * {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} flag.
772      * Otherwise, the search will be performed only in the active window.
773      * </p>
774      *
775      * @param focus The focus to find. One of {@link AccessibilityNodeInfo#FOCUS_INPUT} or
776      *              {@link AccessibilityNodeInfo#FOCUS_ACCESSIBILITY}.
777      * @return The node info of the focused view or null.
778      *
779      * @throws IllegalStateException If the connection to the accessibility subsystem is not
780      *            established.
781      * @see AccessibilityNodeInfo#FOCUS_INPUT
782      * @see AccessibilityNodeInfo#FOCUS_ACCESSIBILITY
783      */
findFocus(int focus)784     public AccessibilityNodeInfo findFocus(int focus) {
785         synchronized (mLock) {
786             throwIfNotConnectedLocked();
787         }
788         return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId,
789                 AccessibilityWindowInfo.ANY_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID, focus);
790     }
791 
792     /**
793      * Gets the an {@link AccessibilityServiceInfo} describing this UiAutomation.
794      * This method is useful if one wants to change some of the dynamically
795      * configurable properties at runtime.
796      *
797      * @return The accessibility service info.
798      *
799      * @throws IllegalStateException If the connection to the accessibility subsystem is not
800      *            established.
801      * @see AccessibilityServiceInfo
802      */
getServiceInfo()803     public final AccessibilityServiceInfo getServiceInfo() {
804         final IAccessibilityServiceConnection connection;
805         synchronized (mLock) {
806             throwIfNotConnectedLocked();
807             connection = AccessibilityInteractionClient.getInstance()
808                     .getConnection(mConnectionId);
809         }
810         // Calling out without a lock held.
811         if (connection != null) {
812             try {
813                 return connection.getServiceInfo();
814             } catch (RemoteException re) {
815                 Log.w(LOG_TAG, "Error while getting AccessibilityServiceInfo", re);
816             }
817         }
818         return null;
819     }
820 
821     /**
822      * Sets the {@link AccessibilityServiceInfo} that describes how this
823      * UiAutomation will be handled by the platform accessibility layer.
824      *
825      * @param info The info.
826      *
827      * @throws IllegalStateException If the connection to the accessibility subsystem is not
828      *            established.
829      * @see AccessibilityServiceInfo
830      */
setServiceInfo(AccessibilityServiceInfo info)831     public final void setServiceInfo(AccessibilityServiceInfo info) {
832         final IAccessibilityServiceConnection connection;
833         synchronized (mLock) {
834             throwIfNotConnectedLocked();
835             AccessibilityInteractionClient.getInstance().clearCache(mConnectionId);
836             connection = AccessibilityInteractionClient.getInstance()
837                     .getConnection(mConnectionId);
838         }
839         // Calling out without a lock held.
840         if (connection != null) {
841             try {
842                 connection.setServiceInfo(info);
843             } catch (RemoteException re) {
844                 Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re);
845             }
846         }
847     }
848 
849     /**
850      * Gets the windows on the screen associated with the {@link UiAutomation} context (usually the
851      * {@link android.view.Display#DEFAULT_DISPLAY default display).
852      *
853      * <p>
854      * This method returns only the windows that a sighted user can interact with, as opposed to
855      * all windows.
856 
857      * <p>
858      * For example, if there is a modal dialog shown and the user cannot touch
859      * anything behind it, then only the modal window will be reported
860      * (assuming it is the top one). For convenience the returned windows
861      * are ordered in a descending layer order, which is the windows that
862      * are higher in the Z-order are reported first.
863      * <p>
864      * <strong>Note:</strong> In order to access the windows you have to opt-in
865      * to retrieve the interactive windows by setting the
866      * {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} flag.
867      *
868      * @return The windows if there are windows such, otherwise an empty list.
869      * @throws IllegalStateException If the connection to the accessibility subsystem is not
870      *            established.
871      */
getWindows()872     public List<AccessibilityWindowInfo> getWindows() {
873         if (DEBUG) {
874             Log.d(LOG_TAG, "getWindows(): returning windows for display " + mDisplayId);
875         }
876         final int connectionId;
877         synchronized (mLock) {
878             throwIfNotConnectedLocked();
879             connectionId = mConnectionId;
880         }
881         // Calling out without a lock held.
882         return AccessibilityInteractionClient.getInstance().getWindowsOnDisplay(connectionId,
883                 mDisplayId);
884     }
885 
886     /**
887      * Gets the windows on the screen of all displays. This method returns only the windows
888      * that a sighted user can interact with, as opposed to all windows.
889      * For example, if there is a modal dialog shown and the user cannot touch
890      * anything behind it, then only the modal window will be reported
891      * (assuming it is the top one). For convenience the returned windows
892      * are ordered in a descending layer order, which is the windows that
893      * are higher in the Z-order are reported first.
894      * <p>
895      * <strong>Note:</strong> In order to access the windows you have to opt-in
896      * to retrieve the interactive windows by setting the
897      * {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} flag.
898      * </p>
899      *
900      * @return The windows of all displays if there are windows and the service is can retrieve
901      *         them, otherwise an empty list. The key of SparseArray is display ID.
902      * @throws IllegalStateException If the connection to the accessibility subsystem is not
903      *            established.
904      */
905     @NonNull
getWindowsOnAllDisplays()906     public SparseArray<List<AccessibilityWindowInfo>> getWindowsOnAllDisplays() {
907         final int connectionId;
908         synchronized (mLock) {
909             throwIfNotConnectedLocked();
910             connectionId = mConnectionId;
911         }
912         // Calling out without a lock held.
913         return AccessibilityInteractionClient.getInstance()
914                 .getWindowsOnAllDisplays(connectionId);
915     }
916 
917     /**
918      * Gets the root {@link AccessibilityNodeInfo} in the active window.
919      *
920      * @return The root info.
921      * @throws IllegalStateException If the connection to the accessibility subsystem is not
922      *            established.
923      */
getRootInActiveWindow()924     public AccessibilityNodeInfo getRootInActiveWindow() {
925         return getRootInActiveWindow(AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS_HYBRID);
926     }
927 
928     /**
929      * Gets the root {@link AccessibilityNodeInfo} in the active window.
930      *
931      * @param prefetchingStrategy the prefetching strategy.
932      * @return The root info.
933      * @throws IllegalStateException If the connection to the accessibility subsystem is not
934      * established.
935      *
936      * @hide
937      */
938     @Nullable
getRootInActiveWindow( @ccessibilityNodeInfo.PrefetchingStrategy int prefetchingStrategy)939     public AccessibilityNodeInfo getRootInActiveWindow(
940             @AccessibilityNodeInfo.PrefetchingStrategy int prefetchingStrategy) {
941         final int connectionId;
942         synchronized (mLock) {
943             throwIfNotConnectedLocked();
944             connectionId = mConnectionId;
945         }
946         // Calling out without a lock held.
947         return AccessibilityInteractionClient.getInstance()
948                 .getRootInActiveWindow(connectionId, prefetchingStrategy);
949     }
950 
951     /**
952      * A method for injecting an arbitrary input event.
953      *
954      * This method waits for all window container animations and surface operations to complete.
955      *
956      * <p>
957      * <strong>Note:</strong> It is caller's responsibility to recycle the event.
958      * </p>
959      * @param event the event to inject
960      * @param sync whether to inject the event synchronously
961      * @return {@code true} if event injection succeeded
962      */
injectInputEvent(InputEvent event, boolean sync)963     public boolean injectInputEvent(InputEvent event, boolean sync) {
964         return injectInputEvent(event, sync, true /* waitForAnimations */);
965     }
966 
967     /**
968      * A method for injecting an arbitrary input event, optionally waiting for window animations to
969      * complete.
970      * <p>
971      * <strong>Note:</strong> It is caller's responsibility to recycle the event.
972      * </p>
973      *
974      * @param event the event to inject
975      * @param sync  whether to inject the event synchronously.
976      * @param waitForAnimations whether to wait for all window container animations and surface
977      *   operations to complete
978      * @return {@code true} if event injection succeeded
979      *
980      * @deprecated for CTS tests prefer inject input events using uinput
981      *   (com.android.cts.input.UinputDevice) or hid devices (com.android.cts.input.HidDevice).
982      *   Alternatively, InjectInputInProcess (com.android.cts.input.InjectInputProcess) can be used
983      *   for in-process injection.
984      * @hide
985      */
986     @TestApi
987     @Deprecated  // Deprecated for CTS tests
988     @SuppressLint("UnflaggedApi")  // @FlaggedApi breaks previously released @TestApi, b/395889250
injectInputEvent(@onNull InputEvent event, boolean sync, boolean waitForAnimations)989     public boolean injectInputEvent(@NonNull InputEvent event, boolean sync,
990             boolean waitForAnimations) {
991         try {
992             if (DEBUG) {
993                 Log.i(LOG_TAG, "Injecting: " + event + " sync: " + sync + " waitForAnimations: "
994                         + waitForAnimations);
995             }
996             // Calling out without a lock held.
997             return mUiAutomationConnection.injectInputEvent(event, sync, waitForAnimations);
998         } catch (RemoteException re) {
999             Log.e(LOG_TAG, "Error while injecting input event!", re);
1000         }
1001         return false;
1002     }
1003 
1004     /**
1005      * Injects an arbitrary {@link InputEvent} to the accessibility input filter, for use in testing
1006      * the accessibility input filter.
1007      *
1008      * Events injected to the input subsystem using the standard {@link #injectInputEvent} method
1009      * skip the accessibility input filter to avoid feedback loops.
1010      *
1011      * @deprecated for CTS tests prefer inject input events using uinput
1012      *   (com.android.cts.input.UinputDevice) or hid devices (com.android.cts.input.HidDevice).
1013      *   Alternatively, InjectInputInProcess (com.android.cts.input.InjectInputProcess) can be used
1014      *   for in-process injection.
1015      * @hide
1016      */
1017     @TestApi
1018     @Deprecated
1019     @SuppressLint("UnflaggedApi")  // @FlaggedApi breaks previously released @TestApi, b/395889250
injectInputEventToInputFilter(@onNull InputEvent event)1020     public void injectInputEventToInputFilter(@NonNull InputEvent event) {
1021         try {
1022             mUiAutomationConnection.injectInputEventToInputFilter(event);
1023         } catch (RemoteException re) {
1024             Log.e(LOG_TAG, "Error while injecting input event to input filter", re);
1025         }
1026     }
1027 
1028     /**
1029      * Sets the system settings values that control the scaling factor for animations. The scale
1030      * controls the animation playback speed for animations that respect these settings. Animations
1031      * that do not respect the settings values will not be affected by this function. A lower scale
1032      * value results in a faster speed. A value of <code>0</code> disables animations entirely. When
1033      * animations are disabled services receive window change events more quickly which can reduce
1034      * the potential by confusion by reducing the time during which windows are in transition.
1035      *
1036      * @see AccessibilityEvent#TYPE_WINDOWS_CHANGED
1037      * @see AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED
1038      * @see android.provider.Settings.Global#WINDOW_ANIMATION_SCALE
1039      * @see android.provider.Settings.Global#TRANSITION_ANIMATION_SCALE
1040      * @see android.provider.Settings.Global#ANIMATOR_DURATION_SCALE
1041      * @param scale The scaling factor for all animations.
1042      */
setAnimationScale(float scale)1043     public void setAnimationScale(float scale) {
1044         final IAccessibilityServiceConnection connection =
1045                 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
1046         if (connection != null) {
1047             try {
1048                 connection.setAnimationScale(scale);
1049             } catch (RemoteException re) {
1050                 throw new RuntimeException(re);
1051             }
1052         }
1053     }
1054 
1055     /**
1056      * A request for WindowManagerService to wait until all animations have completed and input
1057      * information has been sent from WindowManager to native InputManager.
1058      *
1059      * @hide
1060      */
1061     @TestApi
syncInputTransactions()1062     public void syncInputTransactions() {
1063         try {
1064             // Calling out without a lock held.
1065             mUiAutomationConnection.syncInputTransactions(true /* waitForAnimations */);
1066         } catch (RemoteException re) {
1067             Log.e(LOG_TAG, "Error while syncing input transactions!", re);
1068         }
1069     }
1070 
1071     /**
1072      * A request for WindowManagerService to wait until all input information has been sent from
1073      * WindowManager to native InputManager and optionally wait for animations to complete.
1074      *
1075      * @param waitForAnimations Whether to wait for all window container animations and surface
1076      *   operations to complete.
1077      *
1078      * @hide
1079      */
1080     @TestApi
syncInputTransactions(boolean waitForAnimations)1081     public void syncInputTransactions(boolean waitForAnimations) {
1082         try {
1083             // Calling out without a lock held.
1084             mUiAutomationConnection.syncInputTransactions(waitForAnimations);
1085         } catch (RemoteException re) {
1086             Log.e(LOG_TAG, "Error while syncing input transactions!", re);
1087         }
1088     }
1089 
1090     /**
1091      * Sets the device rotation. A client can freeze the rotation in
1092      * desired state or freeze the rotation to its current state or
1093      * unfreeze the rotation (rotating the device changes its rotation
1094      * state).
1095      *
1096      * @param rotation The desired rotation.
1097      * @return Whether the rotation was set successfully.
1098      *
1099      * @see #ROTATION_FREEZE_0
1100      * @see #ROTATION_FREEZE_90
1101      * @see #ROTATION_FREEZE_180
1102      * @see #ROTATION_FREEZE_270
1103      * @see #ROTATION_FREEZE_CURRENT
1104      * @see #ROTATION_UNFREEZE
1105      */
setRotation(int rotation)1106     public boolean setRotation(int rotation) {
1107         switch (rotation) {
1108             case ROTATION_FREEZE_0:
1109             case ROTATION_FREEZE_90:
1110             case ROTATION_FREEZE_180:
1111             case ROTATION_FREEZE_270:
1112             case ROTATION_UNFREEZE:
1113             case ROTATION_FREEZE_CURRENT: {
1114                 try {
1115                     // Calling out without a lock held.
1116                     mUiAutomationConnection.setRotation(rotation);
1117                     return true;
1118                 } catch (RemoteException re) {
1119                     Log.e(LOG_TAG, "Error while setting rotation!", re);
1120                 }
1121             } return false;
1122             default: {
1123                 throw new IllegalArgumentException("Invalid rotation.");
1124             }
1125         }
1126     }
1127 
1128     /**
1129      * Executes a command and waits for a specific accessibility event up to a
1130      * given wait timeout. To detect a sequence of events one can implement a
1131      * filter that keeps track of seen events of the expected sequence and
1132      * returns true after the last event of that sequence is received.
1133      * <p>
1134      * <strong>Note:</strong> It is caller's responsibility to recycle the returned event.
1135      * </p>
1136      *
1137      * @param command The command to execute.
1138      * @param filter Filter that recognizes the expected event.
1139      * @param timeoutMillis The wait timeout in milliseconds.
1140      *
1141      * @throws TimeoutException If the expected event is not received within the timeout.
1142      * @throws IllegalStateException If the connection to the accessibility subsystem is not
1143      *            established.
1144      */
executeAndWaitForEvent(Runnable command, AccessibilityEventFilter filter, long timeoutMillis)1145     public AccessibilityEvent executeAndWaitForEvent(Runnable command,
1146             AccessibilityEventFilter filter, long timeoutMillis) throws TimeoutException {
1147         int watchersDepth;
1148         // Track events added after the index for this command, it is to support nested calls.
1149         // This doesn't support concurrent calls correctly.
1150         int eventQueueStartIndex;
1151         final long executionStartTimeMillis;
1152 
1153         // Acquire the lock and prepare for receiving events.
1154         synchronized (mLock) {
1155             throwIfNotConnectedLocked();
1156             watchersDepth = ++mCurrentEventWatchersCount;
1157             executionStartTimeMillis = SystemClock.uptimeMillis();
1158             eventQueueStartIndex = mEventQueue.size();
1159         }
1160         if (VERBOSE) {
1161             Log.v(LOG_TAG, "executeAndWaitForEvent starts at depth=" + watchersDepth + ", "
1162                     + "command=" + command + ", filter=" + filter + ", timeout=" + timeoutMillis);
1163         }
1164 
1165         try {
1166             // Execute the command *without* the lock being held.
1167             command.run();
1168             synchronized (mLock) {
1169                 if (watchersDepth != mCurrentEventWatchersCount) {
1170                     throw new IllegalStateException("Unexpected event watchers count, expected: "
1171                             + watchersDepth + ", actual: " + mCurrentEventWatchersCount);
1172                 }
1173             }
1174             final long startTimeMillis = SystemClock.uptimeMillis();
1175             List<AccessibilityEvent> receivedEvents = new ArrayList<>();
1176             long elapsedTimeMillis = 0;
1177             int currentQueueSize = 0;
1178             while (timeoutMillis > elapsedTimeMillis) {
1179                 AccessibilityEvent event = null;
1180                 synchronized (mLock) {
1181                     currentQueueSize = mEventQueue.size();
1182                     if (eventQueueStartIndex < currentQueueSize) {
1183                         event = mEventQueue.get(eventQueueStartIndex++);
1184                     } else {
1185                         try {
1186                             mLock.wait(timeoutMillis - elapsedTimeMillis);
1187                         } catch (InterruptedException ie) {
1188                             /* ignore */
1189                         }
1190                     }
1191                 }
1192                 elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
1193                 if (event == null || event.getEventTime() < executionStartTimeMillis) {
1194                     continue;
1195                 }
1196                 if (filter.accept(event)) {
1197                     return event;
1198                 }
1199                 receivedEvents.add(event);
1200             }
1201             if (eventQueueStartIndex < currentQueueSize) {
1202                 Log.w(LOG_TAG, "Timed out before reading all events from the queue");
1203             }
1204             throw new TimeoutException("Expected event not received before timeout, events: "
1205                     + receivedEvents);
1206         } finally {
1207             synchronized (mLock) {
1208                 if (--mCurrentEventWatchersCount == 0) {
1209                     mEventQueue.clear();
1210                 }
1211                 mLock.notifyAll();
1212             }
1213             if (VERBOSE) {
1214                 Log.v(LOG_TAG, "executeAndWaitForEvent ends at depth=" + watchersDepth);
1215             }
1216         }
1217     }
1218 
1219     /**
1220      * Waits for the accessibility event stream to become idle, which is not to
1221      * have received an accessibility event within <code>idleTimeoutMillis</code>.
1222      * The total time spent to wait for an idle accessibility event stream is bounded
1223      * by the <code>globalTimeoutMillis</code>.
1224      *
1225      * @param idleTimeoutMillis The timeout in milliseconds between two events
1226      *            to consider the device idle.
1227      * @param globalTimeoutMillis The maximal global timeout in milliseconds in
1228      *            which to wait for an idle state.
1229      *
1230      * @throws TimeoutException If no idle state was detected within
1231      *            <code>globalTimeoutMillis.</code>
1232      * @throws IllegalStateException If the connection to the accessibility subsystem is not
1233      *            established.
1234      */
waitForIdle(long idleTimeoutMillis, long globalTimeoutMillis)1235     public void waitForIdle(long idleTimeoutMillis, long globalTimeoutMillis)
1236             throws TimeoutException {
1237         synchronized (mLock) {
1238             throwIfNotConnectedLocked();
1239 
1240             final long startTimeMillis = SystemClock.uptimeMillis();
1241             if (mLastEventTimeMillis <= 0) {
1242                 mLastEventTimeMillis = startTimeMillis;
1243             }
1244 
1245             while (true) {
1246                 final long currentTimeMillis = SystemClock.uptimeMillis();
1247                 // Did we get idle state within the global timeout?
1248                 final long elapsedGlobalTimeMillis = currentTimeMillis - startTimeMillis;
1249                 final long remainingGlobalTimeMillis =
1250                         globalTimeoutMillis - elapsedGlobalTimeMillis;
1251                 if (remainingGlobalTimeMillis <= 0) {
1252                     throw new TimeoutException("No idle state with idle timeout: "
1253                             + idleTimeoutMillis + " within global timeout: "
1254                             + globalTimeoutMillis);
1255                 }
1256                 // Did we get an idle state within the idle timeout?
1257                 final long elapsedIdleTimeMillis = currentTimeMillis - mLastEventTimeMillis;
1258                 final long remainingIdleTimeMillis = idleTimeoutMillis - elapsedIdleTimeMillis;
1259                 if (remainingIdleTimeMillis <= 0) {
1260                     return;
1261                 }
1262                 try {
1263                     mLock.wait(remainingIdleTimeMillis);
1264                 } catch (InterruptedException ie) {
1265                     /* ignore */
1266                 }
1267             }
1268         }
1269     }
1270 
1271     /**
1272      * Takes a screenshot.
1273      *
1274      * @return The screenshot bitmap on success, null otherwise.
1275      */
takeScreenshot()1276     public Bitmap takeScreenshot() {
1277         if (DEBUG) {
1278             Log.d(LOG_TAG, "Taking screenshot of display " + mDisplayId);
1279         }
1280         Display display = DisplayManagerGlobal.getInstance().getRealDisplay(mDisplayId);
1281         Point displaySize = new Point();
1282         display.getRealSize(displaySize);
1283 
1284         // Take the screenshot
1285         ScreenCapture.SynchronousScreenCaptureListener syncScreenCapture =
1286                 ScreenCapture.createSyncCaptureListener();
1287         try {
1288             if (!mUiAutomationConnection.takeScreenshot(
1289                     new Rect(0, 0, displaySize.x, displaySize.y), syncScreenCapture, mDisplayId)) {
1290                 return null;
1291             }
1292         } catch (RemoteException re) {
1293             Log.e(LOG_TAG, "Error while taking screenshot of display " + mDisplayId, re);
1294             return null;
1295         }
1296 
1297         final ScreenshotHardwareBuffer screenshotBuffer = syncScreenCapture.getBuffer();
1298         if (screenshotBuffer == null) {
1299             Log.e(LOG_TAG, "Failed to take screenshot for display=" + mDisplayId);
1300             return null;
1301         }
1302         Bitmap screenShot = screenshotBuffer.asBitmap();
1303         if (screenShot == null) {
1304             Log.e(LOG_TAG, "Failed to take screenshot for display=" + mDisplayId);
1305             return null;
1306         }
1307         Bitmap swBitmap;
1308         try (HardwareBuffer buffer = screenshotBuffer.getHardwareBuffer()) {
1309             swBitmap = screenShot.copy(Bitmap.Config.ARGB_8888, false);
1310         }
1311         screenShot.recycle();
1312 
1313         // Optimization
1314         swBitmap.setHasAlpha(false);
1315         return swBitmap;
1316     }
1317 
1318     /**
1319      * Used to capture a screenshot of a Window. This can return null in the following cases:
1320      * 1. Window content hasn't been layed out.
1321      * 2. Window doesn't have a valid SurfaceControl
1322      * 3. An error occurred in SurfaceFlinger when trying to take the screenshot.
1323      *
1324      * @param window Window to take a screenshot of
1325      *
1326      * @return The screenshot bitmap on success, null otherwise.
1327      */
1328     @Nullable
takeScreenshot(@onNull Window window)1329     public Bitmap takeScreenshot(@NonNull Window window) {
1330         if (window == null) {
1331             return null;
1332         }
1333 
1334         View decorView = window.peekDecorView();
1335         if (decorView == null) {
1336             return null;
1337         }
1338 
1339         ViewRootImpl viewRoot = decorView.getViewRootImpl();
1340         if (viewRoot == null) {
1341             return null;
1342         }
1343 
1344         SurfaceControl sc = viewRoot.getSurfaceControl();
1345         if (!sc.isValid()) {
1346             return null;
1347         }
1348 
1349         // Apply a sync transaction to ensure SurfaceFlinger is flushed before capturing a
1350         // screenshot.
1351         new SurfaceControl.Transaction().apply(true);
1352         ScreenCapture.SynchronousScreenCaptureListener syncScreenCapture =
1353                 ScreenCapture.createSyncCaptureListener();
1354         try {
1355             if (!mUiAutomationConnection.takeSurfaceControlScreenshot(sc, syncScreenCapture)) {
1356                 Log.e(LOG_TAG, "Failed to take screenshot for window=" + window);
1357                 return null;
1358             }
1359         } catch (RemoteException re) {
1360             Log.e(LOG_TAG, "Error while taking screenshot!", re);
1361             return null;
1362         }
1363         ScreenCapture.ScreenshotHardwareBuffer captureBuffer = syncScreenCapture.getBuffer();
1364         if (captureBuffer == null) {
1365             Log.e(LOG_TAG, "Failed to take screenshot for window=" + window);
1366             return null;
1367         }
1368         Bitmap screenShot = captureBuffer.asBitmap();
1369         if (screenShot == null) {
1370             Log.e(LOG_TAG, "Failed to take screenshot for window=" + window);
1371             return null;
1372         }
1373         Bitmap swBitmap;
1374         try (HardwareBuffer buffer = captureBuffer.getHardwareBuffer()) {
1375             swBitmap = screenShot.copy(Bitmap.Config.ARGB_8888, false);
1376         }
1377 
1378         screenShot.recycle();
1379         return swBitmap;
1380     }
1381 
1382     /**
1383      * Sets whether this UiAutomation to run in a "monkey" mode. Applications can query whether
1384      * they are executed in a "monkey" mode, i.e. run by a test framework, and avoid doing
1385      * potentially undesirable actions such as calling 911 or posting on public forums etc.
1386      *
1387      * @param enable whether to run in a "monkey" mode or not. Default is not.
1388      * @see ActivityManager#isUserAMonkey()
1389      */
setRunAsMonkey(boolean enable)1390     public void setRunAsMonkey(boolean enable) {
1391         try {
1392             ActivityManager.getService().setUserIsMonkey(enable);
1393         } catch (RemoteException re) {
1394             Log.e(LOG_TAG, "Error while setting run as monkey!", re);
1395         }
1396     }
1397 
1398     /**
1399      * Clears the frame statistics for the content of a given window. These
1400      * statistics contain information about the most recently rendered content
1401      * frames.
1402      *
1403      * @param windowId The window id.
1404      * @return Whether the window is present and its frame statistics
1405      *         were cleared.
1406      *
1407      * @throws IllegalStateException If the connection to the accessibility subsystem is not
1408      *            established.
1409      * @see android.view.WindowContentFrameStats
1410      * @see #getWindowContentFrameStats(int)
1411      * @see #getWindows()
1412      * @see AccessibilityWindowInfo#getId() AccessibilityWindowInfo.getId()
1413      */
clearWindowContentFrameStats(int windowId)1414     public boolean clearWindowContentFrameStats(int windowId) {
1415         synchronized (mLock) {
1416             throwIfNotConnectedLocked();
1417         }
1418         try {
1419             if (DEBUG) {
1420                 Log.i(LOG_TAG, "Clearing content frame stats for window: " + windowId);
1421             }
1422             // Calling out without a lock held.
1423             return mUiAutomationConnection.clearWindowContentFrameStats(windowId);
1424         } catch (RemoteException re) {
1425             Log.e(LOG_TAG, "Error clearing window content frame stats!", re);
1426         }
1427         return false;
1428     }
1429 
1430     /**
1431      * Gets the frame statistics for a given window. These statistics contain
1432      * information about the most recently rendered content frames.
1433      * <p>
1434      * A typical usage requires clearing the window frame statistics via {@link
1435      * #clearWindowContentFrameStats(int)} followed by an interaction with the UI and
1436      * finally getting the window frame statistics via calling this method.
1437      * </p>
1438      * <pre>
1439      * // Assume we have at least one window.
1440      * final int windowId = getWindows().get(0).getId();
1441      *
1442      * // Start with a clean slate.
1443      * uiAutimation.clearWindowContentFrameStats(windowId);
1444      *
1445      * // Do stuff with the UI.
1446      *
1447      * // Get the frame statistics.
1448      * WindowContentFrameStats stats = uiAutomation.getWindowContentFrameStats(windowId);
1449      * </pre>
1450      *
1451      * @param windowId The window id.
1452      * @return The window frame statistics, or null if the window is not present.
1453      *
1454      * @throws IllegalStateException If the connection to the accessibility subsystem is not
1455      *            established.
1456      * @see android.view.WindowContentFrameStats
1457      * @see #clearWindowContentFrameStats(int)
1458      * @see #getWindows()
1459      * @see AccessibilityWindowInfo#getId() AccessibilityWindowInfo.getId()
1460      */
getWindowContentFrameStats(int windowId)1461     public WindowContentFrameStats getWindowContentFrameStats(int windowId) {
1462         synchronized (mLock) {
1463             throwIfNotConnectedLocked();
1464         }
1465         try {
1466             if (DEBUG) {
1467                 Log.i(LOG_TAG, "Getting content frame stats for window: " + windowId);
1468             }
1469             // Calling out without a lock held.
1470             return mUiAutomationConnection.getWindowContentFrameStats(windowId);
1471         } catch (RemoteException re) {
1472             Log.e(LOG_TAG, "Error getting window content frame stats!", re);
1473         }
1474         return null;
1475     }
1476 
1477     /**
1478      * Clears the window animation rendering statistics. These statistics contain
1479      * information about the most recently rendered window animation frames, i.e.
1480      * for window transition animations.
1481      *
1482      * @see android.view.WindowAnimationFrameStats
1483      * @see #getWindowAnimationFrameStats()
1484      * @see android.R.styleable#WindowAnimation
1485      * @deprecated animation-frames are no-longer used. Use Shared
1486      *         <a href="https://perfetto.dev/docs/data-sources/frametimeline">FrameTimeline</a>
1487      *         jank metrics instead.
1488      */
1489     @Deprecated
clearWindowAnimationFrameStats()1490     public void clearWindowAnimationFrameStats() {
1491         try {
1492             if (DEBUG) {
1493                 Log.i(LOG_TAG, "Clearing window animation frame stats");
1494             }
1495             // Calling out without a lock held.
1496             mUiAutomationConnection.clearWindowAnimationFrameStats();
1497         } catch (RemoteException re) {
1498             Log.e(LOG_TAG, "Error clearing window animation frame stats!", re);
1499         }
1500     }
1501 
1502     /**
1503      * Gets the window animation frame statistics. These statistics contain
1504      * information about the most recently rendered window animation frames, i.e.
1505      * for window transition animations.
1506      *
1507      * <p>
1508      * A typical usage requires clearing the window animation frame statistics via
1509      * {@link #clearWindowAnimationFrameStats()} followed by an interaction that causes
1510      * a window transition which uses a window animation and finally getting the window
1511      * animation frame statistics by calling this method.
1512      * </p>
1513      * <pre>
1514      * // Start with a clean slate.
1515      * uiAutimation.clearWindowAnimationFrameStats();
1516      *
1517      * // Do stuff to trigger a window transition.
1518      *
1519      * // Get the frame statistics.
1520      * WindowAnimationFrameStats stats = uiAutomation.getWindowAnimationFrameStats();
1521      * </pre>
1522      *
1523      * @return The window animation frame statistics.
1524      *
1525      * @see android.view.WindowAnimationFrameStats
1526      * @see #clearWindowAnimationFrameStats()
1527      * @see android.R.styleable#WindowAnimation
1528      * @deprecated animation-frames are no-longer used.
1529      */
1530     @Deprecated
getWindowAnimationFrameStats()1531     public WindowAnimationFrameStats getWindowAnimationFrameStats() {
1532         try {
1533             if (DEBUG) {
1534                 Log.i(LOG_TAG, "Getting window animation frame stats");
1535             }
1536             // Calling out without a lock held.
1537             return mUiAutomationConnection.getWindowAnimationFrameStats();
1538         } catch (RemoteException re) {
1539             Log.e(LOG_TAG, "Error getting window animation frame stats!", re);
1540         }
1541         return null;
1542     }
1543 
1544     /**
1545      * Grants a runtime permission to a package.
1546      *
1547      * @param packageName The package to which to grant.
1548      * @param permission The permission to grant.
1549      * @throws SecurityException if unable to grant the permission.
1550      */
grantRuntimePermission(String packageName, String permission)1551     public void grantRuntimePermission(String packageName, String permission) {
1552         grantRuntimePermissionAsUser(packageName, permission, android.os.Process.myUserHandle());
1553     }
1554 
1555     /**
1556      * @deprecated replaced by
1557      *             {@link #grantRuntimePermissionAsUser(String, String, UserHandle)}.
1558      * @hide
1559      */
1560     @Deprecated
1561     @TestApi
grantRuntimePermission(String packageName, String permission, UserHandle userHandle)1562     public boolean grantRuntimePermission(String packageName, String permission,
1563             UserHandle userHandle) {
1564         grantRuntimePermissionAsUser(packageName, permission, userHandle);
1565         return true;
1566     }
1567 
1568     /**
1569      * Grants a runtime permission to a package for a user.
1570      *
1571      * @param packageName The package to which to grant.
1572      * @param permission The permission to grant.
1573      * @throws SecurityException if unable to grant the permission.
1574      */
grantRuntimePermissionAsUser(String packageName, String permission, UserHandle userHandle)1575     public void grantRuntimePermissionAsUser(String packageName, String permission,
1576             UserHandle userHandle) {
1577         try {
1578             if (DEBUG) {
1579                 Log.i(LOG_TAG, "Granting runtime permission (" + permission + ") to package "
1580                         + packageName + " on user " + userHandle);
1581             }
1582             // Calling out without a lock held.
1583             mUiAutomationConnection.grantRuntimePermission(packageName,
1584                     permission, userHandle.getIdentifier());
1585         } catch (Exception e) {
1586             throw new SecurityException("Error granting runtime permission", e);
1587         }
1588     }
1589 
1590     /**
1591      * Revokes a runtime permission from a package.
1592      *
1593      * @param packageName The package to which to grant.
1594      * @param permission The permission to grant.
1595      * @throws SecurityException if unable to revoke the permission.
1596      */
revokeRuntimePermission(String packageName, String permission)1597     public void revokeRuntimePermission(String packageName, String permission) {
1598         revokeRuntimePermissionAsUser(packageName, permission, android.os.Process.myUserHandle());
1599     }
1600 
1601     /**
1602      * @deprecated replaced by
1603      *             {@link #revokeRuntimePermissionAsUser(String, String, UserHandle)}.
1604      * @hide
1605      */
1606     @Deprecated
1607     @TestApi
revokeRuntimePermission(String packageName, String permission, UserHandle userHandle)1608     public boolean revokeRuntimePermission(String packageName, String permission,
1609             UserHandle userHandle) {
1610         revokeRuntimePermissionAsUser(packageName, permission, userHandle);
1611         return true;
1612     }
1613 
1614     /**
1615      * Revokes a runtime permission from a package.
1616      *
1617      * @param packageName The package to which to grant.
1618      * @param permission The permission to grant.
1619      * @throws SecurityException if unable to revoke the permission.
1620      */
revokeRuntimePermissionAsUser(String packageName, String permission, UserHandle userHandle)1621     public void revokeRuntimePermissionAsUser(String packageName, String permission,
1622             UserHandle userHandle) {
1623         try {
1624             if (DEBUG) {
1625                 Log.i(LOG_TAG, "Revoking runtime permission");
1626             }
1627             // Calling out without a lock held.
1628             mUiAutomationConnection.revokeRuntimePermission(packageName,
1629                     permission, userHandle.getIdentifier());
1630         } catch (Exception e) {
1631             throw new SecurityException("Error granting runtime permission", e);
1632         }
1633     }
1634 
1635     /**
1636      * Executes a shell command. This method returns a file descriptor that points
1637      * to the standard output stream. The command execution is similar to running
1638      * "adb shell <command>" from a host connected to the device.
1639      * <p>
1640      * <strong>Note:</strong> It is your responsibility to close the returned file
1641      * descriptor once you are done reading.
1642      * </p>
1643      *
1644      * @param command The command to execute.
1645      * @return A file descriptor to the standard output stream.
1646      *
1647      * @see #adoptShellPermissionIdentity()
1648      */
executeShellCommand(String command)1649     public ParcelFileDescriptor executeShellCommand(String command) {
1650         warnIfBetterCommand(command);
1651 
1652         ParcelFileDescriptor source = null;
1653         ParcelFileDescriptor sink = null;
1654 
1655         try {
1656             ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
1657             source = pipe[0];
1658             sink = pipe[1];
1659 
1660             // Calling out without a lock held.
1661             mUiAutomationConnection.executeShellCommand(command, sink, null);
1662         } catch (IOException | RemoteException e) {
1663             Log.e(LOG_TAG, "Error executing shell command!", e);
1664         } catch (IllegalArgumentException | NullPointerException | SecurityException e) {
1665             // An exception of these types is propagated from the server.
1666             // Rethrow it to keep the old behavior. To avoid FD leak, close the source.
1667             IoUtils.closeQuietly(source);
1668             throw e;
1669         } finally {
1670             IoUtils.closeQuietly(sink);
1671         }
1672 
1673         return source;
1674     }
1675 
1676     /**
1677      * Executes a shell command. This method returns two file descriptors,
1678      * one that points to the standard output stream (element at index 0), and one that points
1679      * to the standard input stream (element at index 1). The command execution is similar
1680      * to running "adb shell <command>" from a host connected to the device.
1681      * <p>
1682      * <strong>Note:</strong> It is your responsibility to close the returned file
1683      * descriptors once you are done reading/writing.
1684      * </p>
1685      *
1686      * @param command The command to execute.
1687      * @return File descriptors (out, in) to the standard output/input streams.
1688      */
1689     @SuppressLint("ArrayReturn") // For consistency with other APIs
executeShellCommandRw(@onNull String command)1690     public @NonNull ParcelFileDescriptor[] executeShellCommandRw(@NonNull String command) {
1691         return executeShellCommandInternal(command, false /* includeStderr */);
1692     }
1693 
1694     /**
1695      * Executes a shell command. This method returns three file descriptors,
1696      * one that points to the standard output stream (element at index 0), one that points
1697      * to the standard input stream (element at index 1), and one points to
1698      * standard error stream (element at index 2). The command execution is similar
1699      * to running "adb shell <command>" from a host connected to the device.
1700      * <p>
1701      * <strong>Note:</strong> It is your responsibility to close the returned file
1702      * descriptors once you are done reading/writing.
1703      * </p>
1704      *
1705      * @param command The command to execute.
1706      * @return File descriptors (out, in, err) to the standard output/input/error streams.
1707      */
1708     @SuppressLint("ArrayReturn") // For consistency with other APIs
executeShellCommandRwe(@onNull String command)1709     public @NonNull ParcelFileDescriptor[] executeShellCommandRwe(@NonNull String command) {
1710         return executeShellCommandInternal(command, true /* includeStderr */);
1711     }
1712 
1713     /**
1714      * @hide
1715      */
1716     @VisibleForTesting
getDisplayId()1717     public int getDisplayId() {
1718         return mDisplayId;
1719     }
1720 
executeShellCommandInternal( String command, boolean includeStderr)1721     private ParcelFileDescriptor[] executeShellCommandInternal(
1722             String command, boolean includeStderr) {
1723         warnIfBetterCommand(command);
1724 
1725         ParcelFileDescriptor source_read = null;
1726         ParcelFileDescriptor sink_read = null;
1727 
1728         ParcelFileDescriptor source_write = null;
1729         ParcelFileDescriptor sink_write = null;
1730 
1731         ParcelFileDescriptor stderr_source_read = null;
1732         ParcelFileDescriptor stderr_sink_read = null;
1733 
1734         try {
1735             ParcelFileDescriptor[] pipe_read = ParcelFileDescriptor.createPipe();
1736             source_read = pipe_read[0];
1737             sink_read = pipe_read[1];
1738 
1739             ParcelFileDescriptor[] pipe_write = ParcelFileDescriptor.createPipe();
1740             source_write = pipe_write[0];
1741             sink_write = pipe_write[1];
1742 
1743             if (includeStderr) {
1744                 ParcelFileDescriptor[] stderr_read = ParcelFileDescriptor.createPipe();
1745                 stderr_source_read = stderr_read[0];
1746                 stderr_sink_read = stderr_read[1];
1747             }
1748 
1749             // Calling out without a lock held.
1750             mUiAutomationConnection.executeShellCommandWithStderr(
1751                     command, sink_read, source_write, stderr_sink_read);
1752         } catch (IOException | RemoteException e) {
1753             Log.e(LOG_TAG, "Error executing shell command!", e);
1754         } catch (IllegalArgumentException | SecurityException | NullPointerException e) {
1755             // An exception of these types is propagated from the server.
1756             // Rethrow it to keep the old behavior. To avoid FD leaks, close the sources.
1757             IoUtils.closeQuietly(sink_write);
1758             IoUtils.closeQuietly(source_read);
1759             IoUtils.closeQuietly(stderr_source_read);
1760             throw e;
1761         } finally {
1762             IoUtils.closeQuietly(sink_read);
1763             IoUtils.closeQuietly(source_write);
1764             IoUtils.closeQuietly(stderr_sink_read);
1765         }
1766 
1767         ParcelFileDescriptor[] result = new ParcelFileDescriptor[includeStderr ? 3 : 2];
1768         result[0] = source_read;
1769         result[1] = sink_write;
1770         if (includeStderr) {
1771             result[2] = stderr_source_read;
1772         }
1773         return result;
1774     }
1775 
1776     @Override
toString()1777     public String toString() {
1778         final StringBuilder stringBuilder = new StringBuilder();
1779         stringBuilder.append("UiAutomation@").append(Integer.toHexString(hashCode()));
1780         stringBuilder.append("[id=").append(mConnectionId);
1781         stringBuilder.append(", displayId=").append(mDisplayId);
1782         stringBuilder.append(", flags=").append(mFlags);
1783         stringBuilder.append("]");
1784         return stringBuilder.toString();
1785     }
1786 
1787     @GuardedBy("mLock")
throwIfConnectedLocked()1788     private void throwIfConnectedLocked() {
1789         if (mConnectionState == ConnectionState.CONNECTED) {
1790             throw new IllegalStateException("UiAutomation connected, " + this);
1791         }
1792     }
1793 
1794     @GuardedBy("mLock")
throwIfNotConnectedLocked()1795     private void throwIfNotConnectedLocked() {
1796         if (mConnectionState != ConnectionState.CONNECTED) {
1797             final String msg = useAccessibility()
1798                     ? "UiAutomation not connected, "
1799                     : "UiAutomation not connected: Accessibility-dependent method called with "
1800                             + "FLAG_DONT_USE_ACCESSIBILITY set, ";
1801             throw new IllegalStateException(msg + this);
1802         }
1803     }
1804 
warnIfBetterCommand(String cmd)1805     private void warnIfBetterCommand(String cmd) {
1806         if (cmd.startsWith("pm grant ")) {
1807             Log.w(LOG_TAG, "UiAutomation.grantRuntimePermission() "
1808                     + "is more robust and should be used instead of 'pm grant'");
1809         } else if (cmd.startsWith("pm revoke ")) {
1810             Log.w(LOG_TAG, "UiAutomation.revokeRuntimePermission() "
1811                     + "is more robust and should be used instead of 'pm revoke'");
1812         }
1813     }
1814 
useAccessibility()1815     private boolean useAccessibility() {
1816         return (mFlags & UiAutomation.FLAG_DONT_USE_ACCESSIBILITY) == 0;
1817     }
1818 
1819     /**
1820      * Gets the display id associated with the UiAutomation context.
1821      *
1822      * <p><b>NOTE: </b> must be a static method because it's called from a constructor to call
1823      * another one.
1824      */
getDisplayId(Context context)1825     private static int getDisplayId(Context context) {
1826         Preconditions.checkArgument(context != null, "Context cannot be null!");
1827 
1828         UserManager userManager = context.getSystemService(UserManager.class);
1829         // TODO(b/255426725): given that this is a temporary solution until a11y supports multiple
1830         // users, the display is only set on devices that support that
1831         if (!userManager.isVisibleBackgroundUsersSupported()) {
1832             return DEFAULT_DISPLAY;
1833         }
1834 
1835         int displayId = context.getDisplayId();
1836         if (displayId == Display.INVALID_DISPLAY) {
1837             // Shouldn't happen, but we better handle it
1838             Log.e(LOG_TAG, "UiAutomation created UI context with invalid display id, assuming it's"
1839                     + " running in the display assigned to the user");
1840             return getMainDisplayIdAssignedToUser(context, userManager);
1841         }
1842 
1843         if (displayId != DEFAULT_DISPLAY) {
1844             if (DEBUG) {
1845                 Log.d(LOG_TAG, "getDisplayId(): returning context's display (" + displayId + ")");
1846             }
1847             // Context is explicitly setting the display, so we respect that...
1848             return displayId;
1849         }
1850         // ...otherwise, we need to get the display the test's user is running on
1851         int userDisplayId = getMainDisplayIdAssignedToUser(context, userManager);
1852         if (DEBUG) {
1853             Log.d(LOG_TAG, "getDisplayId(): returning user's display (" + userDisplayId + ")");
1854         }
1855         return userDisplayId;
1856     }
1857 
getMainDisplayIdAssignedToUser(Context context, UserManager userManager)1858     private static int getMainDisplayIdAssignedToUser(Context context, UserManager userManager) {
1859         if (!userManager.isUserVisible()) {
1860             // Should also not happen, but ...
1861             Log.e(LOG_TAG, "User (" + context.getUserId() + ") is not visible, using "
1862                     + "DEFAULT_DISPLAY");
1863             return DEFAULT_DISPLAY;
1864         }
1865         return userManager.getMainDisplayIdAssignedToUser();
1866     }
1867 
1868     private class IAccessibilityServiceClientImpl extends IAccessibilityServiceClientWrapper {
1869 
IAccessibilityServiceClientImpl(Looper looper, int generationId)1870         public IAccessibilityServiceClientImpl(Looper looper, int generationId) {
1871             super(/* context= */ null, looper, new Callbacks() {
1872                 private final int mGenerationId = generationId;
1873 
1874                 /**
1875                  * True if UiAutomation doesn't interact with this client anymore.
1876                  * Used by methods below to stop sending notifications or changing members
1877                  * of {@link UiAutomation}.
1878                  */
1879                 private boolean isGenerationChangedLocked() {
1880                     return mGenerationId != UiAutomation.this.mGenerationId;
1881                 }
1882 
1883                 @Override
1884                 public void init(int connectionId, IBinder windowToken) {
1885                     if (DEBUG) {
1886                         Log.d(LOG_TAG, "init(): connectionId=" + connectionId + ", windowToken="
1887                                 + windowToken + ", user=" + Process.myUserHandle()
1888                                 + ", UiAutomation.mDisplay=" + UiAutomation.this.mDisplayId
1889                                 + ", mGenerationId=" + mGenerationId
1890                                 + ", UiAutomation.mGenerationId="
1891                                 + UiAutomation.this.mGenerationId);
1892                     }
1893                     synchronized (mLock) {
1894                         if (isGenerationChangedLocked()) {
1895                             if (DEBUG) {
1896                                 Log.d(LOG_TAG, "init(): returning because generation id changed");
1897                             }
1898                             return;
1899                         }
1900                         if (DEBUG) Log.d(LOG_TAG, "setting state to CONNECTED");
1901                         mConnectionState = ConnectionState.CONNECTED;
1902                         mConnectionId = connectionId;
1903                         mLock.notifyAll();
1904                     }
1905                     if (Build.IS_DEBUGGABLE) {
1906                         Log.v(LOG_TAG, "Init " + UiAutomation.this);
1907                     }
1908                 }
1909 
1910                 @Override
1911                 public void onServiceConnected() {
1912                     /* do nothing */
1913                 }
1914 
1915                 @Override
1916                 public void onInterrupt() {
1917                     /* do nothing */
1918                 }
1919 
1920                 @Override
1921                 public void onSystemActionsChanged() {
1922                     /* do nothing */
1923                 }
1924 
1925                 @Override
1926                 public void createImeSession(IAccessibilityInputMethodSessionCallback callback) {
1927                     /* do nothing */
1928                 }
1929 
1930                 @Override
1931                 public void startInput(
1932                         @Nullable RemoteAccessibilityInputConnection inputConnection,
1933                         @NonNull EditorInfo editorInfo, boolean restarting) {
1934                 }
1935 
1936                 @Override
1937                 public boolean onGesture(AccessibilityGestureEvent gestureEvent) {
1938                     /* do nothing */
1939                     return false;
1940                 }
1941 
1942                 public void onMotionEvent(MotionEvent event) {
1943                     /* do nothing */
1944                 }
1945 
1946                 @Override
1947                 public void onTouchStateChanged(int displayId, int state) {
1948                     /* do nothing */
1949                 }
1950 
1951                 @Override
1952                 public void onAccessibilityEvent(AccessibilityEvent event) {
1953                     if (VERBOSE) {
1954                         Log.v(LOG_TAG, "onAccessibilityEvent(" + Process.myUserHandle() + "): "
1955                                 + event);
1956                     }
1957 
1958                     final OnAccessibilityEventListener listener;
1959                     synchronized (mLock) {
1960                         if (isGenerationChangedLocked()) {
1961                             if (VERBOSE) {
1962                                 Log.v(LOG_TAG, "onAccessibilityEvent(): returning because "
1963                                         + "generation id changed (from "
1964                                         + UiAutomation.this.mGenerationId + " to "
1965                                         + mGenerationId + ")");
1966                             }
1967                             return;
1968                         }
1969                         // It is not guaranteed that the accessibility framework sends events by the
1970                         // order of event timestamp.
1971                         mLastEventTimeMillis = Math.max(mLastEventTimeMillis, event.getEventTime());
1972                         if (mCurrentEventWatchersCount > 0) {
1973                             mEventQueue.add(AccessibilityEvent.obtain(event));
1974                         }
1975                         mLock.notifyAll();
1976                         listener = mOnAccessibilityEventListener;
1977                     }
1978                     if (listener != null) {
1979                         // Calling out only without a lock held.
1980                         mLocalCallbackHandler.sendMessage(PooledLambda.obtainMessage(
1981                                 OnAccessibilityEventListener::onAccessibilityEvent,
1982                                 listener, AccessibilityEvent.obtain(event)));
1983                     }
1984                 }
1985 
1986                 @Override
1987                 public boolean onKeyEvent(KeyEvent event) {
1988                     return false;
1989                 }
1990 
1991                 @Override
1992                 public void onMagnificationChanged(int displayId, @NonNull Region region,
1993                         MagnificationConfig config) {
1994                     /* do nothing */
1995                 }
1996 
1997                 @Override
1998                 public void onSoftKeyboardShowModeChanged(int showMode) {
1999                     /* do nothing */
2000                 }
2001 
2002                 @Override
2003                 public void onPerformGestureResult(int sequence, boolean completedSuccessfully) {
2004                     /* do nothing */
2005                 }
2006 
2007                 @Override
2008                 public void onFingerprintCapturingGesturesChanged(boolean active) {
2009                     /* do nothing */
2010                 }
2011 
2012                 @Override
2013                 public void onFingerprintGesture(int gesture) {
2014                     /* do nothing */
2015                 }
2016 
2017                 @Override
2018                 public void onAccessibilityButtonClicked(int displayId) {
2019                     /* do nothing */
2020                 }
2021 
2022                 @Override
2023                 public void onAccessibilityButtonAvailabilityChanged(boolean available) {
2024                     /* do nothing */
2025                 }
2026             });
2027         }
2028     }
2029 }
2030