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