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