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