1 /* 2 * Copyright (C) 2009 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.accessibilityservice; 18 19 import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN; 20 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; 21 22 import android.accessibilityservice.GestureDescription.MotionEventGenerator; 23 import android.annotation.CallbackExecutor; 24 import android.annotation.CheckResult; 25 import android.annotation.ColorInt; 26 import android.annotation.IntDef; 27 import android.annotation.NonNull; 28 import android.annotation.Nullable; 29 import android.annotation.RequiresPermission; 30 import android.annotation.TestApi; 31 import android.app.Service; 32 import android.compat.annotation.UnsupportedAppUsage; 33 import android.content.Context; 34 import android.content.ContextWrapper; 35 import android.content.Intent; 36 import android.content.pm.ParceledListSlice; 37 import android.graphics.Bitmap; 38 import android.graphics.ColorSpace; 39 import android.graphics.ParcelableColorSpace; 40 import android.graphics.Region; 41 import android.hardware.HardwareBuffer; 42 import android.hardware.display.DisplayManager; 43 import android.os.Build; 44 import android.os.Bundle; 45 import android.os.Handler; 46 import android.os.HandlerExecutor; 47 import android.os.IBinder; 48 import android.os.Looper; 49 import android.os.RemoteCallback; 50 import android.os.RemoteException; 51 import android.os.SystemClock; 52 import android.util.ArrayMap; 53 import android.util.Log; 54 import android.util.Slog; 55 import android.util.SparseArray; 56 import android.view.Display; 57 import android.view.InputDevice; 58 import android.view.KeyEvent; 59 import android.view.MotionEvent; 60 import android.view.SurfaceControl; 61 import android.view.WindowManager; 62 import android.view.WindowManagerImpl; 63 import android.view.accessibility.AccessibilityCache; 64 import android.view.accessibility.AccessibilityEvent; 65 import android.view.accessibility.AccessibilityInteractionClient; 66 import android.view.accessibility.AccessibilityNodeInfo; 67 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; 68 import android.view.accessibility.AccessibilityWindowInfo; 69 import android.view.inputmethod.EditorInfo; 70 71 import com.android.internal.inputmethod.CancellationGroup; 72 import com.android.internal.inputmethod.IAccessibilityInputMethodSession; 73 import com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback; 74 import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection; 75 import com.android.internal.inputmethod.RemoteAccessibilityInputConnection; 76 import com.android.internal.util.Preconditions; 77 78 import java.lang.annotation.Retention; 79 import java.lang.annotation.RetentionPolicy; 80 import java.util.Collections; 81 import java.util.List; 82 import java.util.concurrent.Executor; 83 import java.util.function.Consumer; 84 85 /** 86 * Accessibility services should only be used to assist users with disabilities in using 87 * Android devices and apps. They run in the background and receive callbacks by the system 88 * when {@link AccessibilityEvent}s are fired. Such events denote some state transition 89 * in the user interface, for example, the focus has changed, a button has been clicked, 90 * etc. Such a service can optionally request the capability for querying the content 91 * of the active window. Development of an accessibility service requires extending this 92 * class and implementing its abstract methods. 93 * 94 * <div class="special reference"> 95 * <h3>Developer Guides</h3> 96 * <p>For more information about creating AccessibilityServices, read the 97 * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a> 98 * developer guide.</p> 99 * </div> 100 * 101 * <h3>Lifecycle</h3> 102 * <p> 103 * The lifecycle of an accessibility service is managed exclusively by the system and 104 * follows the established service life cycle. Starting an accessibility service is triggered 105 * exclusively by the user explicitly turning the service on in device settings. After the system 106 * binds to a service, it calls {@link AccessibilityService#onServiceConnected()}. This method can 107 * be overridden by clients that want to perform post binding setup. 108 * </p> 109 * <p> 110 * An accessibility service stops either when the user turns it off in device settings or when 111 * it calls {@link AccessibilityService#disableSelf()}. 112 * </p> 113 * <h3>Declaration</h3> 114 * <p> 115 * An accessibility is declared as any other service in an AndroidManifest.xml, but it 116 * must do two things: 117 * <ul> 118 * <li> 119 * Specify that it handles the "android.accessibilityservice.AccessibilityService" 120 * {@link android.content.Intent}. 121 * </li> 122 * <li> 123 * Request the {@link android.Manifest.permission#BIND_ACCESSIBILITY_SERVICE} permission to 124 * ensure that only the system can bind to it. 125 * </li> 126 * </ul> 127 * If either of these items is missing, the system will ignore the accessibility service. 128 * Following is an example declaration: 129 * </p> 130 * <pre> <service android:name=".MyAccessibilityService" 131 * android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"> 132 * <intent-filter> 133 * <action android:name="android.accessibilityservice.AccessibilityService" /> 134 * </intent-filter> 135 * . . . 136 * </service></pre> 137 * <h3>Configuration</h3> 138 * <p> 139 * An accessibility service can be configured to receive specific types of accessibility events, 140 * listen only to specific packages, get events from each type only once in a given time frame, 141 * retrieve window content, specify a settings activity, etc. 142 * </p> 143 * <p> 144 * There are two approaches for configuring an accessibility service: 145 * </p> 146 * <ul> 147 * <li> 148 * Providing a {@link #SERVICE_META_DATA meta-data} entry in the manifest when declaring 149 * the service. A service declaration with a meta-data tag is presented below: 150 * <pre> <service android:name=".MyAccessibilityService"> 151 * <intent-filter> 152 * <action android:name="android.accessibilityservice.AccessibilityService" /> 153 * </intent-filter> 154 * <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibilityservice" /> 155 * </service></pre> 156 * <p class="note"> 157 * <strong>Note:</strong> This approach enables setting all properties. 158 * </p> 159 * <p> 160 * For more details refer to {@link #SERVICE_META_DATA} and 161 * <code><{@link android.R.styleable#AccessibilityService accessibility-service}></code>. 162 * </p> 163 * </li> 164 * <li> 165 * Calling {@link AccessibilityService#setServiceInfo(AccessibilityServiceInfo)}. Note 166 * that this method can be called any time to dynamically change the service configuration. 167 * <p class="note"> 168 * <strong>Note:</strong> This approach enables setting only dynamically configurable properties: 169 * {@link AccessibilityServiceInfo#eventTypes}, 170 * {@link AccessibilityServiceInfo#feedbackType}, 171 * {@link AccessibilityServiceInfo#flags}, 172 * {@link AccessibilityServiceInfo#notificationTimeout}, 173 * {@link AccessibilityServiceInfo#packageNames} 174 * </p> 175 * <p> 176 * For more details refer to {@link AccessibilityServiceInfo}. 177 * </p> 178 * </li> 179 * </ul> 180 * <h3>Retrieving window content</h3> 181 * <p> 182 * A service can specify in its declaration that it can retrieve window 183 * content which is represented as a tree of {@link AccessibilityWindowInfo} and 184 * {@link AccessibilityNodeInfo} objects. Note that 185 * declaring this capability requires that the service declares its configuration via 186 * an XML resource referenced by {@link #SERVICE_META_DATA}. 187 * </p> 188 * <p> 189 * Window content may be retrieved with 190 * {@link AccessibilityEvent#getSource() AccessibilityEvent.getSource()}, 191 * {@link AccessibilityService#findFocus(int)}, 192 * {@link AccessibilityService#getWindows()}, or 193 * {@link AccessibilityService#getRootInActiveWindow()}. 194 * </p> 195 * <p class="note"> 196 * <strong>Note</strong> An accessibility service may have requested to be notified for 197 * a subset of the event types, and thus be unaware when the node hierarchy has changed. It is also 198 * possible for a node to contain outdated information because the window content may change at any 199 * time. 200 * </p> 201 * <h3>Drawing Accessibility Overlays</h3> 202 * <p>Accessibility services can draw overlays on top of existing screen contents. 203 * Accessibility overlays can be used to visually highlight items on the screen 204 * e.g. indicate the current item with accessibility focus. 205 * Overlays can also offer the user a way to interact with the service directly and quickly 206 * customize the service's behavior.</p> 207 * <p>Accessibility overlays can be attached to a particular window or to the display itself. 208 * Attaching an overlay to a window allows the overly to move, grow and shrink as the window does. 209 * The overlay will maintain the same relative position within the window bounds as the window 210 * moves. The overlay will also maintain the same relative position within the window bounds if 211 * the window is resized. 212 * To attach an overlay to a window, use {@link #attachAccessibilityOverlayToWindow}. 213 * Attaching an overlay to the display means that the overlay is independent of the active 214 * windows on that display. 215 * To attach an overlay to a display, use {@link #attachAccessibilityOverlayToDisplay}. </p> 216 * <p> When positioning an overlay that is attached to a window, the service must use window 217 * coordinates. In order to position an overlay on top of an existing UI element it is necessary 218 * to know the bounds of that element in window coordinates. To find the bounds in window 219 * coordinates of an element, find the corresponding {@link AccessibilityNodeInfo} as discussed 220 * above and call {@link AccessibilityNodeInfo#getBoundsInWindow}. </p> 221 * <h3>Notification strategy</h3> 222 * <p> 223 * All accessibility services are notified of all events they have requested, regardless of their 224 * feedback type. 225 * </p> 226 * <p class="note"> 227 * <strong>Note:</strong> The event notification timeout is useful to avoid propagating 228 * events to the client too frequently since this is accomplished via an expensive 229 * interprocess call. One can think of the timeout as a criteria to determine when 230 * event generation has settled down.</p> 231 * <h3>Event types</h3> 232 * <ul> 233 * <li>{@link AccessibilityEvent#TYPE_VIEW_CLICKED}</li> 234 * <li>{@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED}</li> 235 * <li>{@link AccessibilityEvent#TYPE_VIEW_FOCUSED}</li> 236 * <li>{@link AccessibilityEvent#TYPE_VIEW_SELECTED}</li> 237 * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED}</li> 238 * <li>{@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}</li> 239 * <li>{@link AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED}</li> 240 * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START}</li> 241 * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END}</li> 242 * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER}</li> 243 * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}</li> 244 * <li>{@link AccessibilityEvent#TYPE_VIEW_SCROLLED}</li> 245 * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED}</li> 246 * <li>{@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED}</li> 247 * <li>{@link AccessibilityEvent#TYPE_ANNOUNCEMENT}</li> 248 * <li>{@link AccessibilityEvent#TYPE_GESTURE_DETECTION_START}</li> 249 * <li>{@link AccessibilityEvent#TYPE_GESTURE_DETECTION_END}</li> 250 * <li>{@link AccessibilityEvent#TYPE_TOUCH_INTERACTION_START}</li> 251 * <li>{@link AccessibilityEvent#TYPE_TOUCH_INTERACTION_END}</li> 252 * <li>{@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUSED}</li> 253 * <li>{@link AccessibilityEvent#TYPE_WINDOWS_CHANGED}</li> 254 * <li>{@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED}</li> 255 * </ul> 256 * <h3>Feedback types</h3> 257 * <ul> 258 * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}</li> 259 * <li>{@link AccessibilityServiceInfo#FEEDBACK_HAPTIC}</li> 260 * <li>{@link AccessibilityServiceInfo#FEEDBACK_SPOKEN}</li> 261 * <li>{@link AccessibilityServiceInfo#FEEDBACK_VISUAL}</li> 262 * <li>{@link AccessibilityServiceInfo#FEEDBACK_GENERIC}</li> 263 * <li>{@link AccessibilityServiceInfo#FEEDBACK_BRAILLE}</li> 264 * </ul> 265 * @see AccessibilityEvent 266 * @see AccessibilityServiceInfo 267 * @see android.view.accessibility.AccessibilityManager 268 */ 269 public abstract class AccessibilityService extends Service { 270 271 /** 272 * The user has performed a touch-exploration gesture on the touch screen without ever 273 * triggering gesture detection. This gesture is only dispatched when {@link 274 * AccessibilityServiceInfo#FLAG_SEND_MOTION_EVENTS} is set. 275 * 276 * @hide 277 */ 278 public static final int GESTURE_TOUCH_EXPLORATION = -2; 279 280 /** 281 * The user has performed a passthrough gesture on the touch screen without ever triggering 282 * gesture detection. This gesture is only dispatched when {@link 283 * AccessibilityServiceInfo#FLAG_SEND_MOTION_EVENTS} is set. 284 * @hide 285 */ 286 public static final int GESTURE_PASSTHROUGH = -1; 287 288 /** 289 * The user has performed an unrecognized gesture on the touch screen. This gesture is only 290 * dispatched when {@link AccessibilityServiceInfo#FLAG_SEND_MOTION_EVENTS} is set. 291 */ 292 public static final int GESTURE_UNKNOWN = 0; 293 294 /** 295 * The user has performed a swipe up gesture on the touch screen. 296 */ 297 public static final int GESTURE_SWIPE_UP = 1; 298 299 /** 300 * The user has performed a swipe down gesture on the touch screen. 301 */ 302 public static final int GESTURE_SWIPE_DOWN = 2; 303 304 /** 305 * The user has performed a swipe left gesture on the touch screen. 306 */ 307 public static final int GESTURE_SWIPE_LEFT = 3; 308 309 /** 310 * The user has performed a swipe right gesture on the touch screen. 311 */ 312 public static final int GESTURE_SWIPE_RIGHT = 4; 313 314 /** 315 * The user has performed a swipe left and right gesture on the touch screen. 316 */ 317 public static final int GESTURE_SWIPE_LEFT_AND_RIGHT = 5; 318 319 /** 320 * The user has performed a swipe right and left gesture on the touch screen. 321 */ 322 public static final int GESTURE_SWIPE_RIGHT_AND_LEFT = 6; 323 324 /** 325 * The user has performed a swipe up and down gesture on the touch screen. 326 */ 327 public static final int GESTURE_SWIPE_UP_AND_DOWN = 7; 328 329 /** 330 * The user has performed a swipe down and up gesture on the touch screen. 331 */ 332 public static final int GESTURE_SWIPE_DOWN_AND_UP = 8; 333 334 /** 335 * The user has performed a left and up gesture on the touch screen. 336 */ 337 public static final int GESTURE_SWIPE_LEFT_AND_UP = 9; 338 339 /** 340 * The user has performed a left and down gesture on the touch screen. 341 */ 342 public static final int GESTURE_SWIPE_LEFT_AND_DOWN = 10; 343 344 /** 345 * The user has performed a right and up gesture on the touch screen. 346 */ 347 public static final int GESTURE_SWIPE_RIGHT_AND_UP = 11; 348 349 /** 350 * The user has performed a right and down gesture on the touch screen. 351 */ 352 public static final int GESTURE_SWIPE_RIGHT_AND_DOWN = 12; 353 354 /** 355 * The user has performed an up and left gesture on the touch screen. 356 */ 357 public static final int GESTURE_SWIPE_UP_AND_LEFT = 13; 358 359 /** 360 * The user has performed an up and right gesture on the touch screen. 361 */ 362 public static final int GESTURE_SWIPE_UP_AND_RIGHT = 14; 363 364 /** 365 * The user has performed an down and left gesture on the touch screen. 366 */ 367 public static final int GESTURE_SWIPE_DOWN_AND_LEFT = 15; 368 369 /** 370 * The user has performed an down and right gesture on the touch screen. 371 */ 372 public static final int GESTURE_SWIPE_DOWN_AND_RIGHT = 16; 373 374 /** 375 * The user has performed a double tap gesture on the touch screen. 376 */ 377 public static final int GESTURE_DOUBLE_TAP = 17; 378 379 /** 380 * The user has performed a double tap and hold gesture on the touch screen. 381 */ 382 public static final int GESTURE_DOUBLE_TAP_AND_HOLD = 18; 383 384 /** 385 * The user has performed a two-finger single tap gesture on the touch screen. 386 */ 387 public static final int GESTURE_2_FINGER_SINGLE_TAP = 19; 388 389 /** 390 * The user has performed a two-finger double tap gesture on the touch screen. 391 */ 392 public static final int GESTURE_2_FINGER_DOUBLE_TAP = 20; 393 394 /** 395 * The user has performed a two-finger triple tap gesture on the touch screen. 396 */ 397 public static final int GESTURE_2_FINGER_TRIPLE_TAP = 21; 398 399 /** 400 * The user has performed a three-finger single tap gesture on the touch screen. 401 */ 402 public static final int GESTURE_3_FINGER_SINGLE_TAP = 22; 403 404 /** 405 * The user has performed a three-finger double tap gesture on the touch screen. 406 */ 407 public static final int GESTURE_3_FINGER_DOUBLE_TAP = 23; 408 409 /** 410 * The user has performed a three-finger triple tap gesture on the touch screen. 411 */ 412 public static final int GESTURE_3_FINGER_TRIPLE_TAP = 24; 413 414 /** 415 * The user has performed a two-finger swipe up gesture on the touch screen. 416 */ 417 public static final int GESTURE_2_FINGER_SWIPE_UP = 25; 418 419 /** 420 * The user has performed a two-finger swipe down gesture on the touch screen. 421 */ 422 public static final int GESTURE_2_FINGER_SWIPE_DOWN = 26; 423 424 /** 425 * The user has performed a two-finger swipe left gesture on the touch screen. 426 */ 427 public static final int GESTURE_2_FINGER_SWIPE_LEFT = 27; 428 429 /** 430 * The user has performed a two-finger swipe right gesture on the touch screen. 431 */ 432 public static final int GESTURE_2_FINGER_SWIPE_RIGHT = 28; 433 434 /** 435 * The user has performed a three-finger swipe up gesture on the touch screen. 436 */ 437 public static final int GESTURE_3_FINGER_SWIPE_UP = 29; 438 439 /** 440 * The user has performed a three-finger swipe down gesture on the touch screen. 441 */ 442 public static final int GESTURE_3_FINGER_SWIPE_DOWN = 30; 443 444 /** 445 * The user has performed a three-finger swipe left gesture on the touch screen. 446 */ 447 public static final int GESTURE_3_FINGER_SWIPE_LEFT = 31; 448 449 /** 450 * The user has performed a three-finger swipe right gesture on the touch screen. 451 */ 452 public static final int GESTURE_3_FINGER_SWIPE_RIGHT = 32; 453 454 /** The user has performed a four-finger swipe up gesture on the touch screen. */ 455 public static final int GESTURE_4_FINGER_SWIPE_UP = 33; 456 457 /** The user has performed a four-finger swipe down gesture on the touch screen. */ 458 public static final int GESTURE_4_FINGER_SWIPE_DOWN = 34; 459 460 /** The user has performed a four-finger swipe left gesture on the touch screen. */ 461 public static final int GESTURE_4_FINGER_SWIPE_LEFT = 35; 462 463 /** The user has performed a four-finger swipe right gesture on the touch screen. */ 464 public static final int GESTURE_4_FINGER_SWIPE_RIGHT = 36; 465 466 /** The user has performed a four-finger single tap gesture on the touch screen. */ 467 public static final int GESTURE_4_FINGER_SINGLE_TAP = 37; 468 469 /** The user has performed a four-finger double tap gesture on the touch screen. */ 470 public static final int GESTURE_4_FINGER_DOUBLE_TAP = 38; 471 472 /** The user has performed a four-finger triple tap gesture on the touch screen. */ 473 public static final int GESTURE_4_FINGER_TRIPLE_TAP = 39; 474 475 /** The user has performed a two-finger double tap and hold gesture on the touch screen. */ 476 public static final int GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD = 40; 477 478 /** The user has performed a three-finger double tap and hold gesture on the touch screen. */ 479 public static final int GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD = 41; 480 481 /** The user has performed a two-finger triple-tap and hold gesture on the touch screen. */ 482 public static final int GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD = 43; 483 484 /** The user has performed a three-finger single-tap and hold gesture on the touch screen. */ 485 public static final int GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD = 44; 486 487 /** The user has performed a three-finger triple-tap and hold gesture on the touch screen. */ 488 public static final int GESTURE_3_FINGER_TRIPLE_TAP_AND_HOLD = 45; 489 490 /** The user has performed a two-finger double tap and hold gesture on the touch screen. */ 491 public static final int GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD = 42; 492 493 /** 494 * The {@link Intent} that must be declared as handled by the service. 495 */ 496 public static final String SERVICE_INTERFACE = 497 "android.accessibilityservice.AccessibilityService"; 498 499 /** 500 * Name under which an AccessibilityService component publishes information 501 * about itself. This meta-data must reference an XML resource containing an 502 * <code><{@link android.R.styleable#AccessibilityService accessibility-service}></code> 503 * tag. This is a sample XML file configuring an accessibility service: 504 * <pre> <accessibility-service 505 * android:accessibilityEventTypes="typeViewClicked|typeViewFocused" 506 * android:packageNames="foo.bar, foo.baz" 507 * android:accessibilityFeedbackType="feedbackSpoken" 508 * android:notificationTimeout="100" 509 * android:accessibilityFlags="flagDefault" 510 * android:settingsActivity="foo.bar.TestBackActivity" 511 * android:canRetrieveWindowContent="true" 512 * android:canRequestTouchExplorationMode="true" 513 * . . . 514 * /></pre> 515 */ 516 public static final String SERVICE_META_DATA = "android.accessibilityservice"; 517 518 /** 519 * Action to go back. 520 */ 521 public static final int GLOBAL_ACTION_BACK = 1; 522 523 /** 524 * Action to go home. 525 */ 526 public static final int GLOBAL_ACTION_HOME = 2; 527 528 /** 529 * Action to toggle showing the overview of recent apps. Will fail on platforms that don't 530 * show recent apps. 531 */ 532 public static final int GLOBAL_ACTION_RECENTS = 3; 533 534 /** 535 * Action to open the notifications. 536 */ 537 public static final int GLOBAL_ACTION_NOTIFICATIONS = 4; 538 539 /** 540 * Action to open the quick settings. 541 */ 542 public static final int GLOBAL_ACTION_QUICK_SETTINGS = 5; 543 544 /** 545 * Action to open the power long-press dialog. 546 */ 547 public static final int GLOBAL_ACTION_POWER_DIALOG = 6; 548 549 /** 550 * Action to toggle docking the current app's window. 551 * <p> 552 * <strong>Note:</strong> It is effective only if it appears in {@link #getSystemActions()}. 553 */ 554 public static final int GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7; 555 556 /** 557 * Action to lock the screen 558 */ 559 public static final int GLOBAL_ACTION_LOCK_SCREEN = 8; 560 561 /** 562 * Action to take a screenshot 563 */ 564 public static final int GLOBAL_ACTION_TAKE_SCREENSHOT = 9; 565 566 /** 567 * Action to send the KEYCODE_HEADSETHOOK KeyEvent, which is used to answer/hang up calls and 568 * play/stop media 569 */ 570 public static final int GLOBAL_ACTION_KEYCODE_HEADSETHOOK = 10; 571 572 /** 573 * Action to trigger the Accessibility Button 574 */ 575 public static final int GLOBAL_ACTION_ACCESSIBILITY_BUTTON = 11; 576 577 /** 578 * Action to bring up the Accessibility Button's chooser menu 579 */ 580 public static final int GLOBAL_ACTION_ACCESSIBILITY_BUTTON_CHOOSER = 12; 581 582 /** 583 * Action to trigger the Accessibility Shortcut. This shortcut has a hardware trigger and can 584 * be activated by holding down the two volume keys. 585 */ 586 public static final int GLOBAL_ACTION_ACCESSIBILITY_SHORTCUT = 13; 587 588 /** 589 * Action to show Launcher's all apps. 590 */ 591 public static final int GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS = 14; 592 593 /** 594 * Action to dismiss the notification shade 595 */ 596 public static final int GLOBAL_ACTION_DISMISS_NOTIFICATION_SHADE = 15; 597 598 /** 599 * Action to trigger dpad up keyevent. 600 */ 601 public static final int GLOBAL_ACTION_DPAD_UP = 16; 602 603 /** 604 * Action to trigger dpad down keyevent. 605 */ 606 public static final int GLOBAL_ACTION_DPAD_DOWN = 17; 607 608 /** 609 * Action to trigger dpad left keyevent. 610 */ 611 public static final int GLOBAL_ACTION_DPAD_LEFT = 18; 612 613 /** 614 * Action to trigger dpad right keyevent. 615 */ 616 public static final int GLOBAL_ACTION_DPAD_RIGHT = 19; 617 618 /** 619 * Action to trigger dpad center keyevent. 620 */ 621 public static final int GLOBAL_ACTION_DPAD_CENTER = 20; 622 623 private static final String LOG_TAG = "AccessibilityService"; 624 625 /** 626 * Interface used by IAccessibilityServiceClientWrapper to call the service from its main 627 * thread. 628 * @hide 629 */ 630 public interface Callbacks { onAccessibilityEvent(AccessibilityEvent event)631 void onAccessibilityEvent(AccessibilityEvent event); onInterrupt()632 void onInterrupt(); onServiceConnected()633 void onServiceConnected(); init(int connectionId, IBinder windowToken)634 void init(int connectionId, IBinder windowToken); 635 /** The detected gesture information for different displays */ onGesture(AccessibilityGestureEvent gestureInfo)636 boolean onGesture(AccessibilityGestureEvent gestureInfo); onKeyEvent(KeyEvent event)637 boolean onKeyEvent(KeyEvent event); 638 /** Magnification changed callbacks for different displays */ onMagnificationChanged(int displayId, @NonNull Region region, MagnificationConfig config)639 void onMagnificationChanged(int displayId, @NonNull Region region, 640 MagnificationConfig config); 641 /** Callbacks for receiving motion events. */ onMotionEvent(MotionEvent event)642 void onMotionEvent(MotionEvent event); 643 /** Callback for tuch state changes. */ onTouchStateChanged(int displayId, int state)644 void onTouchStateChanged(int displayId, int state); onSoftKeyboardShowModeChanged(int showMode)645 void onSoftKeyboardShowModeChanged(int showMode); onPerformGestureResult(int sequence, boolean completedSuccessfully)646 void onPerformGestureResult(int sequence, boolean completedSuccessfully); onFingerprintCapturingGesturesChanged(boolean active)647 void onFingerprintCapturingGesturesChanged(boolean active); onFingerprintGesture(int gesture)648 void onFingerprintGesture(int gesture); 649 /** Accessbility button clicked callbacks for different displays */ onAccessibilityButtonClicked(int displayId)650 void onAccessibilityButtonClicked(int displayId); onAccessibilityButtonAvailabilityChanged(boolean available)651 void onAccessibilityButtonAvailabilityChanged(boolean available); 652 /** This is called when the system action list is changed. */ onSystemActionsChanged()653 void onSystemActionsChanged(); 654 /** This is called when an app requests ime sessions or when the service is enabled. */ createImeSession(IAccessibilityInputMethodSessionCallback callback)655 void createImeSession(IAccessibilityInputMethodSessionCallback callback); 656 /** This is called when an app starts input or when the service is enabled. */ startInput(@ullable RemoteAccessibilityInputConnection inputConnection, @NonNull EditorInfo editorInfo, boolean restarting)657 void startInput(@Nullable RemoteAccessibilityInputConnection inputConnection, 658 @NonNull EditorInfo editorInfo, boolean restarting); 659 } 660 661 /** 662 * Annotations for Soft Keyboard show modes so tools can catch invalid show modes. 663 * @hide 664 */ 665 @Retention(RetentionPolicy.SOURCE) 666 @IntDef(prefix = { "SHOW_MODE_" }, value = { 667 SHOW_MODE_AUTO, 668 SHOW_MODE_HIDDEN, 669 SHOW_MODE_IGNORE_HARD_KEYBOARD 670 }) 671 public @interface SoftKeyboardShowMode {} 672 673 /** 674 * Allow the system to control when the soft keyboard is shown. 675 * @see SoftKeyboardController 676 */ 677 public static final int SHOW_MODE_AUTO = 0; 678 679 /** 680 * Never show the soft keyboard. 681 * @see SoftKeyboardController 682 */ 683 public static final int SHOW_MODE_HIDDEN = 1; 684 685 /** 686 * Allow the soft keyboard to be shown, even if a hard keyboard is connected 687 * @see SoftKeyboardController 688 */ 689 public static final int SHOW_MODE_IGNORE_HARD_KEYBOARD = 2; 690 691 /** 692 * Mask used to cover the show modes supported in public API 693 * @hide 694 */ 695 public static final int SHOW_MODE_MASK = 0x03; 696 697 /** 698 * Bit used to hold the old value of the hard IME setting to restore when a service is shut 699 * down. 700 * @hide 701 */ 702 public static final int SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE = 0x20000000; 703 704 /** 705 * Bit for show mode setting to indicate that the user has overridden the hard keyboard 706 * behavior. 707 * @hide 708 */ 709 public static final int SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN = 0x40000000; 710 711 /** 712 * Annotations for error codes of taking screenshot. 713 * @hide 714 */ 715 @Retention(RetentionPolicy.SOURCE) 716 @IntDef(prefix = { "TAKE_SCREENSHOT_" }, value = { 717 ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR, 718 ERROR_TAKE_SCREENSHOT_NO_ACCESSIBILITY_ACCESS, 719 ERROR_TAKE_SCREENSHOT_INTERVAL_TIME_SHORT, 720 ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY, 721 ERROR_TAKE_SCREENSHOT_INVALID_WINDOW 722 }) 723 public @interface ScreenshotErrorCode {} 724 725 /** 726 * The status of taking screenshot is success. 727 * @hide 728 */ 729 public static final int TAKE_SCREENSHOT_SUCCESS = 0; 730 731 /** 732 * The status of taking screenshot is failure and the reason is internal error. 733 */ 734 public static final int ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR = 1; 735 736 /** 737 * The status of taking screenshot is failure and the reason is no accessibility access. 738 */ 739 public static final int ERROR_TAKE_SCREENSHOT_NO_ACCESSIBILITY_ACCESS = 2; 740 741 /** 742 * The status of taking screenshot is failure and the reason is that too little time has 743 * elapsed since the last screenshot. 744 */ 745 public static final int ERROR_TAKE_SCREENSHOT_INTERVAL_TIME_SHORT = 3; 746 747 /** 748 * The status of taking screenshot is failure and the reason is invalid display Id. 749 */ 750 public static final int ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY = 4; 751 752 /** 753 * The status of taking screenshot is failure and the reason is invalid accessibility window Id. 754 */ 755 public static final int ERROR_TAKE_SCREENSHOT_INVALID_WINDOW = 5; 756 757 /** 758 * The status of taking screenshot is failure and the reason is the window contains secure 759 * content. 760 * @see WindowManager.LayoutParams#FLAG_SECURE 761 */ 762 public static final int ERROR_TAKE_SCREENSHOT_SECURE_WINDOW = 6; 763 764 /** 765 * The interval time of calling 766 * {@link AccessibilityService#takeScreenshot(int, Executor, Consumer)} API. 767 * @hide 768 */ 769 @TestApi 770 public static final int ACCESSIBILITY_TAKE_SCREENSHOT_REQUEST_INTERVAL_TIMES_MS = 333; 771 772 /** @hide */ 773 public static final String KEY_ACCESSIBILITY_SCREENSHOT_STATUS = 774 "screenshot_status"; 775 776 /** @hide */ 777 public static final String KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER = 778 "screenshot_hardwareBuffer"; 779 780 /** @hide */ 781 public static final String KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE = 782 "screenshot_colorSpace"; 783 784 /** @hide */ 785 public static final String KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP = 786 "screenshot_timestamp"; 787 788 private int mConnectionId = AccessibilityInteractionClient.NO_ID; 789 790 @UnsupportedAppUsage 791 private AccessibilityServiceInfo mInfo; 792 793 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 794 private IBinder mWindowToken; 795 796 private WindowManager mWindowManager; 797 798 /** List of magnification controllers, mapping from displayId -> MagnificationController. */ 799 private final SparseArray<MagnificationController> mMagnificationControllers = 800 new SparseArray<>(0); 801 /** 802 * List of touch interaction controllers, mapping from displayId -> TouchInteractionController. 803 */ 804 private final SparseArray<TouchInteractionController> mTouchInteractionControllers = 805 new SparseArray<>(0); 806 807 private SoftKeyboardController mSoftKeyboardController; 808 private InputMethod mInputMethod; 809 private boolean mInputMethodInitialized = false; 810 private final SparseArray<AccessibilityButtonController> mAccessibilityButtonControllers = 811 new SparseArray<>(0); 812 813 private int mGestureStatusCallbackSequence; 814 815 private SparseArray<GestureResultCallbackInfo> mGestureStatusCallbackInfos; 816 817 private final Object mLock = new Object(); 818 819 private FingerprintGestureController mFingerprintGestureController; 820 821 private int mMotionEventSources; 822 823 /** 824 * Callback for {@link android.view.accessibility.AccessibilityEvent}s. 825 * 826 * @param event The new event. This event is owned by the caller and cannot be used after 827 * this method returns. Services wishing to use the event after this method returns should 828 * make a copy. 829 */ onAccessibilityEvent(AccessibilityEvent event)830 public abstract void onAccessibilityEvent(AccessibilityEvent event); 831 832 /** 833 * Callback for interrupting the accessibility feedback. 834 */ onInterrupt()835 public abstract void onInterrupt(); 836 837 /** 838 * Dispatches service connection to internal components first, then the 839 * client code. 840 */ dispatchServiceConnected()841 private void dispatchServiceConnected() { 842 synchronized (mLock) { 843 for (int i = 0; i < mMagnificationControllers.size(); i++) { 844 mMagnificationControllers.valueAt(i).onServiceConnectedLocked(); 845 } 846 final AccessibilityServiceInfo info = getServiceInfo(); 847 if (info != null) { 848 updateInputMethod(info); 849 mMotionEventSources = info.getMotionEventSources(); 850 } 851 } 852 if (mSoftKeyboardController != null) { 853 mSoftKeyboardController.onServiceConnected(); 854 } 855 856 // The client gets to handle service connection last, after we've set 857 // up any state upon which their code may rely. 858 onServiceConnected(); 859 } 860 updateInputMethod(AccessibilityServiceInfo info)861 private void updateInputMethod(AccessibilityServiceInfo info) { 862 if (info != null) { 863 boolean requestIme = (info.flags 864 & AccessibilityServiceInfo.FLAG_INPUT_METHOD_EDITOR) != 0; 865 if (requestIme && !mInputMethodInitialized) { 866 mInputMethod = onCreateInputMethod(); 867 mInputMethodInitialized = true; 868 } else if (!requestIme & mInputMethodInitialized) { 869 mInputMethod = null; 870 mInputMethodInitialized = false; 871 } 872 } 873 } 874 875 /** 876 * This method is a part of the {@link AccessibilityService} lifecycle and is 877 * called after the system has successfully bound to the service. If is 878 * convenient to use this method for setting the {@link AccessibilityServiceInfo}. 879 * 880 * @see AccessibilityServiceInfo 881 * @see #setServiceInfo(AccessibilityServiceInfo) 882 */ onServiceConnected()883 protected void onServiceConnected() { 884 885 } 886 887 /** 888 * Called by {@link #onGesture(AccessibilityGestureEvent)} when the user performs a specific 889 * gesture on the default display. 890 * 891 * <strong>Note:</strong> To receive gestures an accessibility service must 892 * request that the device is in touch exploration mode by setting the 893 * {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} 894 * flag. 895 * 896 * @param gestureId The unique id of the performed gesture. 897 * 898 * @return Whether the gesture was handled. 899 * @deprecated Override {@link #onGesture(AccessibilityGestureEvent)} instead. 900 * 901 * @see #GESTURE_SWIPE_UP 902 * @see #GESTURE_SWIPE_UP_AND_LEFT 903 * @see #GESTURE_SWIPE_UP_AND_DOWN 904 * @see #GESTURE_SWIPE_UP_AND_RIGHT 905 * @see #GESTURE_SWIPE_DOWN 906 * @see #GESTURE_SWIPE_DOWN_AND_LEFT 907 * @see #GESTURE_SWIPE_DOWN_AND_UP 908 * @see #GESTURE_SWIPE_DOWN_AND_RIGHT 909 * @see #GESTURE_SWIPE_LEFT 910 * @see #GESTURE_SWIPE_LEFT_AND_UP 911 * @see #GESTURE_SWIPE_LEFT_AND_RIGHT 912 * @see #GESTURE_SWIPE_LEFT_AND_DOWN 913 * @see #GESTURE_SWIPE_RIGHT 914 * @see #GESTURE_SWIPE_RIGHT_AND_UP 915 * @see #GESTURE_SWIPE_RIGHT_AND_LEFT 916 * @see #GESTURE_SWIPE_RIGHT_AND_DOWN 917 */ 918 @Deprecated onGesture(int gestureId)919 protected boolean onGesture(int gestureId) { 920 return false; 921 } 922 923 /** 924 * Called by the system when the user performs a specific gesture on the 925 * specific touch screen. 926 *<p> 927 * <strong>Note:</strong> To receive gestures an accessibility service must 928 * request that the device is in touch exploration mode by setting the 929 * {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} 930 * flag. 931 *<p> 932 * <strong>Note:</strong> The default implementation calls {@link #onGesture(int)} when the 933 * touch screen is default display. 934 * 935 * @param gestureEvent The information of gesture. 936 * 937 * @return Whether the gesture was handled. 938 * 939 */ onGesture(@onNull AccessibilityGestureEvent gestureEvent)940 public boolean onGesture(@NonNull AccessibilityGestureEvent gestureEvent) { 941 if (gestureEvent.getDisplayId() == Display.DEFAULT_DISPLAY) { 942 onGesture(gestureEvent.getGestureId()); 943 } 944 return false; 945 } 946 947 /** 948 * Callback that allows an accessibility service to observe the key events 949 * before they are passed to the rest of the system. This means that the events 950 * are first delivered here before they are passed to the device policy, the 951 * input method, or applications. 952 * <p> 953 * <strong>Note:</strong> It is important that key events are handled in such 954 * a way that the event stream that would be passed to the rest of the system 955 * is well-formed. For example, handling the down event but not the up event 956 * and vice versa would generate an inconsistent event stream. 957 * </p> 958 * <p> 959 * <strong>Note:</strong> The key events delivered in this method are copies 960 * and modifying them will have no effect on the events that will be passed 961 * to the system. This method is intended to perform purely filtering 962 * functionality. 963 * <p> 964 * 965 * @param event The event to be processed. This event is owned by the caller and cannot be used 966 * after this method returns. Services wishing to use the event after this method returns should 967 * make a copy. 968 * @return If true then the event will be consumed and not delivered to 969 * applications, otherwise it will be delivered as usual. 970 */ onKeyEvent(KeyEvent event)971 protected boolean onKeyEvent(KeyEvent event) { 972 return false; 973 } 974 975 /** 976 * Callback that allows an accessibility service to observe generic {@link MotionEvent}s. 977 * <p> 978 * Prefer {@link TouchInteractionController} to observe and control touchscreen events, 979 * including touch gestures. If this or any enabled service is using 980 * {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} then 981 * {@link #onMotionEvent} will not receive touchscreen events. 982 * </p> 983 * <p> 984 * <strong>Note:</strong> The service must first request to listen to events using 985 * {@link AccessibilityServiceInfo#setMotionEventSources}. 986 * {@link MotionEvent}s from sources in {@link AccessibilityServiceInfo#getMotionEventSources()} 987 * are not sent to the rest of the system. To stop listening to events from a given source, call 988 * {@link AccessibilityServiceInfo#setMotionEventSources} with that source removed. 989 * </p> 990 * @param event The event to be processed. 991 */ onMotionEvent(@onNull MotionEvent event)992 public void onMotionEvent(@NonNull MotionEvent event) { } 993 994 /** 995 * Gets the windows on the screen of the default display. This method returns only the windows 996 * that a sighted user can interact with, as opposed to all windows. 997 * For example, if there is a modal dialog shown and the user cannot touch 998 * anything behind it, then only the modal window will be reported 999 * (assuming it is the top one). For convenience the returned windows 1000 * are ordered in a descending layer order, which is the windows that 1001 * are on top are reported first. Since the user can always 1002 * interact with the window that has input focus by typing, the focused 1003 * window is always returned (even if covered by a modal window). 1004 * <p> 1005 * <strong>Note:</strong> In order to access the windows your service has 1006 * to declare the capability to retrieve window content by setting the 1007 * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent} 1008 * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}. 1009 * Also the service has to opt-in to retrieve the interactive windows by 1010 * setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} 1011 * flag. 1012 * </p> 1013 * 1014 * @return The windows if there are windows and the service is can retrieve 1015 * them, otherwise an empty list. 1016 */ getWindows()1017 public List<AccessibilityWindowInfo> getWindows() { 1018 return AccessibilityInteractionClient.getInstance(this).getWindows(mConnectionId); 1019 } 1020 1021 /** 1022 * Gets the windows on the screen of all displays. This method returns only the windows 1023 * that a sighted user can interact with, as opposed to all windows. 1024 * For example, if there is a modal dialog shown and the user cannot touch 1025 * anything behind it, then only the modal window will be reported 1026 * (assuming it is the top one). For convenience the returned windows 1027 * are ordered in a descending layer order, which is the windows that 1028 * are on top are reported first. Since the user can always 1029 * interact with the window that has input focus by typing, the focused 1030 * window is always returned (even if covered by a modal window). 1031 * <p> 1032 * <strong>Note:</strong> In order to access the windows your service has 1033 * to declare the capability to retrieve window content by setting the 1034 * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent} 1035 * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}. 1036 * Also the service has to opt-in to retrieve the interactive windows by 1037 * setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} 1038 * flag. 1039 * </p> 1040 * 1041 * @return The windows of all displays if there are windows and the service is can retrieve 1042 * them, otherwise an empty list. The key of SparseArray is display ID. 1043 */ 1044 @NonNull getWindowsOnAllDisplays()1045 public final SparseArray<List<AccessibilityWindowInfo>> getWindowsOnAllDisplays() { 1046 return AccessibilityInteractionClient.getInstance(this).getWindowsOnAllDisplays( 1047 mConnectionId); 1048 } 1049 1050 /** 1051 * Gets the root node in the currently active window if this service 1052 * can retrieve window content. The active window is the one that the user 1053 * is currently touching or the window with input focus, if the user is not 1054 * touching any window. It could be from any logical display. 1055 * <p> 1056 * <strong>Note:</strong> In order to access the root node your service has 1057 * to declare the capability to retrieve window content by setting the 1058 * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent} 1059 * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}. 1060 * </p> 1061 * 1062 * @return The root node if this service can retrieve window content. 1063 * @see AccessibilityWindowInfo#isActive() for more explanation about the active window. 1064 */ getRootInActiveWindow()1065 public AccessibilityNodeInfo getRootInActiveWindow() { 1066 return getRootInActiveWindow(AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS_HYBRID); 1067 } 1068 1069 /** 1070 * Gets the root node in the currently active window if this service 1071 * can retrieve window content. The active window is the one that the user 1072 * is currently touching or the window with input focus, if the user is not 1073 * touching any window. It could be from any logical display. 1074 * 1075 * @param prefetchingStrategy the prefetching strategy. 1076 * @return The root node if this service can retrieve window content. 1077 * 1078 * @see #getRootInActiveWindow() 1079 * @see AccessibilityNodeInfo#getParent(int) for a description of prefetching. 1080 */ 1081 @Nullable getRootInActiveWindow( @ccessibilityNodeInfo.PrefetchingStrategy int prefetchingStrategy)1082 public AccessibilityNodeInfo getRootInActiveWindow( 1083 @AccessibilityNodeInfo.PrefetchingStrategy int prefetchingStrategy) { 1084 return AccessibilityInteractionClient.getInstance(this).getRootInActiveWindow( 1085 mConnectionId, prefetchingStrategy); 1086 } 1087 1088 /** 1089 * Disables the service. After calling this method, the service will be disabled and settings 1090 * will show that it is turned off. 1091 */ disableSelf()1092 public final void disableSelf() { 1093 final IAccessibilityServiceConnection connection = 1094 AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); 1095 if (connection != null) { 1096 try { 1097 connection.disableSelf(); 1098 } catch (RemoteException re) { 1099 throw new RuntimeException(re); 1100 } 1101 } 1102 } 1103 1104 @NonNull 1105 @Override createDisplayContext(Display display)1106 public Context createDisplayContext(Display display) { 1107 return new AccessibilityContext(super.createDisplayContext(display), mConnectionId); 1108 } 1109 1110 @NonNull 1111 @Override createWindowContext(int type, @Nullable Bundle options)1112 public Context createWindowContext(int type, @Nullable Bundle options) { 1113 final Context context = super.createWindowContext(type, options); 1114 if (type != TYPE_ACCESSIBILITY_OVERLAY) { 1115 return context; 1116 } 1117 return new AccessibilityContext(context, mConnectionId); 1118 } 1119 1120 @NonNull 1121 @Override createWindowContext(@onNull Display display, int type, @Nullable Bundle options)1122 public Context createWindowContext(@NonNull Display display, int type, 1123 @Nullable Bundle options) { 1124 final Context context = super.createWindowContext(display, type, options); 1125 if (type != TYPE_ACCESSIBILITY_OVERLAY) { 1126 return context; 1127 } 1128 return new AccessibilityContext(context, mConnectionId); 1129 } 1130 1131 /** 1132 * Returns the magnification controller, which may be used to query and 1133 * modify the state of display magnification. 1134 * <p> 1135 * <strong>Note:</strong> In order to control magnification, your service 1136 * must declare the capability by setting the 1137 * {@link android.R.styleable#AccessibilityService_canControlMagnification} 1138 * property in its meta-data. For more information, see 1139 * {@link #SERVICE_META_DATA}. 1140 * 1141 * @return the magnification controller 1142 */ 1143 @NonNull getMagnificationController()1144 public final MagnificationController getMagnificationController() { 1145 return getMagnificationController(Display.DEFAULT_DISPLAY); 1146 } 1147 1148 /** 1149 * Returns the magnification controller of specified logical display, which may be used to 1150 * query and modify the state of display magnification. 1151 * <p> 1152 * <strong>Note:</strong> In order to control magnification, your service 1153 * must declare the capability by setting the 1154 * {@link android.R.styleable#AccessibilityService_canControlMagnification} 1155 * property in its meta-data. For more information, see 1156 * {@link #SERVICE_META_DATA}. 1157 * 1158 * @param displayId The logic display id, use {@link Display#DEFAULT_DISPLAY} for 1159 * default display. 1160 * @return the magnification controller 1161 * 1162 * @hide 1163 */ 1164 @NonNull getMagnificationController(int displayId)1165 public final MagnificationController getMagnificationController(int displayId) { 1166 synchronized (mLock) { 1167 MagnificationController controller = mMagnificationControllers.get(displayId); 1168 if (controller == null) { 1169 controller = new MagnificationController(this, mLock, displayId); 1170 mMagnificationControllers.put(displayId, controller); 1171 } 1172 return controller; 1173 } 1174 } 1175 1176 /** 1177 * Get the controller for fingerprint gestures. This feature requires {@link 1178 * AccessibilityServiceInfo#CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES}. 1179 * 1180 *<strong>Note: </strong> The service must be connected before this method is called. 1181 * 1182 * @return The controller for fingerprint gestures, or {@code null} if gestures are unavailable. 1183 */ 1184 @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT) getFingerprintGestureController()1185 public final @NonNull FingerprintGestureController getFingerprintGestureController() { 1186 if (mFingerprintGestureController == null) { 1187 mFingerprintGestureController = new FingerprintGestureController( 1188 AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId)); 1189 } 1190 return mFingerprintGestureController; 1191 } 1192 1193 /** 1194 * Dispatch a gesture to the touch screen. Any gestures currently in progress, whether from 1195 * the user, this service, or another service, will be cancelled. 1196 * <p> 1197 * The gesture will be dispatched as if it were performed directly on the screen by a user, so 1198 * the events may be affected by features such as magnification and explore by touch. 1199 * </p> 1200 * <p> 1201 * <strong>Note:</strong> In order to dispatch gestures, your service 1202 * must declare the capability by setting the 1203 * {@link android.R.styleable#AccessibilityService_canPerformGestures} 1204 * property in its meta-data. For more information, see 1205 * {@link #SERVICE_META_DATA}. 1206 * </p> 1207 * 1208 * @param gesture The gesture to dispatch 1209 * @param callback The object to call back when the status of the gesture is known. If 1210 * {@code null}, no status is reported. 1211 * @param handler The handler on which to call back the {@code callback} object. If 1212 * {@code null}, the object is called back on the service's main thread. 1213 * 1214 * @return {@code true} if the gesture is dispatched, {@code false} if not. 1215 */ dispatchGesture(@onNull GestureDescription gesture, @Nullable GestureResultCallback callback, @Nullable Handler handler)1216 public final boolean dispatchGesture(@NonNull GestureDescription gesture, 1217 @Nullable GestureResultCallback callback, 1218 @Nullable Handler handler) { 1219 final IAccessibilityServiceConnection connection = 1220 AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); 1221 if (connection == null) { 1222 return false; 1223 } 1224 int sampleTimeMs = calculateGestureSampleTimeMs(gesture.getDisplayId()); 1225 List<GestureDescription.GestureStep> steps = 1226 MotionEventGenerator.getGestureStepsFromGestureDescription(gesture, sampleTimeMs); 1227 try { 1228 synchronized (mLock) { 1229 mGestureStatusCallbackSequence++; 1230 if (callback != null) { 1231 if (mGestureStatusCallbackInfos == null) { 1232 mGestureStatusCallbackInfos = new SparseArray<>(); 1233 } 1234 GestureResultCallbackInfo callbackInfo = new GestureResultCallbackInfo(gesture, 1235 callback, handler); 1236 mGestureStatusCallbackInfos.put(mGestureStatusCallbackSequence, callbackInfo); 1237 } 1238 connection.dispatchGesture(mGestureStatusCallbackSequence, 1239 new ParceledListSlice<>(steps), gesture.getDisplayId()); 1240 } 1241 } catch (RemoteException re) { 1242 throw new RuntimeException(re); 1243 } 1244 return true; 1245 } 1246 1247 /** 1248 * Returns the sample time in millis of gesture steps for the current display. 1249 * 1250 * <p>For gestures to be smooth they should line up with the refresh rate of the display. 1251 * On versions of Android before R, the sample time was fixed to 100ms. 1252 */ calculateGestureSampleTimeMs(int displayId)1253 private int calculateGestureSampleTimeMs(int displayId) { 1254 if (getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.Q) { 1255 return 100; 1256 } 1257 Display display = getSystemService(DisplayManager.class).getDisplay( 1258 displayId); 1259 if (display == null) { 1260 return 100; 1261 } 1262 int msPerSecond = 1000; 1263 int sampleTimeMs = (int) (msPerSecond / display.getRefreshRate()); 1264 if (sampleTimeMs < 1) { 1265 // Should be impossible, but do not return 0. 1266 return 100; 1267 } 1268 return sampleTimeMs; 1269 } 1270 onPerformGestureResult(int sequence, final boolean completedSuccessfully)1271 void onPerformGestureResult(int sequence, final boolean completedSuccessfully) { 1272 if (mGestureStatusCallbackInfos == null) { 1273 return; 1274 } 1275 GestureResultCallbackInfo callbackInfo; 1276 synchronized (mLock) { 1277 callbackInfo = mGestureStatusCallbackInfos.get(sequence); 1278 mGestureStatusCallbackInfos.remove(sequence); 1279 } 1280 final GestureResultCallbackInfo finalCallbackInfo = callbackInfo; 1281 if ((callbackInfo != null) && (callbackInfo.gestureDescription != null) 1282 && (callbackInfo.callback != null)) { 1283 if (callbackInfo.handler != null) { 1284 callbackInfo.handler.post(new Runnable() { 1285 @Override 1286 public void run() { 1287 if (completedSuccessfully) { 1288 finalCallbackInfo.callback 1289 .onCompleted(finalCallbackInfo.gestureDescription); 1290 } else { 1291 finalCallbackInfo.callback 1292 .onCancelled(finalCallbackInfo.gestureDescription); 1293 } 1294 } 1295 }); 1296 return; 1297 } 1298 if (completedSuccessfully) { 1299 callbackInfo.callback.onCompleted(callbackInfo.gestureDescription); 1300 } else { 1301 callbackInfo.callback.onCancelled(callbackInfo.gestureDescription); 1302 } 1303 } 1304 } 1305 onMagnificationChanged(int displayId, @NonNull Region region, MagnificationConfig config)1306 private void onMagnificationChanged(int displayId, @NonNull Region region, 1307 MagnificationConfig config) { 1308 MagnificationController controller; 1309 synchronized (mLock) { 1310 controller = mMagnificationControllers.get(displayId); 1311 } 1312 if (controller != null) { 1313 controller.dispatchMagnificationChanged(region, config); 1314 } 1315 } 1316 1317 /** 1318 * Callback for fingerprint gesture handling 1319 * @param active If gesture detection is active 1320 */ onFingerprintCapturingGesturesChanged(boolean active)1321 private void onFingerprintCapturingGesturesChanged(boolean active) { 1322 getFingerprintGestureController().onGestureDetectionActiveChanged(active); 1323 } 1324 1325 /** 1326 * Callback for fingerprint gesture handling 1327 * @param gesture The identifier for the gesture performed 1328 */ onFingerprintGesture(int gesture)1329 private void onFingerprintGesture(int gesture) { 1330 getFingerprintGestureController().onGesture(gesture); 1331 } 1332 getConnectionId()1333 int getConnectionId() { 1334 return mConnectionId; 1335 } 1336 1337 /** 1338 * Used to control and query the state of display magnification. 1339 */ 1340 public static final class MagnificationController { 1341 private final AccessibilityService mService; 1342 private final int mDisplayId; 1343 1344 /** 1345 * Map of listeners to their handlers. Lazily created when adding the 1346 * first magnification listener. 1347 */ 1348 private ArrayMap<OnMagnificationChangedListener, Handler> mListeners; 1349 private final Object mLock; 1350 MagnificationController(@onNull AccessibilityService service, @NonNull Object lock, int displayId)1351 MagnificationController(@NonNull AccessibilityService service, @NonNull Object lock, 1352 int displayId) { 1353 mService = service; 1354 mLock = lock; 1355 mDisplayId = displayId; 1356 } 1357 1358 /** 1359 * Called when the service is connected. 1360 */ onServiceConnectedLocked()1361 void onServiceConnectedLocked() { 1362 if (mListeners != null && !mListeners.isEmpty()) { 1363 setMagnificationCallbackEnabled(true); 1364 } 1365 } 1366 1367 /** 1368 * Adds the specified change listener to the list of magnification 1369 * change listeners. The callback will occur on the service's main 1370 * thread. 1371 * 1372 * @param listener the listener to add, must be non-{@code null} 1373 */ addListener(@onNull OnMagnificationChangedListener listener)1374 public void addListener(@NonNull OnMagnificationChangedListener listener) { 1375 addListener(listener, null); 1376 } 1377 1378 /** 1379 * Adds the specified change listener to the list of magnification 1380 * change listeners. The callback will occur on the specified 1381 * {@link Handler}'s thread, or on the service's main thread if the 1382 * handler is {@code null}. 1383 * 1384 * @param listener the listener to add, must be non-null 1385 * @param handler the handler on which the callback should execute, or 1386 * {@code null} to execute on the service's main thread 1387 */ addListener(@onNull OnMagnificationChangedListener listener, @Nullable Handler handler)1388 public void addListener(@NonNull OnMagnificationChangedListener listener, 1389 @Nullable Handler handler) { 1390 synchronized (mLock) { 1391 if (mListeners == null) { 1392 mListeners = new ArrayMap<>(); 1393 } 1394 1395 final boolean shouldEnableCallback = mListeners.isEmpty(); 1396 mListeners.put(listener, handler); 1397 1398 if (shouldEnableCallback) { 1399 // This may fail if the service is not connected yet, but if we 1400 // still have listeners when it connects then we can try again. 1401 setMagnificationCallbackEnabled(true); 1402 } 1403 } 1404 } 1405 1406 /** 1407 * Removes the specified change listener from the list of magnification change listeners. 1408 * 1409 * @param listener the listener to remove, must be non-null 1410 * @return {@code true} if the listener was removed, {@code false} otherwise 1411 */ removeListener(@onNull OnMagnificationChangedListener listener)1412 public boolean removeListener(@NonNull OnMagnificationChangedListener listener) { 1413 if (mListeners == null) { 1414 return false; 1415 } 1416 1417 synchronized (mLock) { 1418 final int keyIndex = mListeners.indexOfKey(listener); 1419 final boolean hasKey = keyIndex >= 0; 1420 if (hasKey) { 1421 mListeners.removeAt(keyIndex); 1422 } 1423 1424 if (hasKey && mListeners.isEmpty()) { 1425 // We just removed the last listener, so we don't need 1426 // callbacks from the service anymore. 1427 setMagnificationCallbackEnabled(false); 1428 } 1429 1430 return hasKey; 1431 } 1432 } 1433 setMagnificationCallbackEnabled(boolean enabled)1434 private void setMagnificationCallbackEnabled(boolean enabled) { 1435 final IAccessibilityServiceConnection connection = 1436 AccessibilityInteractionClient.getInstance(mService).getConnection( 1437 mService.mConnectionId); 1438 if (connection != null) { 1439 try { 1440 connection.setMagnificationCallbackEnabled(mDisplayId, enabled); 1441 } catch (RemoteException re) { 1442 throw new RuntimeException(re); 1443 } 1444 } 1445 } 1446 1447 /** 1448 * Dispatches magnification changes to any registered listeners. This 1449 * should be called on the service's main thread. 1450 */ dispatchMagnificationChanged(final @NonNull Region region, final MagnificationConfig config)1451 void dispatchMagnificationChanged(final @NonNull Region region, 1452 final MagnificationConfig config) { 1453 final ArrayMap<OnMagnificationChangedListener, Handler> entries; 1454 synchronized (mLock) { 1455 if (mListeners == null || mListeners.isEmpty()) { 1456 Slog.d(LOG_TAG, "Received magnification changed " 1457 + "callback with no listeners registered!"); 1458 setMagnificationCallbackEnabled(false); 1459 return; 1460 } 1461 1462 // Listeners may remove themselves. Perform a shallow copy to avoid concurrent 1463 // modification. 1464 entries = new ArrayMap<>(mListeners); 1465 } 1466 1467 for (int i = 0, count = entries.size(); i < count; i++) { 1468 final OnMagnificationChangedListener listener = entries.keyAt(i); 1469 final Handler handler = entries.valueAt(i); 1470 if (handler != null) { 1471 handler.post(() -> { 1472 listener.onMagnificationChanged(MagnificationController.this, 1473 region, config); 1474 }); 1475 } else { 1476 // We're already on the main thread, just run the listener. 1477 listener.onMagnificationChanged(this, region, config); 1478 } 1479 } 1480 } 1481 1482 /** 1483 * Gets the {@link MagnificationConfig} of the controlling magnifier on the display. 1484 * <p> 1485 * <strong>Note:</strong> If the service is not yet connected (e.g. 1486 * {@link AccessibilityService#onServiceConnected()} has not yet been 1487 * called) or the service has been disconnected, this method will 1488 * return null. 1489 * </p> 1490 * 1491 * @return the magnification config that the service controls 1492 */ getMagnificationConfig()1493 public @Nullable MagnificationConfig getMagnificationConfig() { 1494 final IAccessibilityServiceConnection connection = 1495 AccessibilityInteractionClient.getInstance(mService).getConnection( 1496 mService.mConnectionId); 1497 if (connection != null) { 1498 try { 1499 return connection.getMagnificationConfig(mDisplayId); 1500 } catch (RemoteException re) { 1501 Log.w(LOG_TAG, "Failed to obtain magnification config", re); 1502 re.rethrowFromSystemServer(); 1503 } 1504 } 1505 return null; 1506 } 1507 1508 /** 1509 * Returns the current magnification scale. 1510 * <p> 1511 * <strong>Note:</strong> If the service is not yet connected (e.g. 1512 * {@link AccessibilityService#onServiceConnected()} has not yet been 1513 * called) or the service has been disconnected, this method will 1514 * return a default value of {@code 1.0f}. 1515 * </p> 1516 * <p> 1517 * <strong>Note:</strong> This legacy API gets the scale of full-screen 1518 * magnification. To get the scale of the current controlling magnifier, 1519 * use {@link #getMagnificationConfig} instead. 1520 * </p> 1521 * 1522 * @return the current magnification scale 1523 * @deprecated Use {@link #getMagnificationConfig()} instead 1524 */ 1525 @Deprecated getScale()1526 public float getScale() { 1527 final IAccessibilityServiceConnection connection = 1528 AccessibilityInteractionClient.getInstance(mService).getConnection( 1529 mService.mConnectionId); 1530 if (connection != null) { 1531 try { 1532 return connection.getMagnificationScale(mDisplayId); 1533 } catch (RemoteException re) { 1534 Log.w(LOG_TAG, "Failed to obtain scale", re); 1535 re.rethrowFromSystemServer(); 1536 } 1537 } 1538 return 1.0f; 1539 } 1540 1541 /** 1542 * Returns the unscaled screen-relative X coordinate of the focal 1543 * center of the magnified region. This is the point around which 1544 * zooming occurs and is guaranteed to lie within the magnified 1545 * region. 1546 * <p> 1547 * <strong>Note:</strong> If the service is not yet connected (e.g. 1548 * {@link AccessibilityService#onServiceConnected()} has not yet been 1549 * called) or the service has been disconnected, this method will 1550 * return a default value of {@code 0.0f}. 1551 * </p> 1552 * <p> 1553 * <strong>Note:</strong> This legacy API gets the center position of full-screen 1554 * magnification. To get the magnification center of the current controlling magnifier, 1555 * use {@link #getMagnificationConfig} instead. 1556 * </p> 1557 * 1558 * @return the unscaled screen-relative X coordinate of the center of 1559 * the magnified region 1560 * @deprecated Use {@link #getMagnificationConfig()} instead 1561 */ 1562 @Deprecated getCenterX()1563 public float getCenterX() { 1564 final IAccessibilityServiceConnection connection = 1565 AccessibilityInteractionClient.getInstance(mService).getConnection( 1566 mService.mConnectionId); 1567 if (connection != null) { 1568 try { 1569 return connection.getMagnificationCenterX(mDisplayId); 1570 } catch (RemoteException re) { 1571 Log.w(LOG_TAG, "Failed to obtain center X", re); 1572 re.rethrowFromSystemServer(); 1573 } 1574 } 1575 return 0.0f; 1576 } 1577 1578 /** 1579 * Returns the unscaled screen-relative Y coordinate of the focal 1580 * center of the magnified region. This is the point around which 1581 * zooming occurs and is guaranteed to lie within the magnified 1582 * region. 1583 * <p> 1584 * <strong>Note:</strong> If the service is not yet connected (e.g. 1585 * {@link AccessibilityService#onServiceConnected()} has not yet been 1586 * called) or the service has been disconnected, this method will 1587 * return a default value of {@code 0.0f}. 1588 * </p> 1589 * <p> 1590 * <strong>Note:</strong> This legacy API gets the center position of full-screen 1591 * magnification. To get the magnification center of the current controlling magnifier, 1592 * use {@link #getMagnificationConfig} instead. 1593 * </p> 1594 * 1595 * @return the unscaled screen-relative Y coordinate of the center of 1596 * the magnified region 1597 * @deprecated Use {@link #getMagnificationConfig()} instead 1598 */ 1599 @Deprecated getCenterY()1600 public float getCenterY() { 1601 final IAccessibilityServiceConnection connection = 1602 AccessibilityInteractionClient.getInstance(mService).getConnection( 1603 mService.mConnectionId); 1604 if (connection != null) { 1605 try { 1606 return connection.getMagnificationCenterY(mDisplayId); 1607 } catch (RemoteException re) { 1608 Log.w(LOG_TAG, "Failed to obtain center Y", re); 1609 re.rethrowFromSystemServer(); 1610 } 1611 } 1612 return 0.0f; 1613 } 1614 1615 /** 1616 * Returns the region of the screen currently active for magnification. Changes to 1617 * magnification scale and center only affect this portion of the screen. The rest of the 1618 * screen, for example input methods, cannot be magnified. This region is relative to the 1619 * unscaled screen and is independent of the scale and center point. 1620 * <p> 1621 * The returned region will be empty if magnification is not active. Magnification is active 1622 * if magnification gestures are enabled or if a service is running that can control 1623 * magnification. 1624 * <p> 1625 * <strong>Note:</strong> If the service is not yet connected (e.g. 1626 * {@link AccessibilityService#onServiceConnected()} has not yet been 1627 * called) or the service has been disconnected, this method will 1628 * return an empty region. 1629 * </p> 1630 * <p> 1631 * <strong>Note:</strong> This legacy API gets the magnification region of full-screen 1632 * magnification. To get the magnification region of the current controlling magnifier, 1633 * use {@link #getCurrentMagnificationRegion()} instead. 1634 * </p> 1635 * 1636 * @return the region of the screen currently active for magnification, or an empty region 1637 * if magnification is not active. 1638 * @deprecated Use {@link #getCurrentMagnificationRegion()} instead 1639 */ 1640 @Deprecated 1641 @NonNull getMagnificationRegion()1642 public Region getMagnificationRegion() { 1643 final IAccessibilityServiceConnection connection = 1644 AccessibilityInteractionClient.getInstance(mService).getConnection( 1645 mService.mConnectionId); 1646 if (connection != null) { 1647 try { 1648 return connection.getMagnificationRegion(mDisplayId); 1649 } catch (RemoteException re) { 1650 Log.w(LOG_TAG, "Failed to obtain magnified region", re); 1651 re.rethrowFromSystemServer(); 1652 } 1653 } 1654 return Region.obtain(); 1655 } 1656 1657 /** 1658 * Returns the region of the screen currently active for magnification if the 1659 * controlling magnification is {@link MagnificationConfig#MAGNIFICATION_MODE_FULLSCREEN}. 1660 * Returns the region of screen projected on the magnification window if the 1661 * controlling magnification is {@link MagnificationConfig#MAGNIFICATION_MODE_WINDOW}. 1662 * 1663 * <p> 1664 * If the controlling mode is {@link MagnificationConfig#MAGNIFICATION_MODE_FULLSCREEN}, 1665 * the returned region will be empty if the magnification is 1666 * not active. And the magnification is active if magnification gestures are enabled 1667 * or if a service is running that can control magnification. 1668 * </p><p> 1669 * If the controlling mode is {@link MagnificationConfig#MAGNIFICATION_MODE_WINDOW}, 1670 * the returned region will be empty if the magnification is not activated. 1671 * </p><p> 1672 * <strong>Note:</strong> If the service is not yet connected (e.g. 1673 * {@link AccessibilityService#onServiceConnected()} has not yet been 1674 * called) or the service has been disconnected, this method will 1675 * return an empty region. 1676 * </p> 1677 * 1678 * @return the magnification region of the currently controlling magnification 1679 */ 1680 @NonNull getCurrentMagnificationRegion()1681 public Region getCurrentMagnificationRegion() { 1682 final IAccessibilityServiceConnection connection = 1683 AccessibilityInteractionClient.getInstance(mService).getConnection( 1684 mService.mConnectionId); 1685 if (connection != null) { 1686 try { 1687 return connection.getCurrentMagnificationRegion(mDisplayId); 1688 } catch (RemoteException re) { 1689 Log.w(LOG_TAG, "Failed to obtain the current magnified region", re); 1690 re.rethrowFromSystemServer(); 1691 } 1692 } 1693 return Region.obtain(); 1694 } 1695 1696 /** 1697 * Resets magnification scale and center to their default (e.g. no 1698 * magnification) values. 1699 * <p> 1700 * <strong>Note:</strong> If the service is not yet connected (e.g. 1701 * {@link AccessibilityService#onServiceConnected()} has not yet been 1702 * called) or the service has been disconnected, this method will have 1703 * no effect and return {@code false}. 1704 * <p> 1705 * <strong>Note:</strong> This legacy API reset full-screen magnification. 1706 * To reset the current controlling magnifier, use 1707 * {@link #resetCurrentMagnification(boolean)} ()} instead. 1708 * </p> 1709 * 1710 * @param animate {@code true} to animate from the current scale and 1711 * center or {@code false} to reset the scale and center 1712 * immediately 1713 * @return {@code true} on success, {@code false} on failure 1714 */ reset(boolean animate)1715 public boolean reset(boolean animate) { 1716 final IAccessibilityServiceConnection connection = 1717 AccessibilityInteractionClient.getInstance(mService).getConnection( 1718 mService.mConnectionId); 1719 if (connection != null) { 1720 try { 1721 return connection.resetMagnification(mDisplayId, animate); 1722 } catch (RemoteException re) { 1723 Log.w(LOG_TAG, "Failed to reset", re); 1724 re.rethrowFromSystemServer(); 1725 } 1726 } 1727 return false; 1728 } 1729 1730 /** 1731 * Resets magnification scale and center of the controlling magnification 1732 * to their default (e.g. no magnification) values. 1733 * <p> 1734 * <strong>Note:</strong> If the service is not yet connected (e.g. 1735 * {@link AccessibilityService#onServiceConnected()} has not yet been 1736 * called) or the service has been disconnected, this method will have 1737 * no effect and return {@code false}. 1738 * </p> 1739 * 1740 * @param animate {@code true} to animate from the current scale and 1741 * center or {@code false} to reset the scale and center 1742 * immediately 1743 * @return {@code true} on success, {@code false} on failure 1744 */ resetCurrentMagnification(boolean animate)1745 public boolean resetCurrentMagnification(boolean animate) { 1746 final IAccessibilityServiceConnection connection = 1747 AccessibilityInteractionClient.getInstance(mService).getConnection( 1748 mService.mConnectionId); 1749 if (connection != null) { 1750 try { 1751 return connection.resetCurrentMagnification(mDisplayId, animate); 1752 } catch (RemoteException re) { 1753 Log.w(LOG_TAG, "Failed to reset", re); 1754 re.rethrowFromSystemServer(); 1755 } 1756 } 1757 return false; 1758 } 1759 1760 /** 1761 * Sets the {@link MagnificationConfig}. The service controls the magnification by 1762 * setting the config. 1763 * <p> 1764 * <strong>Note:</strong> If the service is not yet connected (e.g. 1765 * {@link AccessibilityService#onServiceConnected()} has not yet been 1766 * called) or the service has been disconnected, this method will have 1767 * no effect and return {@code false}. 1768 * </p> 1769 * 1770 * @param config the magnification config 1771 * @param animate {@code true} to animate from the current spec or 1772 * {@code false} to set the spec immediately 1773 * @return {@code true} on success, {@code false} on failure 1774 */ setMagnificationConfig(@onNull MagnificationConfig config, boolean animate)1775 public boolean setMagnificationConfig(@NonNull MagnificationConfig config, 1776 boolean animate) { 1777 final IAccessibilityServiceConnection connection = 1778 AccessibilityInteractionClient.getInstance(mService).getConnection( 1779 mService.mConnectionId); 1780 if (connection != null) { 1781 try { 1782 return connection.setMagnificationConfig(mDisplayId, config, animate); 1783 } catch (RemoteException re) { 1784 Log.w(LOG_TAG, "Failed to set magnification config", re); 1785 re.rethrowFromSystemServer(); 1786 } 1787 } 1788 return false; 1789 } 1790 1791 /** 1792 * Sets the magnification scale. 1793 * <p> 1794 * <strong>Note:</strong> If the service is not yet connected (e.g. 1795 * {@link AccessibilityService#onServiceConnected()} has not yet been 1796 * called) or the service has been disconnected, this method will have 1797 * no effect and return {@code false}. 1798 * <p> 1799 * <strong>Note:</strong> This legacy API sets the scale of full-screen 1800 * magnification. To set the scale of the specified magnifier, 1801 * use {@link #setMagnificationConfig} instead. 1802 * </p> 1803 * 1804 * @param scale the magnification scale to set, must be >= 1 and <= 8 1805 * @param animate {@code true} to animate from the current scale or 1806 * {@code false} to set the scale immediately 1807 * @return {@code true} on success, {@code false} on failure 1808 * @deprecated Use {@link #setMagnificationConfig(MagnificationConfig, boolean)} instead 1809 */ 1810 @Deprecated setScale(float scale, boolean animate)1811 public boolean setScale(float scale, boolean animate) { 1812 final IAccessibilityServiceConnection connection = 1813 AccessibilityInteractionClient.getInstance(mService).getConnection( 1814 mService.mConnectionId); 1815 if (connection != null) { 1816 try { 1817 final MagnificationConfig config = new MagnificationConfig.Builder() 1818 .setMode(MAGNIFICATION_MODE_FULLSCREEN) 1819 .setScale(scale).build(); 1820 return connection.setMagnificationConfig(mDisplayId, config, animate); 1821 } catch (RemoteException re) { 1822 Log.w(LOG_TAG, "Failed to set scale", re); 1823 re.rethrowFromSystemServer(); 1824 } 1825 } 1826 return false; 1827 } 1828 1829 /** 1830 * Sets the center of the magnified viewport. 1831 * <p> 1832 * <strong>Note:</strong> If the service is not yet connected (e.g. 1833 * {@link AccessibilityService#onServiceConnected()} has not yet been 1834 * called) or the service has been disconnected, this method will have 1835 * no effect and return {@code false}. 1836 * </p> 1837 * <p> 1838 * <strong>Note:</strong> This legacy API sets the center of full-screen 1839 * magnification. To set the center of the specified magnifier, 1840 * use {@link #setMagnificationConfig} instead. 1841 * </p> 1842 * 1843 * @param centerX the unscaled screen-relative X coordinate on which to 1844 * center the viewport 1845 * @param centerY the unscaled screen-relative Y coordinate on which to 1846 * center the viewport 1847 * @param animate {@code true} to animate from the current viewport 1848 * center or {@code false} to set the center immediately 1849 * @return {@code true} on success, {@code false} on failure 1850 * @deprecated Use {@link #setMagnificationConfig(MagnificationConfig, boolean)} instead 1851 */ 1852 @Deprecated setCenter(float centerX, float centerY, boolean animate)1853 public boolean setCenter(float centerX, float centerY, boolean animate) { 1854 final IAccessibilityServiceConnection connection = 1855 AccessibilityInteractionClient.getInstance(mService).getConnection( 1856 mService.mConnectionId); 1857 if (connection != null) { 1858 try { 1859 final MagnificationConfig config = new MagnificationConfig.Builder() 1860 .setMode(MAGNIFICATION_MODE_FULLSCREEN) 1861 .setCenterX(centerX).setCenterY(centerY).build(); 1862 return connection.setMagnificationConfig(mDisplayId, config, animate); 1863 } catch (RemoteException re) { 1864 Log.w(LOG_TAG, "Failed to set center", re); 1865 re.rethrowFromSystemServer(); 1866 } 1867 } 1868 return false; 1869 } 1870 1871 /** 1872 * Listener for changes in the state of magnification. 1873 */ 1874 public interface OnMagnificationChangedListener { 1875 /** 1876 * Called when the magnified region, scale, or center changes. 1877 * <p> 1878 * <strong>Note:</strong> This legacy callback notifies only full-screen 1879 * magnification change. 1880 * </p> 1881 * 1882 * @param controller the magnification controller 1883 * @param region the magnification region 1884 * @param scale the new scale 1885 * @param centerX the new X coordinate, in unscaled coordinates, around which 1886 * magnification is focused 1887 * @param centerY the new Y coordinate, in unscaled coordinates, around which 1888 * magnification is focused 1889 * @deprecated Override 1890 * {@link #onMagnificationChanged(MagnificationController, Region, MagnificationConfig)} 1891 * instead 1892 */ 1893 @Deprecated onMagnificationChanged(@onNull MagnificationController controller, @NonNull Region region, float scale, float centerX, float centerY)1894 void onMagnificationChanged(@NonNull MagnificationController controller, 1895 @NonNull Region region, float scale, float centerX, float centerY); 1896 1897 /** 1898 * Called when the magnified region, mode, scale, or center changes of 1899 * all magnification modes. 1900 * <p> 1901 * <strong>Note:</strong> This method can be overridden to listen to the 1902 * magnification changes of all magnification modes then the legacy callback 1903 * would not receive the notifications. 1904 * Skipping calling super when overriding this method results in 1905 * {@link #onMagnificationChanged(MagnificationController, Region, float, float, float)} 1906 * not getting called. 1907 * </p> 1908 * 1909 * @param controller the magnification controller 1910 * @param region the magnification region 1911 * If the config mode is 1912 * {@link MagnificationConfig#MAGNIFICATION_MODE_FULLSCREEN}, 1913 * it is the region of the screen currently active for magnification. 1914 * that is the same region as {@link #getMagnificationRegion()}. 1915 * If the config mode is 1916 * {@link MagnificationConfig#MAGNIFICATION_MODE_WINDOW}, 1917 * it is the region of screen projected on the magnification window. 1918 * @param config The magnification config. That has the controlling magnification 1919 * mode, the new scale and the new screen-relative center position 1920 */ onMagnificationChanged(@onNull MagnificationController controller, @NonNull Region region, @NonNull MagnificationConfig config)1921 default void onMagnificationChanged(@NonNull MagnificationController controller, 1922 @NonNull Region region, @NonNull MagnificationConfig config) { 1923 if (config.getMode() == MAGNIFICATION_MODE_FULLSCREEN) { 1924 onMagnificationChanged(controller, region, 1925 config.getScale(), config.getCenterX(), config.getCenterY()); 1926 } 1927 } 1928 } 1929 } 1930 1931 /** 1932 * Returns the soft keyboard controller, which may be used to query and modify the soft keyboard 1933 * show mode. 1934 * 1935 * @return the soft keyboard controller 1936 */ 1937 @NonNull getSoftKeyboardController()1938 public final SoftKeyboardController getSoftKeyboardController() { 1939 synchronized (mLock) { 1940 if (mSoftKeyboardController == null) { 1941 mSoftKeyboardController = new SoftKeyboardController(this, mLock); 1942 } 1943 return mSoftKeyboardController; 1944 } 1945 } 1946 1947 /** 1948 * The default implementation returns our default {@link InputMethod}. Subclasses can override 1949 * it to provide their own customized version. Accessibility services need to set the 1950 * {@link AccessibilityServiceInfo#FLAG_INPUT_METHOD_EDITOR} flag to use input method APIs. 1951 * 1952 * @return the InputMethod. 1953 */ 1954 @NonNull onCreateInputMethod()1955 public InputMethod onCreateInputMethod() { 1956 return new InputMethod(this); 1957 } 1958 1959 /** 1960 * Returns the InputMethod instance after the system calls {@link #onCreateInputMethod()}, 1961 * which may be used to input text or get editable text selection change notifications. It will 1962 * return null if the accessibility service doesn't set the 1963 * {@link AccessibilityServiceInfo#FLAG_INPUT_METHOD_EDITOR} flag or the system doesn't call 1964 * {@link #onCreateInputMethod()}. 1965 * 1966 * @return the InputMethod instance 1967 */ 1968 @Nullable getInputMethod()1969 public final InputMethod getInputMethod() { 1970 return mInputMethod; 1971 } 1972 onSoftKeyboardShowModeChanged(int showMode)1973 private void onSoftKeyboardShowModeChanged(int showMode) { 1974 if (mSoftKeyboardController != null) { 1975 mSoftKeyboardController.dispatchSoftKeyboardShowModeChanged(showMode); 1976 } 1977 } 1978 1979 /** 1980 * Used to control, query, and listen for changes to the soft keyboard show mode. 1981 * <p> 1982 * Accessibility services may request to override the decisions normally made about whether or 1983 * not the soft keyboard is shown. 1984 * <p> 1985 * If multiple services make conflicting requests, the last request is honored. A service may 1986 * register a listener to find out if the mode has changed under it. 1987 * <p> 1988 * If the user takes action to override the behavior behavior requested by an accessibility 1989 * service, the user's request takes precendence, the show mode will be reset to 1990 * {@link AccessibilityService#SHOW_MODE_AUTO}, and services will no longer be able to control 1991 * that aspect of the soft keyboard's behavior. 1992 * <p> 1993 * Note: Because soft keyboards are independent apps, the framework does not have total control 1994 * over their behavior. They may choose to show themselves, or not, without regard to requests 1995 * made here. So the framework will make a best effort to deliver the behavior requested, but 1996 * cannot guarantee success. 1997 * 1998 * @see AccessibilityService#SHOW_MODE_AUTO 1999 * @see AccessibilityService#SHOW_MODE_HIDDEN 2000 * @see AccessibilityService#SHOW_MODE_IGNORE_HARD_KEYBOARD 2001 */ 2002 public static final class SoftKeyboardController { 2003 private final AccessibilityService mService; 2004 2005 /** 2006 * Map of listeners to their handlers. Lazily created when adding the first 2007 * soft keyboard change listener. 2008 */ 2009 private ArrayMap<OnShowModeChangedListener, Handler> mListeners; 2010 private final Object mLock; 2011 2012 /** @hide */ 2013 @Retention(RetentionPolicy.SOURCE) 2014 @IntDef({ 2015 ENABLE_IME_SUCCESS, 2016 ENABLE_IME_FAIL_BY_ADMIN, 2017 ENABLE_IME_FAIL_UNKNOWN 2018 }) 2019 public @interface EnableImeResult {} 2020 /** 2021 * Return value for {@link #setInputMethodEnabled(String, boolean)}. The action succeeded. 2022 */ 2023 public static final int ENABLE_IME_SUCCESS = 0; 2024 /** 2025 * Return value for {@link #setInputMethodEnabled(String, boolean)}. The action failed 2026 * because the InputMethod is not permitted by device policy manager. 2027 */ 2028 public static final int ENABLE_IME_FAIL_BY_ADMIN = 1; 2029 /** 2030 * Return value for {@link #setInputMethodEnabled(String, boolean)}. The action failed 2031 * and the reason is unknown. 2032 */ 2033 public static final int ENABLE_IME_FAIL_UNKNOWN = 2; 2034 SoftKeyboardController(@onNull AccessibilityService service, @NonNull Object lock)2035 SoftKeyboardController(@NonNull AccessibilityService service, @NonNull Object lock) { 2036 mService = service; 2037 mLock = lock; 2038 } 2039 2040 /** 2041 * Called when the service is connected. 2042 */ onServiceConnected()2043 void onServiceConnected() { 2044 synchronized(mLock) { 2045 if (mListeners != null && !mListeners.isEmpty()) { 2046 setSoftKeyboardCallbackEnabled(true); 2047 } 2048 } 2049 } 2050 2051 /** 2052 * Adds the specified change listener to the list of show mode change listeners. The 2053 * callback will occur on the service's main thread. Listener is not called on registration. 2054 */ addOnShowModeChangedListener(@onNull OnShowModeChangedListener listener)2055 public void addOnShowModeChangedListener(@NonNull OnShowModeChangedListener listener) { 2056 addOnShowModeChangedListener(listener, null); 2057 } 2058 2059 /** 2060 * Adds the specified change listener to the list of soft keyboard show mode change 2061 * listeners. The callback will occur on the specified {@link Handler}'s thread, or on the 2062 * services's main thread if the handler is {@code null}. 2063 * 2064 * @param listener the listener to add, must be non-null 2065 * @param handler the handler on which to callback should execute, or {@code null} to 2066 * execute on the service's main thread 2067 */ addOnShowModeChangedListener(@onNull OnShowModeChangedListener listener, @Nullable Handler handler)2068 public void addOnShowModeChangedListener(@NonNull OnShowModeChangedListener listener, 2069 @Nullable Handler handler) { 2070 synchronized (mLock) { 2071 if (mListeners == null) { 2072 mListeners = new ArrayMap<>(); 2073 } 2074 2075 final boolean shouldEnableCallback = mListeners.isEmpty(); 2076 mListeners.put(listener, handler); 2077 2078 if (shouldEnableCallback) { 2079 // This may fail if the service is not connected yet, but if we still have 2080 // listeners when it connects, we can try again. 2081 setSoftKeyboardCallbackEnabled(true); 2082 } 2083 } 2084 } 2085 2086 /** 2087 * Removes the specified change listener from the list of keyboard show mode change 2088 * listeners. 2089 * 2090 * @param listener the listener to remove, must be non-null 2091 * @return {@code true} if the listener was removed, {@code false} otherwise 2092 */ removeOnShowModeChangedListener( @onNull OnShowModeChangedListener listener)2093 public boolean removeOnShowModeChangedListener( 2094 @NonNull OnShowModeChangedListener listener) { 2095 if (mListeners == null) { 2096 return false; 2097 } 2098 2099 synchronized (mLock) { 2100 final int keyIndex = mListeners.indexOfKey(listener); 2101 final boolean hasKey = keyIndex >= 0; 2102 if (hasKey) { 2103 mListeners.removeAt(keyIndex); 2104 } 2105 2106 if (hasKey && mListeners.isEmpty()) { 2107 // We just removed the last listener, so we don't need callbacks from the 2108 // service anymore. 2109 setSoftKeyboardCallbackEnabled(false); 2110 } 2111 2112 return hasKey; 2113 } 2114 } 2115 setSoftKeyboardCallbackEnabled(boolean enabled)2116 private void setSoftKeyboardCallbackEnabled(boolean enabled) { 2117 final IAccessibilityServiceConnection connection = 2118 AccessibilityInteractionClient.getInstance(mService).getConnection( 2119 mService.mConnectionId); 2120 if (connection != null) { 2121 try { 2122 connection.setSoftKeyboardCallbackEnabled(enabled); 2123 } catch (RemoteException re) { 2124 throw new RuntimeException(re); 2125 } 2126 } 2127 } 2128 2129 /** 2130 * Dispatches the soft keyboard show mode change to any registered listeners. This should 2131 * be called on the service's main thread. 2132 */ dispatchSoftKeyboardShowModeChanged(final int showMode)2133 void dispatchSoftKeyboardShowModeChanged(final int showMode) { 2134 final ArrayMap<OnShowModeChangedListener, Handler> entries; 2135 synchronized (mLock) { 2136 if (mListeners == null || mListeners.isEmpty()) { 2137 Slog.w(LOG_TAG, "Received soft keyboard show mode changed callback" 2138 + " with no listeners registered!"); 2139 setSoftKeyboardCallbackEnabled(false); 2140 return; 2141 } 2142 2143 // Listeners may remove themselves. Perform a shallow copy to avoid concurrent 2144 // modification. 2145 entries = new ArrayMap<>(mListeners); 2146 } 2147 2148 for (int i = 0, count = entries.size(); i < count; i++) { 2149 final OnShowModeChangedListener listener = entries.keyAt(i); 2150 final Handler handler = entries.valueAt(i); 2151 if (handler != null) { 2152 handler.post(new Runnable() { 2153 @Override 2154 public void run() { 2155 listener.onShowModeChanged(SoftKeyboardController.this, showMode); 2156 } 2157 }); 2158 } else { 2159 // We're already on the main thread, just run the listener. 2160 listener.onShowModeChanged(this, showMode); 2161 } 2162 } 2163 } 2164 2165 /** 2166 * Returns the show mode of the soft keyboard. 2167 * 2168 * @return the current soft keyboard show mode 2169 * 2170 * @see AccessibilityService#SHOW_MODE_AUTO 2171 * @see AccessibilityService#SHOW_MODE_HIDDEN 2172 * @see AccessibilityService#SHOW_MODE_IGNORE_HARD_KEYBOARD 2173 */ 2174 @SoftKeyboardShowMode getShowMode()2175 public int getShowMode() { 2176 final IAccessibilityServiceConnection connection = 2177 AccessibilityInteractionClient.getInstance(mService).getConnection( 2178 mService.mConnectionId); 2179 if (connection != null) { 2180 try { 2181 return connection.getSoftKeyboardShowMode(); 2182 } catch (RemoteException re) { 2183 Log.w(LOG_TAG, "Failed to set soft keyboard behavior", re); 2184 re.rethrowFromSystemServer(); 2185 } 2186 } 2187 return SHOW_MODE_AUTO; 2188 } 2189 2190 /** 2191 * Sets the soft keyboard show mode. 2192 * <p> 2193 * <strong>Note:</strong> If the service is not yet connected (e.g. 2194 * {@link AccessibilityService#onServiceConnected()} has not yet been called) or the 2195 * service has been disconnected, this method will have no effect and return {@code false}. 2196 * 2197 * @param showMode the new show mode for the soft keyboard 2198 * @return {@code true} on success 2199 * 2200 * @see AccessibilityService#SHOW_MODE_AUTO 2201 * @see AccessibilityService#SHOW_MODE_HIDDEN 2202 * @see AccessibilityService#SHOW_MODE_IGNORE_HARD_KEYBOARD 2203 */ setShowMode(@oftKeyboardShowMode int showMode)2204 public boolean setShowMode(@SoftKeyboardShowMode int showMode) { 2205 final IAccessibilityServiceConnection connection = 2206 AccessibilityInteractionClient.getInstance(mService).getConnection( 2207 mService.mConnectionId); 2208 if (connection != null) { 2209 try { 2210 return connection.setSoftKeyboardShowMode(showMode); 2211 } catch (RemoteException re) { 2212 Log.w(LOG_TAG, "Failed to set soft keyboard behavior", re); 2213 re.rethrowFromSystemServer(); 2214 } 2215 } 2216 return false; 2217 } 2218 2219 /** 2220 * Listener for changes in the soft keyboard show mode. 2221 */ 2222 public interface OnShowModeChangedListener { 2223 /** 2224 * Called when the soft keyboard behavior changes. The default show mode is 2225 * {@code SHOW_MODE_AUTO}, where the soft keyboard is shown when a text input field is 2226 * focused. An AccessibilityService can also request the show mode 2227 * {@code SHOW_MODE_HIDDEN}, where the soft keyboard is never shown. 2228 * 2229 * @param controller the soft keyboard controller 2230 * @param showMode the current soft keyboard show mode 2231 */ onShowModeChanged(@onNull SoftKeyboardController controller, @SoftKeyboardShowMode int showMode)2232 void onShowModeChanged(@NonNull SoftKeyboardController controller, 2233 @SoftKeyboardShowMode int showMode); 2234 } 2235 2236 /** 2237 * Switches the current IME for the user for whom the service is enabled. The change will 2238 * persist until the current IME is explicitly changed again, and may persist beyond the 2239 * life cycle of the requesting service. 2240 * 2241 * @param imeId The ID of the input method to make current. This IME must be installed and 2242 * enabled. 2243 * @return {@code true} if the current input method was successfully switched to the input 2244 * method by {@code imeId}, 2245 * {@code false} if the input method specified is not installed, not enabled, or 2246 * otherwise not available to become the current IME 2247 * 2248 * @see android.view.inputmethod.InputMethodInfo#getId() 2249 */ switchToInputMethod(@onNull String imeId)2250 public boolean switchToInputMethod(@NonNull String imeId) { 2251 final IAccessibilityServiceConnection connection = 2252 AccessibilityInteractionClient.getInstance(mService).getConnection( 2253 mService.mConnectionId); 2254 if (connection != null) { 2255 try { 2256 return connection.switchToInputMethod(imeId); 2257 } catch (RemoteException re) { 2258 throw new RuntimeException(re); 2259 } 2260 } 2261 return false; 2262 } 2263 2264 /** 2265 * Enable or disable the specified IME for the user for whom the service is activated. The 2266 * IME needs to be in the same package as the service and needs to be allowed by device 2267 * policy, if there is one. The change will persist until the specified IME is next 2268 * explicitly enabled or disabled by whatever means, such as user choice, and may persist 2269 * beyond the life cycle of the requesting service. 2270 * 2271 * @param imeId The ID of the input method to enable or disable. This IME must be installed. 2272 * @param enabled {@code true} if the input method associated with {@code imeId} should be 2273 * enabled. 2274 * @return status code for the result of enabling/disabling the input method associated 2275 * with {@code imeId}. 2276 * @throws SecurityException if the input method is not in the same package as the service. 2277 * 2278 * @see android.view.inputmethod.InputMethodInfo#getId() 2279 */ 2280 @CheckResult 2281 @EnableImeResult setInputMethodEnabled(@onNull String imeId, boolean enabled)2282 public int setInputMethodEnabled(@NonNull String imeId, boolean enabled) 2283 throws SecurityException { 2284 final IAccessibilityServiceConnection connection = 2285 AccessibilityInteractionClient.getInstance(mService).getConnection( 2286 mService.mConnectionId); 2287 if (connection != null) { 2288 try { 2289 return connection.setInputMethodEnabled(imeId, enabled); 2290 } catch (RemoteException re) { 2291 throw new RuntimeException(re); 2292 } 2293 } 2294 return ENABLE_IME_FAIL_UNKNOWN; 2295 } 2296 } 2297 2298 /** 2299 * Returns the controller for the accessibility button within the system's navigation area. 2300 * This instance may be used to query the accessibility button's state and register listeners 2301 * for interactions with and state changes for the accessibility button when 2302 * {@link AccessibilityServiceInfo#FLAG_REQUEST_ACCESSIBILITY_BUTTON} is set. 2303 * <p> 2304 * <strong>Note:</strong> Not all devices are capable of displaying the accessibility button 2305 * within a navigation area, and as such, use of this class should be considered only as an 2306 * optional feature or shortcut on supported device implementations. 2307 * </p> 2308 * 2309 * @return the accessibility button controller for this {@link AccessibilityService} 2310 */ 2311 @NonNull getAccessibilityButtonController()2312 public final AccessibilityButtonController getAccessibilityButtonController() { 2313 return getAccessibilityButtonController(Display.DEFAULT_DISPLAY); 2314 } 2315 2316 /** 2317 * Returns the controller of specified logical display for the accessibility button within the 2318 * system's navigation area. This instance may be used to query the accessibility button's 2319 * state and register listeners for interactions with and state changes for the accessibility 2320 * button when {@link AccessibilityServiceInfo#FLAG_REQUEST_ACCESSIBILITY_BUTTON} is set. 2321 * <p> 2322 * <strong>Note:</strong> Not all devices are capable of displaying the accessibility button 2323 * within a navigation area, and as such, use of this class should be considered only as an 2324 * optional feature or shortcut on supported device implementations. 2325 * </p> 2326 * 2327 * @param displayId The logic display id, use {@link Display#DEFAULT_DISPLAY} for default 2328 * display. 2329 * @return the accessibility button controller for this {@link AccessibilityService} 2330 */ 2331 @NonNull getAccessibilityButtonController(int displayId)2332 public final AccessibilityButtonController getAccessibilityButtonController(int displayId) { 2333 synchronized (mLock) { 2334 AccessibilityButtonController controller = mAccessibilityButtonControllers.get( 2335 displayId); 2336 if (controller == null) { 2337 controller = new AccessibilityButtonController( 2338 AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId)); 2339 mAccessibilityButtonControllers.put(displayId, controller); 2340 } 2341 return controller; 2342 } 2343 } 2344 onAccessibilityButtonClicked(int displayId)2345 private void onAccessibilityButtonClicked(int displayId) { 2346 getAccessibilityButtonController(displayId).dispatchAccessibilityButtonClicked(); 2347 } 2348 onAccessibilityButtonAvailabilityChanged(boolean available)2349 private void onAccessibilityButtonAvailabilityChanged(boolean available) { 2350 getAccessibilityButtonController().dispatchAccessibilityButtonAvailabilityChanged( 2351 available); 2352 } 2353 2354 /** Sets the cache status. 2355 * 2356 * <p>If {@code enabled}, enable the cache and prefetching. Otherwise, disable the cache 2357 * and prefetching. 2358 * Note: By default the cache is enabled. 2359 * @param enabled whether to enable or disable the cache. 2360 * @return {@code true} if the cache and connection are not null, so the cache status is set. 2361 */ setCacheEnabled(boolean enabled)2362 public boolean setCacheEnabled(boolean enabled) { 2363 AccessibilityCache cache = 2364 AccessibilityInteractionClient.getCache(mConnectionId); 2365 if (cache == null) { 2366 return false; 2367 } 2368 final IAccessibilityServiceConnection connection = 2369 AccessibilityInteractionClient.getConnection(mConnectionId); 2370 if (connection == null) { 2371 return false; 2372 } 2373 try { 2374 connection.setCacheEnabled(enabled); 2375 cache.setEnabled(enabled); 2376 return true; 2377 } catch (RemoteException re) { 2378 Log.w(LOG_TAG, "Error while setting status of cache", re); 2379 re.rethrowFromSystemServer(); 2380 } 2381 return false; 2382 } 2383 2384 /** Invalidates {@code node} and its subtree in the cache. 2385 * @param node the node to invalidate. 2386 * @return {@code true} if the subtree rooted at {@code node} was invalidated. 2387 */ clearCachedSubtree(@onNull AccessibilityNodeInfo node)2388 public boolean clearCachedSubtree(@NonNull AccessibilityNodeInfo node) { 2389 AccessibilityCache cache = 2390 AccessibilityInteractionClient.getCache(mConnectionId); 2391 if (cache == null) { 2392 return false; 2393 } 2394 return cache.clearSubTree(node); 2395 } 2396 2397 /** Clears the cache. 2398 * @return {@code true} if the cache was cleared 2399 */ clearCache()2400 public boolean clearCache() { 2401 AccessibilityCache cache = 2402 AccessibilityInteractionClient.getCache(mConnectionId); 2403 if (cache == null) { 2404 return false; 2405 } 2406 cache.clear(); 2407 return true; 2408 } 2409 2410 /** Checks if {@code node} is in the cache. 2411 * @param node the node to check. 2412 * @return {@code true} if {@code node} is in the cache. 2413 */ isNodeInCache(@onNull AccessibilityNodeInfo node)2414 public boolean isNodeInCache(@NonNull AccessibilityNodeInfo node) { 2415 AccessibilityCache cache = 2416 AccessibilityInteractionClient.getCache(mConnectionId); 2417 if (cache == null) { 2418 return false; 2419 } 2420 return cache.isNodeInCache(node); 2421 } 2422 2423 /** Returns {@code true} if the cache is enabled. */ isCacheEnabled()2424 public boolean isCacheEnabled() { 2425 AccessibilityCache cache = 2426 AccessibilityInteractionClient.getCache(mConnectionId); 2427 if (cache == null) { 2428 return false; 2429 } 2430 return cache.isEnabled(); 2431 } 2432 2433 /** This is called when the system action list is changed. */ onSystemActionsChanged()2434 public void onSystemActionsChanged() { 2435 } 2436 2437 /** 2438 * Returns a list of system actions available in the system right now. 2439 * <p> 2440 * System actions that correspond to the global action constants will have matching action IDs. 2441 * For example, an with id {@link #GLOBAL_ACTION_BACK} will perform the back action. 2442 * </p> 2443 * <p> 2444 * These actions should be called by {@link #performGlobalAction}. 2445 * </p> 2446 * 2447 * @return A list of available system actions. 2448 */ getSystemActions()2449 public final @NonNull List<AccessibilityAction> getSystemActions() { 2450 IAccessibilityServiceConnection connection = 2451 AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); 2452 if (connection != null) { 2453 try { 2454 return connection.getSystemActions(); 2455 } catch (RemoteException re) { 2456 Log.w(LOG_TAG, "Error while calling getSystemActions", re); 2457 re.rethrowFromSystemServer(); 2458 } 2459 } 2460 return Collections.emptyList(); 2461 } 2462 2463 /** 2464 * Performs a global action. Such an action can be performed 2465 * at any moment regardless of the current application or user 2466 * location in that application. For example going back, going 2467 * home, opening recents, etc. 2468 * 2469 * <p> 2470 * Note: The global action ids themselves give no information about the current availability 2471 * of their corresponding actions. To determine if a global action is available, use 2472 * {@link #getSystemActions()} 2473 * 2474 * @param action The action to perform. 2475 * @return Whether the action was successfully performed. 2476 * 2477 * Perform actions using ids like the id constants referenced below: 2478 * @see #GLOBAL_ACTION_BACK 2479 * @see #GLOBAL_ACTION_HOME 2480 * @see #GLOBAL_ACTION_NOTIFICATIONS 2481 * @see #GLOBAL_ACTION_RECENTS 2482 * @see #GLOBAL_ACTION_DPAD_UP 2483 * @see #GLOBAL_ACTION_DPAD_DOWN 2484 * @see #GLOBAL_ACTION_DPAD_LEFT 2485 * @see #GLOBAL_ACTION_DPAD_RIGHT 2486 * @see #GLOBAL_ACTION_DPAD_CENTER 2487 */ performGlobalAction(int action)2488 public final boolean performGlobalAction(int action) { 2489 IAccessibilityServiceConnection connection = 2490 AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); 2491 if (connection != null) { 2492 try { 2493 return connection.performGlobalAction(action); 2494 } catch (RemoteException re) { 2495 Log.w(LOG_TAG, "Error while calling performGlobalAction", re); 2496 re.rethrowFromSystemServer(); 2497 } 2498 } 2499 return false; 2500 } 2501 2502 /** 2503 * Find the view that has the specified focus type. The search is performed 2504 * across all windows. 2505 * <p> 2506 * <strong>Note:</strong> In order to access the windows your service has 2507 * to declare the capability to retrieve window content by setting the 2508 * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent} 2509 * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}. 2510 * Also the service has to opt-in to retrieve the interactive windows by 2511 * setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} 2512 * flag. Otherwise, the search will be performed only in the active window. 2513 * </p> 2514 * <p> 2515 * <strong>Note:</strong> If the view with {@link AccessibilityNodeInfo#FOCUS_INPUT} 2516 * is on an embedded view hierarchy which is embedded in a {@link android.view.SurfaceView} via 2517 * {@link android.view.SurfaceView#setChildSurfacePackage}, there is a limitation that this API 2518 * won't be able to find the node for the view. It's because views don't know about 2519 * the embedded hierarchies. Instead, you could traverse all the nodes to find the 2520 * focus. 2521 * </p> 2522 * 2523 * @param focus The focus to find. One of {@link AccessibilityNodeInfo#FOCUS_INPUT} or 2524 * {@link AccessibilityNodeInfo#FOCUS_ACCESSIBILITY}. 2525 * @return The node info of the focused view or null. 2526 * 2527 * @see AccessibilityNodeInfo#FOCUS_INPUT 2528 * @see AccessibilityNodeInfo#FOCUS_ACCESSIBILITY 2529 */ findFocus(int focus)2530 public AccessibilityNodeInfo findFocus(int focus) { 2531 return AccessibilityInteractionClient.getInstance(this).findFocus(mConnectionId, 2532 AccessibilityWindowInfo.ANY_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID, focus); 2533 } 2534 2535 /** 2536 * Gets the an {@link AccessibilityServiceInfo} describing this 2537 * {@link AccessibilityService}. This method is useful if one wants 2538 * to change some of the dynamically configurable properties at 2539 * runtime. 2540 * 2541 * @return The accessibility service info. 2542 * 2543 * @see AccessibilityServiceInfo 2544 */ getServiceInfo()2545 public final AccessibilityServiceInfo getServiceInfo() { 2546 IAccessibilityServiceConnection connection = 2547 AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); 2548 if (connection != null) { 2549 try { 2550 return connection.getServiceInfo(); 2551 } catch (RemoteException re) { 2552 Log.w(LOG_TAG, "Error while getting AccessibilityServiceInfo", re); 2553 re.rethrowFromSystemServer(); 2554 } 2555 } 2556 return null; 2557 } 2558 2559 /** 2560 * Sets the {@link AccessibilityServiceInfo} that describes this service. 2561 * <p> 2562 * Note: You can call this method any time but the info will be picked up after 2563 * the system has bound to this service and when this method is called thereafter. 2564 * 2565 * @param info The info. 2566 */ setServiceInfo(AccessibilityServiceInfo info)2567 public final void setServiceInfo(AccessibilityServiceInfo info) { 2568 mInfo = info; 2569 updateInputMethod(info); 2570 mMotionEventSources = info.getMotionEventSources(); 2571 sendServiceInfo(); 2572 } 2573 2574 /** 2575 * Sets the {@link AccessibilityServiceInfo} for this service if the latter is 2576 * properly set and there is an {@link IAccessibilityServiceConnection} to the 2577 * AccessibilityManagerService. 2578 */ sendServiceInfo()2579 private void sendServiceInfo() { 2580 IAccessibilityServiceConnection connection = 2581 AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); 2582 if (mInfo != null && connection != null) { 2583 if (!mInfo.isWithinParcelableSize()) { 2584 throw new IllegalStateException( 2585 "Cannot update service info: size is larger than safe parcelable limits."); 2586 } 2587 try { 2588 connection.setServiceInfo(mInfo); 2589 mInfo = null; 2590 AccessibilityInteractionClient.getInstance(this).clearCache(mConnectionId); 2591 } catch (RemoteException re) { 2592 Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re); 2593 re.rethrowFromSystemServer(); 2594 } 2595 } 2596 } 2597 2598 @Override getSystemService(@erviceName @onNull String name)2599 public Object getSystemService(@ServiceName @NonNull String name) { 2600 if (getBaseContext() == null) { 2601 throw new IllegalStateException( 2602 "System services not available to Activities before onCreate()"); 2603 } 2604 2605 // Guarantee that we always return the same window manager instance. 2606 if (WINDOW_SERVICE.equals(name)) { 2607 if (mWindowManager == null) { 2608 mWindowManager = (WindowManager) getBaseContext().getSystemService(name); 2609 final WindowManagerImpl wm = (WindowManagerImpl) mWindowManager; 2610 // Set e default token obtained from the connection to ensure client could use 2611 // accessibility overlay. 2612 wm.setDefaultToken(mWindowToken); 2613 } 2614 return mWindowManager; 2615 } 2616 return super.getSystemService(name); 2617 } 2618 2619 /** 2620 * Takes a screenshot of the specified display and returns it via an 2621 * {@link AccessibilityService.ScreenshotResult}. You can use {@link Bitmap#wrapHardwareBuffer} 2622 * to construct the bitmap from the ScreenshotResult's payload. 2623 * <p> 2624 * <strong>Note:</strong> In order to take screenshot your service has 2625 * to declare the capability to take screenshot by setting the 2626 * {@link android.R.styleable#AccessibilityService_canTakeScreenshot} 2627 * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}. 2628 * </p> 2629 * 2630 * @param displayId The logic display id, must be {@link Display#DEFAULT_DISPLAY} for 2631 * default display. 2632 * @param executor Executor on which to run the callback. 2633 * @param callback The callback invoked when taking screenshot has succeeded or failed. 2634 * See {@link TakeScreenshotCallback} for details. 2635 * @see #takeScreenshotOfWindow 2636 */ takeScreenshot(int displayId, @NonNull @CallbackExecutor Executor executor, @NonNull TakeScreenshotCallback callback)2637 public void takeScreenshot(int displayId, @NonNull @CallbackExecutor Executor executor, 2638 @NonNull TakeScreenshotCallback callback) { 2639 Preconditions.checkNotNull(executor, "executor cannot be null"); 2640 Preconditions.checkNotNull(callback, "callback cannot be null"); 2641 final IAccessibilityServiceConnection connection = 2642 AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); 2643 if (connection == null) { 2644 sendScreenshotFailure(ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR, executor, callback); 2645 return; 2646 } 2647 try { 2648 connection.takeScreenshot(displayId, new RemoteCallback((result) -> { 2649 final int status = result.getInt(KEY_ACCESSIBILITY_SCREENSHOT_STATUS); 2650 if (status != TAKE_SCREENSHOT_SUCCESS) { 2651 sendScreenshotFailure(status, executor, callback); 2652 return; 2653 } 2654 final HardwareBuffer hardwareBuffer = 2655 result.getParcelable(KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER, android.hardware.HardwareBuffer.class); 2656 final ParcelableColorSpace colorSpace = 2657 result.getParcelable(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE, 2658 android.graphics.ParcelableColorSpace.class); 2659 final ScreenshotResult screenshot = new ScreenshotResult(hardwareBuffer, 2660 colorSpace.getColorSpace(), 2661 result.getLong(KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP)); 2662 sendScreenshotSuccess(screenshot, executor, callback); 2663 })); 2664 } catch (RemoteException re) { 2665 throw new RuntimeException(re); 2666 } 2667 } 2668 2669 /** 2670 * Takes a screenshot of the specified window and returns it via an 2671 * {@link AccessibilityService.ScreenshotResult}. You can use {@link Bitmap#wrapHardwareBuffer} 2672 * to construct the bitmap from the ScreenshotResult's payload. 2673 * <p> 2674 * <strong>Note:</strong> In order to take screenshots your service has 2675 * to declare the capability to take screenshot by setting the 2676 * {@link android.R.styleable#AccessibilityService_canTakeScreenshot} 2677 * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}. 2678 * </p> 2679 * <p> 2680 * Both this method and {@link #takeScreenshot} can be used for machine learning-based visual 2681 * screen understanding. Use <code>takeScreenshotOfWindow</code> if your target window might be 2682 * visually underneath an accessibility overlay (from your or another accessibility service) in 2683 * order to capture the window contents without the screenshot being covered by the overlay 2684 * contents drawn on the screen. 2685 * </p> 2686 * 2687 * @param accessibilityWindowId The window id, from {@link AccessibilityWindowInfo#getId()}. 2688 * @param executor Executor on which to run the callback. 2689 * @param callback The callback invoked when taking screenshot has succeeded or failed. 2690 * See {@link TakeScreenshotCallback} for details. 2691 * @see #takeScreenshot 2692 */ takeScreenshotOfWindow(int accessibilityWindowId, @NonNull @CallbackExecutor Executor executor, @NonNull TakeScreenshotCallback callback)2693 public void takeScreenshotOfWindow(int accessibilityWindowId, 2694 @NonNull @CallbackExecutor Executor executor, 2695 @NonNull TakeScreenshotCallback callback) { 2696 AccessibilityInteractionClient.getInstance(this).takeScreenshotOfWindow( 2697 mConnectionId, accessibilityWindowId, executor, callback); 2698 } 2699 2700 /** 2701 * Sets the strokeWidth and color of the accessibility focus rectangle. 2702 * <p> 2703 * <strong>Note:</strong> This setting persists until this or another active 2704 * AccessibilityService changes it or the device reboots. 2705 * </p> 2706 * 2707 * @param strokeWidth The stroke width of the rectangle in pixels. 2708 * Setting this value to zero results in no focus rectangle being drawn. 2709 * @param color The color of the rectangle. 2710 */ setAccessibilityFocusAppearance(int strokeWidth, @ColorInt int color)2711 public void setAccessibilityFocusAppearance(int strokeWidth, @ColorInt int color) { 2712 IAccessibilityServiceConnection connection = 2713 AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); 2714 if (connection != null) { 2715 try { 2716 connection.setFocusAppearance(strokeWidth, color); 2717 } catch (RemoteException re) { 2718 Log.w(LOG_TAG, "Error while setting the strokeWidth and color of the " 2719 + "accessibility focus rectangle", re); 2720 re.rethrowFromSystemServer(); 2721 } 2722 } 2723 } 2724 2725 /** 2726 * Implement to return the implementation of the internal accessibility 2727 * service interface. 2728 */ 2729 @Override onBind(Intent intent)2730 public final IBinder onBind(Intent intent) { 2731 return new IAccessibilityServiceClientWrapper(this, getMainExecutor(), new Callbacks() { 2732 @Override 2733 public void onServiceConnected() { 2734 AccessibilityService.this.dispatchServiceConnected(); 2735 } 2736 2737 @Override 2738 public void onInterrupt() { 2739 AccessibilityService.this.onInterrupt(); 2740 } 2741 2742 @Override 2743 public void onAccessibilityEvent(AccessibilityEvent event) { 2744 AccessibilityService.this.onAccessibilityEvent(event); 2745 } 2746 2747 @Override 2748 public void init(int connectionId, IBinder windowToken) { 2749 mConnectionId = connectionId; 2750 mWindowToken = windowToken; 2751 2752 // The client may have already obtained the window manager, so 2753 // update the default token on whatever manager we gave them. 2754 if (mWindowManager != null) { 2755 final WindowManagerImpl wm = (WindowManagerImpl) mWindowManager; 2756 wm.setDefaultToken(mWindowToken); 2757 } 2758 } 2759 2760 @Override 2761 public boolean onGesture(AccessibilityGestureEvent gestureEvent) { 2762 return AccessibilityService.this.onGesture(gestureEvent); 2763 } 2764 2765 @Override 2766 public boolean onKeyEvent(KeyEvent event) { 2767 return AccessibilityService.this.onKeyEvent(event); 2768 } 2769 2770 @Override 2771 public void onMagnificationChanged(int displayId, @NonNull Region region, 2772 MagnificationConfig config) { 2773 AccessibilityService.this.onMagnificationChanged(displayId, region, config); 2774 } 2775 2776 @Override 2777 public void onMotionEvent(MotionEvent event) { 2778 AccessibilityService.this.sendMotionEventToCallback(event); 2779 } 2780 2781 @Override 2782 public void onTouchStateChanged(int displayId, int state) { 2783 AccessibilityService.this.onTouchStateChanged(displayId, state); 2784 } 2785 2786 @Override 2787 public void onSoftKeyboardShowModeChanged(int showMode) { 2788 AccessibilityService.this.onSoftKeyboardShowModeChanged(showMode); 2789 } 2790 2791 @Override 2792 public void onPerformGestureResult(int sequence, boolean completedSuccessfully) { 2793 AccessibilityService.this.onPerformGestureResult(sequence, completedSuccessfully); 2794 } 2795 2796 @Override 2797 public void onFingerprintCapturingGesturesChanged(boolean active) { 2798 AccessibilityService.this.onFingerprintCapturingGesturesChanged(active); 2799 } 2800 2801 @Override 2802 public void onFingerprintGesture(int gesture) { 2803 AccessibilityService.this.onFingerprintGesture(gesture); 2804 } 2805 2806 @Override 2807 public void onAccessibilityButtonClicked(int displayId) { 2808 AccessibilityService.this.onAccessibilityButtonClicked(displayId); 2809 } 2810 2811 @Override 2812 public void onAccessibilityButtonAvailabilityChanged(boolean available) { 2813 AccessibilityService.this.onAccessibilityButtonAvailabilityChanged(available); 2814 } 2815 2816 @Override 2817 public void onSystemActionsChanged() { 2818 AccessibilityService.this.onSystemActionsChanged(); 2819 } 2820 2821 @Override 2822 public void createImeSession(IAccessibilityInputMethodSessionCallback callback) { 2823 if (mInputMethod != null) { 2824 mInputMethod.createImeSession(callback); 2825 } 2826 } 2827 2828 @Override 2829 public void startInput(@Nullable RemoteAccessibilityInputConnection connection, 2830 @NonNull EditorInfo editorInfo, boolean restarting) { 2831 if (mInputMethod != null) { 2832 if (restarting) { 2833 mInputMethod.restartInput(connection, editorInfo); 2834 } else { 2835 mInputMethod.startInput(connection, editorInfo); 2836 } 2837 } 2838 } 2839 }); 2840 } 2841 2842 /** 2843 * Implements the internal {@link IAccessibilityServiceClient} interface to convert 2844 * incoming calls to it back to calls on an {@link AccessibilityService}. 2845 * 2846 * @hide 2847 */ 2848 public static class IAccessibilityServiceClientWrapper extends 2849 IAccessibilityServiceClient.Stub { 2850 2851 private final Callbacks mCallback; 2852 private final Context mContext; 2853 private final Executor mExecutor; 2854 2855 private int mConnectionId = AccessibilityInteractionClient.NO_ID; 2856 2857 /** 2858 * This is not {@code null} only between {@link #bindInput()} and {@link #unbindInput()} so 2859 * that {@link RemoteAccessibilityInputConnection} can query if {@link #unbindInput()} has 2860 * already been called or not, mainly to avoid unnecessary blocking operations. 2861 * 2862 * <p>This field must be set and cleared only from the binder thread(s), where the system 2863 * guarantees that {@link #bindInput()}, 2864 * {@link #startInput(IRemoteAccessibilityInputConnection, EditorInfo, boolean)}, 2865 * and {@link #unbindInput()} are called with the same order as the original calls 2866 * in {@link com.android.server.inputmethod.InputMethodManagerService}. 2867 * See {@link IBinder#FLAG_ONEWAY} for detailed semantics.</p> 2868 */ 2869 @Nullable 2870 CancellationGroup mCancellationGroup = null; 2871 2872 public IAccessibilityServiceClientWrapper(Context context, Executor executor, 2873 Callbacks callback) { 2874 mCallback = callback; 2875 mContext = context; 2876 mExecutor = executor; 2877 } 2878 2879 public IAccessibilityServiceClientWrapper(Context context, Looper looper, 2880 Callbacks callback) { 2881 this(context, new HandlerExecutor(new Handler(looper)), callback); 2882 } 2883 2884 public void init(IAccessibilityServiceConnection connection, int connectionId, 2885 IBinder windowToken) { 2886 mExecutor.execute(() -> { 2887 mConnectionId = connectionId; 2888 if (connection != null) { 2889 AccessibilityInteractionClient.getInstance(mContext).addConnection( 2890 mConnectionId, connection, /*initializeCache=*/true); 2891 if (mContext != null) { 2892 try { 2893 connection.setAttributionTag(mContext.getAttributionTag()); 2894 } catch (RemoteException re) { 2895 Log.w(LOG_TAG, "Error while setting attributionTag", re); 2896 re.rethrowFromSystemServer(); 2897 } 2898 } 2899 mCallback.init(mConnectionId, windowToken); 2900 mCallback.onServiceConnected(); 2901 } else { 2902 AccessibilityInteractionClient.getInstance(mContext) 2903 .clearCache(mConnectionId); 2904 AccessibilityInteractionClient.getInstance(mContext).removeConnection( 2905 mConnectionId); 2906 mConnectionId = AccessibilityInteractionClient.NO_ID; 2907 mCallback.init(AccessibilityInteractionClient.NO_ID, null); 2908 } 2909 return; 2910 }); 2911 } 2912 2913 public void onInterrupt() { 2914 mExecutor.execute(() -> { 2915 if (mConnectionId != AccessibilityInteractionClient.NO_ID) { 2916 mCallback.onInterrupt(); 2917 } 2918 }); 2919 } 2920 2921 public void onAccessibilityEvent(AccessibilityEvent event, boolean serviceWantsEvent) { 2922 mExecutor.execute(() -> { 2923 if (event != null) { 2924 // Send the event to AccessibilityCache via AccessibilityInteractionClient 2925 AccessibilityInteractionClient.getInstance(mContext).onAccessibilityEvent( 2926 event, mConnectionId); 2927 if (serviceWantsEvent 2928 && (mConnectionId != AccessibilityInteractionClient.NO_ID)) { 2929 // Send the event to AccessibilityService 2930 mCallback.onAccessibilityEvent(event); 2931 } 2932 } 2933 return; 2934 }); 2935 } 2936 2937 @Override 2938 public void onGesture(AccessibilityGestureEvent gestureInfo) { 2939 mExecutor.execute(() -> { 2940 if (mConnectionId != AccessibilityInteractionClient.NO_ID) { 2941 mCallback.onGesture(gestureInfo); 2942 } 2943 return; 2944 }); 2945 } 2946 2947 public void clearAccessibilityCache() { 2948 mExecutor.execute(() -> { 2949 AccessibilityInteractionClient.getInstance(mContext).clearCache(mConnectionId); 2950 return; 2951 }); 2952 } 2953 2954 @Override 2955 public void onKeyEvent(KeyEvent event, int sequence) { 2956 mExecutor.execute(() -> { 2957 try { 2958 IAccessibilityServiceConnection connection = AccessibilityInteractionClient 2959 .getInstance(mContext).getConnection(mConnectionId); 2960 if (connection != null) { 2961 final boolean result = mCallback.onKeyEvent(event); 2962 try { 2963 connection.setOnKeyEventResult(result, sequence); 2964 } catch (RemoteException re) { 2965 /* ignore */ 2966 } 2967 } 2968 } finally { 2969 // Make sure the event is recycled. 2970 try { 2971 event.recycle(); 2972 } catch (IllegalStateException ise) { 2973 /* ignore - best effort */ 2974 } 2975 } 2976 return; 2977 }); 2978 } 2979 2980 /** Magnification changed callbacks for different displays */ 2981 public void onMagnificationChanged(int displayId, @NonNull Region region, 2982 MagnificationConfig config) { 2983 mExecutor.execute(() -> { 2984 if (mConnectionId != AccessibilityInteractionClient.NO_ID) { 2985 mCallback.onMagnificationChanged(displayId, region, config); 2986 } 2987 return; 2988 }); 2989 } 2990 2991 public void onSoftKeyboardShowModeChanged(int showMode) { 2992 mExecutor.execute(() -> { 2993 if (mConnectionId != AccessibilityInteractionClient.NO_ID) { 2994 mCallback.onSoftKeyboardShowModeChanged(showMode); 2995 } 2996 return; 2997 }); 2998 } 2999 3000 public void onPerformGestureResult(int sequence, boolean successfully) { 3001 mExecutor.execute(() -> { 3002 if (mConnectionId != AccessibilityInteractionClient.NO_ID) { 3003 mCallback.onPerformGestureResult(sequence, successfully); 3004 } 3005 return; 3006 }); 3007 } 3008 3009 public void onFingerprintCapturingGesturesChanged(boolean active) { 3010 mExecutor.execute(() -> { 3011 if (mConnectionId != AccessibilityInteractionClient.NO_ID) { 3012 mCallback.onFingerprintCapturingGesturesChanged(active); 3013 } 3014 return; 3015 }); 3016 } 3017 3018 public void onFingerprintGesture(int gesture) { 3019 mExecutor.execute(() -> { 3020 if (mConnectionId != AccessibilityInteractionClient.NO_ID) { 3021 mCallback.onFingerprintGesture(gesture); 3022 } 3023 return; 3024 }); 3025 } 3026 3027 /** Accessibility button clicked callbacks for different displays */ 3028 public void onAccessibilityButtonClicked(int displayId) { 3029 mExecutor.execute(() -> { 3030 if (mConnectionId != AccessibilityInteractionClient.NO_ID) { 3031 mCallback.onAccessibilityButtonClicked(displayId); 3032 } 3033 return; 3034 }); 3035 } 3036 3037 public void onAccessibilityButtonAvailabilityChanged(boolean available) { 3038 mExecutor.execute(() -> { 3039 if (mConnectionId != AccessibilityInteractionClient.NO_ID) { 3040 mCallback.onAccessibilityButtonAvailabilityChanged(available); 3041 } 3042 return; 3043 }); 3044 } 3045 3046 /** This is called when the system action list is changed. */ 3047 public void onSystemActionsChanged() { 3048 mExecutor.execute(() -> { 3049 if (mConnectionId != AccessibilityInteractionClient.NO_ID) { 3050 mCallback.onSystemActionsChanged(); 3051 } 3052 return; 3053 }); 3054 } 3055 3056 /** This is called when an app requests ime sessions or when the service is enabled. */ 3057 public void createImeSession(IAccessibilityInputMethodSessionCallback callback) { 3058 mExecutor.execute(() -> { 3059 if (mConnectionId != AccessibilityInteractionClient.NO_ID) { 3060 mCallback.createImeSession(callback); 3061 } 3062 }); 3063 } 3064 3065 /** 3066 * This is called when InputMethodManagerService requests to set the session enabled or 3067 * disabled 3068 */ 3069 public void setImeSessionEnabled(IAccessibilityInputMethodSession session, 3070 boolean enabled) { 3071 try { 3072 AccessibilityInputMethodSession ls = 3073 ((AccessibilityInputMethodSessionWrapper) session).getSession(); 3074 if (ls == null) { 3075 Log.w(LOG_TAG, "Session is already finished: " + session); 3076 return; 3077 } 3078 mExecutor.execute(() -> { 3079 if (mConnectionId != AccessibilityInteractionClient.NO_ID) { 3080 ls.setEnabled(enabled); 3081 } 3082 return; 3083 }); 3084 } catch (ClassCastException e) { 3085 Log.w(LOG_TAG, "Incoming session not of correct type: " + session, e); 3086 } 3087 } 3088 3089 /** This is called when an app binds input or when the service is enabled. */ 3090 public void bindInput() { 3091 if (mCancellationGroup != null) { 3092 Log.e(LOG_TAG, "bindInput must be paired with unbindInput."); 3093 } 3094 mCancellationGroup = new CancellationGroup(); 3095 } 3096 3097 /** This is called when an app unbinds input or when the service is disabled. */ 3098 public void unbindInput() { 3099 if (mCancellationGroup != null) { 3100 // Signal the flag then forget it. 3101 mCancellationGroup.cancelAll(); 3102 mCancellationGroup = null; 3103 } else { 3104 Log.e(LOG_TAG, "unbindInput must be paired with bindInput."); 3105 } 3106 } 3107 3108 /** This is called when an app starts input or when the service is enabled. */ 3109 public void startInput(IRemoteAccessibilityInputConnection connection, 3110 EditorInfo editorInfo, boolean restarting) { 3111 if (mCancellationGroup == null) { 3112 Log.e(LOG_TAG, "startInput must be called after bindInput."); 3113 mCancellationGroup = new CancellationGroup(); 3114 } 3115 mExecutor.execute(() -> { 3116 if (mConnectionId != AccessibilityInteractionClient.NO_ID) { 3117 final RemoteAccessibilityInputConnection ic = connection == null ? null 3118 : new RemoteAccessibilityInputConnection( 3119 connection, mCancellationGroup); 3120 editorInfo.makeCompatible(mContext.getApplicationInfo().targetSdkVersion); 3121 mCallback.startInput(ic, editorInfo, restarting); 3122 } 3123 }); 3124 } 3125 3126 @Override 3127 public void onMotionEvent(MotionEvent event) { 3128 mExecutor.execute(() -> { 3129 mCallback.onMotionEvent(event); 3130 }); 3131 } 3132 3133 @Override 3134 public void onTouchStateChanged(int displayId, int state) { 3135 mExecutor.execute(() -> { 3136 mCallback.onTouchStateChanged(displayId, state); 3137 }); 3138 } 3139 } 3140 3141 /** 3142 * Class used to report status of dispatched gestures 3143 */ 3144 public static abstract class GestureResultCallback { 3145 /** Called when the gesture has completed successfully 3146 * 3147 * @param gestureDescription The description of the gesture that completed. 3148 */ 3149 public void onCompleted(GestureDescription gestureDescription) { 3150 } 3151 3152 /** Called when the gesture was cancelled 3153 * 3154 * @param gestureDescription The description of the gesture that was cancelled. 3155 */ 3156 public void onCancelled(GestureDescription gestureDescription) { 3157 } 3158 } 3159 3160 /* Object to keep track of gesture result callbacks */ 3161 private static class GestureResultCallbackInfo { 3162 GestureDescription gestureDescription; 3163 GestureResultCallback callback; 3164 Handler handler; 3165 3166 GestureResultCallbackInfo(GestureDescription gestureDescription, 3167 GestureResultCallback callback, Handler handler) { 3168 this.gestureDescription = gestureDescription; 3169 this.callback = callback; 3170 this.handler = handler; 3171 } 3172 } 3173 3174 private void sendScreenshotSuccess(ScreenshotResult screenshot, Executor executor, 3175 TakeScreenshotCallback callback) { 3176 executor.execute(() -> callback.onSuccess(screenshot)); 3177 } 3178 3179 private void sendScreenshotFailure(@ScreenshotErrorCode int errorCode, Executor executor, 3180 TakeScreenshotCallback callback) { 3181 executor.execute(() -> callback.onFailure(errorCode)); 3182 } 3183 3184 /** 3185 * Interface used to report status of taking screenshot. 3186 */ 3187 public interface TakeScreenshotCallback { 3188 /** Called when taking screenshot has completed successfully. 3189 * 3190 * @param screenshot The content of screenshot. 3191 */ 3192 void onSuccess(@NonNull ScreenshotResult screenshot); 3193 3194 /** Called when taking screenshot has failed. {@code errorCode} will identify the 3195 * reason of failure. 3196 * 3197 * @param errorCode The error code of this operation. 3198 */ 3199 void onFailure(@ScreenshotErrorCode int errorCode); 3200 } 3201 3202 /** 3203 * Can be used to construct a bitmap of the screenshot or any other operations for 3204 * {@link AccessibilityService#takeScreenshot} API. 3205 */ 3206 public static final class ScreenshotResult { 3207 private final @NonNull HardwareBuffer mHardwareBuffer; 3208 private final @NonNull ColorSpace mColorSpace; 3209 private final long mTimestamp; 3210 3211 /** @hide */ 3212 public ScreenshotResult(@NonNull HardwareBuffer hardwareBuffer, 3213 @NonNull ColorSpace colorSpace, long timestamp) { 3214 Preconditions.checkNotNull(hardwareBuffer, "hardwareBuffer cannot be null"); 3215 Preconditions.checkNotNull(colorSpace, "colorSpace cannot be null"); 3216 mHardwareBuffer = hardwareBuffer; 3217 mColorSpace = colorSpace; 3218 mTimestamp = timestamp; 3219 } 3220 3221 /** 3222 * Gets the {@link ColorSpace} identifying a specific organization of colors of the 3223 * screenshot. 3224 * 3225 * @return the color space 3226 */ 3227 @NonNull 3228 public ColorSpace getColorSpace() { 3229 return mColorSpace; 3230 } 3231 3232 /** 3233 * Gets the {@link HardwareBuffer} representing a memory buffer of the screenshot. 3234 * <p> 3235 * <strong>Note:</strong> The application should call {@link HardwareBuffer#close()} when 3236 * the buffer is no longer needed to free the underlying resources. 3237 * </p> 3238 * 3239 * @return the hardware buffer 3240 */ 3241 @NonNull 3242 public HardwareBuffer getHardwareBuffer() { 3243 return mHardwareBuffer; 3244 } 3245 3246 /** 3247 * Gets the timestamp of taking the screenshot. 3248 * 3249 * @return milliseconds of non-sleep uptime before screenshot since boot and it's from 3250 * {@link SystemClock#uptimeMillis()} 3251 */ 3252 public long getTimestamp() { 3253 return mTimestamp; 3254 }; 3255 } 3256 3257 /** 3258 * When {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} is enabled, this 3259 * function requests that touch interactions starting in the specified region of the screen 3260 * bypass the gesture detector. There can only be one gesture detection passthrough region per 3261 * display. Requesting a new gesture detection passthrough region clears the existing one. To 3262 * disable this passthrough and return to the original behavior, pass in an empty region. When 3263 * {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} is disabled this 3264 * function has no effect. 3265 * 3266 * @param displayId The display on which to set this region. 3267 * @param region the region of the screen. 3268 */ 3269 public void setGestureDetectionPassthroughRegion(int displayId, @NonNull Region region) { 3270 Preconditions.checkNotNull(region, "region cannot be null"); 3271 final IAccessibilityServiceConnection connection = 3272 AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); 3273 if (connection != null) { 3274 try { 3275 connection.setGestureDetectionPassthroughRegion(displayId, region); 3276 } catch (RemoteException re) { 3277 throw new RuntimeException(re); 3278 } 3279 } 3280 } 3281 3282 /** 3283 * When {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} is enabled, this 3284 * function requests that touch interactions starting in the specified region of the screen 3285 * bypass the touch explorer and go straight to the view hierarchy. There can only be one touch 3286 * exploration passthrough region per display. Requesting a new touch explorationpassthrough 3287 * region clears the existing one. To disable this passthrough and return to the original 3288 * behavior, pass in an empty region. When {@link 3289 * AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} is disabled this function has 3290 * no effect. 3291 * 3292 * @param displayId The display on which to set this region. 3293 * @param region the region of the screen . 3294 */ 3295 public void setTouchExplorationPassthroughRegion(int displayId, @NonNull Region region) { 3296 Preconditions.checkNotNull(region, "region cannot be null"); 3297 final IAccessibilityServiceConnection connection = 3298 AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); 3299 if (connection != null) { 3300 try { 3301 connection.setTouchExplorationPassthroughRegion(displayId, region); 3302 } catch (RemoteException re) { 3303 throw new RuntimeException(re); 3304 } 3305 } 3306 } 3307 3308 /** 3309 * Sets the system settings values that control the scaling factor for animations. The scale 3310 * controls the animation playback speed for animations that respect these settings. Animations 3311 * that do not respect the settings values will not be affected by this function. A lower scale 3312 * value results in a faster speed. A value of <code>0</code> disables animations entirely. When 3313 * animations are disabled services receive window change events more quickly which can reduce 3314 * the potential by confusion by reducing the time during which windows are in transition. 3315 * 3316 * @see AccessibilityEvent#TYPE_WINDOWS_CHANGED 3317 * @see AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED 3318 * @see android.provider.Settings.Global#WINDOW_ANIMATION_SCALE 3319 * @see android.provider.Settings.Global#TRANSITION_ANIMATION_SCALE 3320 * @see android.provider.Settings.Global#ANIMATOR_DURATION_SCALE 3321 * @param scale The scaling factor for all animations. 3322 */ 3323 public void setAnimationScale(float scale) { 3324 final IAccessibilityServiceConnection connection = 3325 AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); 3326 if (connection != null) { 3327 try { 3328 connection.setAnimationScale(scale); 3329 } catch (RemoteException re) { 3330 throw new RuntimeException(re); 3331 } 3332 } 3333 } 3334 3335 private static class AccessibilityContext extends ContextWrapper { 3336 private final int mConnectionId; 3337 3338 private AccessibilityContext(Context base, int connectionId) { 3339 super(base); 3340 mConnectionId = connectionId; 3341 setDefaultTokenInternal(this, getDisplayId()); 3342 } 3343 3344 @NonNull 3345 @Override 3346 public Context createDisplayContext(Display display) { 3347 return new AccessibilityContext(super.createDisplayContext(display), mConnectionId); 3348 } 3349 3350 @NonNull 3351 @Override 3352 public Context createWindowContext(int type, @Nullable Bundle options) { 3353 final Context context = super.createWindowContext(type, options); 3354 if (type != TYPE_ACCESSIBILITY_OVERLAY) { 3355 return context; 3356 } 3357 return new AccessibilityContext(context, mConnectionId); 3358 } 3359 3360 @NonNull 3361 @Override 3362 public Context createWindowContext(@NonNull Display display, int type, 3363 @Nullable Bundle options) { 3364 final Context context = super.createWindowContext(display, type, options); 3365 if (type != TYPE_ACCESSIBILITY_OVERLAY) { 3366 return context; 3367 } 3368 return new AccessibilityContext(context, mConnectionId); 3369 } 3370 3371 private void setDefaultTokenInternal(Context context, int displayId) { 3372 final WindowManagerImpl wm = (WindowManagerImpl) context.getSystemService( 3373 WINDOW_SERVICE); 3374 final IAccessibilityServiceConnection connection = 3375 AccessibilityInteractionClient.getConnection(mConnectionId); 3376 IBinder token = null; 3377 if (connection != null) { 3378 try { 3379 token = connection.getOverlayWindowToken(displayId); 3380 } catch (RemoteException re) { 3381 Log.w(LOG_TAG, "Failed to get window token", re); 3382 re.rethrowFromSystemServer(); 3383 } 3384 wm.setDefaultToken(token); 3385 } 3386 } 3387 } 3388 3389 /** 3390 * Returns the touch interaction controller for the specified logical display, which may be used 3391 * to detect gestures and otherwise control touch interactions. If 3392 * {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} is disabled the 3393 * controller's methods will have no effect. 3394 * 3395 * @param displayId The logical display id, use {@link Display#DEFAULT_DISPLAY} for default 3396 * display. 3397 * @return the TouchExploration controller 3398 */ 3399 @NonNull 3400 public final TouchInteractionController getTouchInteractionController(int displayId) { 3401 synchronized (mLock) { 3402 TouchInteractionController controller = mTouchInteractionControllers.get(displayId); 3403 if (controller == null) { 3404 controller = new TouchInteractionController(this, mLock, displayId); 3405 mTouchInteractionControllers.put(displayId, controller); 3406 } 3407 return controller; 3408 } 3409 } 3410 3411 void sendMotionEventToCallback(MotionEvent event) { 3412 boolean sendingTouchEventToTouchInteractionController = false; 3413 if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) { 3414 TouchInteractionController controller; 3415 synchronized (mLock) { 3416 int displayId = event.getDisplayId(); 3417 controller = mTouchInteractionControllers.get(displayId); 3418 } 3419 if (controller != null) { 3420 sendingTouchEventToTouchInteractionController = true; 3421 controller.onMotionEvent(event); 3422 } 3423 } 3424 final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK; 3425 if ((mMotionEventSources & eventSourceWithoutClass) != 0 3426 && !sendingTouchEventToTouchInteractionController) { 3427 onMotionEvent(event); 3428 } 3429 } 3430 3431 void onTouchStateChanged(int displayId, int state) { 3432 TouchInteractionController controller; 3433 synchronized (mLock) { 3434 controller = mTouchInteractionControllers.get(displayId); 3435 } 3436 if (controller != null) { 3437 controller.onStateChanged(state); 3438 } 3439 } 3440 3441 /** 3442 * <p>Attaches a {@link android.view.SurfaceControl} containing an accessibility 3443 * overlay to the 3444 * specified display. This type of overlay should be used for content that does 3445 * not need to 3446 * track the location and size of Views in the currently active app e.g. service 3447 * configuration 3448 * or general service UI.</p> 3449 * <p>Generally speaking, an accessibility overlay will be a {@link android.view.View}. 3450 * To embed the View into a {@link android.view.SurfaceControl}, create a 3451 * {@link android.view.SurfaceControlViewHost} and attach the View using 3452 * {@link android.view.SurfaceControlViewHost#setView}. Then obtain the SurfaceControl by 3453 * calling <code> viewHost.getSurfacePackage().getSurfaceControl()</code>.</p> 3454 * <p>To remove this overlay and free the associated 3455 * resources, use 3456 * <code> new SurfaceControl.Transaction().reparent(sc, null).apply();</code>.</p> 3457 * <p>If the specified overlay has already been attached to the specified display 3458 * this method does nothing. 3459 * If the specified overlay has already been attached to a previous display this 3460 * function will transfer the overlay to the new display. 3461 * Services can attach multiple overlays. Use 3462 * <code> new SurfaceControl.Transaction().setLayer(sc, layer).apply();</code>. 3463 * to coordinate the order of the overlays on screen.</p> 3464 * 3465 * @param displayId the display to which the SurfaceControl should be attached. 3466 * @param sc the SurfaceControl containing the overlay content 3467 */ 3468 public void attachAccessibilityOverlayToDisplay(int displayId, @NonNull SurfaceControl sc) { 3469 Preconditions.checkNotNull(sc, "SurfaceControl cannot be null"); 3470 final IAccessibilityServiceConnection connection = 3471 AccessibilityInteractionClient.getConnection(mConnectionId); 3472 if (connection == null) { 3473 return; 3474 } 3475 try { 3476 connection.attachAccessibilityOverlayToDisplay(displayId, sc); 3477 } catch (RemoteException re) { 3478 re.rethrowFromSystemServer(); 3479 } 3480 } 3481 3482 /** 3483 * <p>Attaches an accessibility overlay {@link android.view.SurfaceControl} to the 3484 * specified 3485 * window. This method should be used when you want the overlay to move and 3486 * resize as the parent window moves and resizes.</p> 3487 * <p>Generally speaking, an accessibility overlay will be a {@link android.view.View}. 3488 * To embed the View into a {@link android.view.SurfaceControl}, create a 3489 * {@link android.view.SurfaceControlViewHost} and attach the View using 3490 * {@link android.view.SurfaceControlViewHost#setView}. Then obtain the SurfaceControl by 3491 * calling <code> viewHost.getSurfacePackage().getSurfaceControl()</code>.</p> 3492 * <p>To remove this overlay and free the associated resources, use 3493 * <code> new SurfaceControl.Transaction().reparent(sc, null).apply();</code>.</p> 3494 * <p>If the specified overlay has already been attached to the specified window 3495 * this method does nothing. 3496 * If the specified overlay has already been attached to a previous window this 3497 * function will transfer the overlay to the new window. 3498 * Services can attach multiple overlays. Use 3499 * <code> new SurfaceControl.Transaction().setLayer(sc, layer).apply();</code>. 3500 * to coordinate the order of the overlays on screen.</p> 3501 * 3502 * @param accessibilityWindowId The window id, from 3503 * {@link AccessibilityWindowInfo#getId()}. 3504 * @param sc the SurfaceControl containing the overlay 3505 * content 3506 */ 3507 public void attachAccessibilityOverlayToWindow( 3508 int accessibilityWindowId, @NonNull SurfaceControl sc) { 3509 Preconditions.checkNotNull(sc, "SurfaceControl cannot be null"); 3510 AccessibilityInteractionClient.getInstance(this) 3511 .attachAccessibilityOverlayToWindow(mConnectionId, accessibilityWindowId, sc); 3512 } 3513 } 3514