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 android.accessibilityservice.GestureDescription.MotionEventGenerator; 20 import android.annotation.IntDef; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.RequiresPermission; 24 import android.app.Service; 25 import android.compat.annotation.UnsupportedAppUsage; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.pm.ParceledListSlice; 29 import android.graphics.Region; 30 import android.os.Build; 31 import android.os.Handler; 32 import android.os.IBinder; 33 import android.os.Looper; 34 import android.os.Message; 35 import android.os.RemoteException; 36 import android.util.ArrayMap; 37 import android.util.Log; 38 import android.util.Slog; 39 import android.util.SparseArray; 40 import android.view.Display; 41 import android.view.KeyEvent; 42 import android.view.WindowManager; 43 import android.view.WindowManagerImpl; 44 import android.view.accessibility.AccessibilityEvent; 45 import android.view.accessibility.AccessibilityInteractionClient; 46 import android.view.accessibility.AccessibilityNodeInfo; 47 import android.view.accessibility.AccessibilityWindowInfo; 48 49 import com.android.internal.os.HandlerCaller; 50 import com.android.internal.os.SomeArgs; 51 52 import java.lang.annotation.Retention; 53 import java.lang.annotation.RetentionPolicy; 54 import java.util.List; 55 56 /** 57 * Accessibility services should only be used to assist users with disabilities in using 58 * Android devices and apps. They run in the background and receive callbacks by the system 59 * when {@link AccessibilityEvent}s are fired. Such events denote some state transition 60 * in the user interface, for example, the focus has changed, a button has been clicked, 61 * etc. Such a service can optionally request the capability for querying the content 62 * of the active window. Development of an accessibility service requires extending this 63 * class and implementing its abstract methods. 64 * 65 * <div class="special reference"> 66 * <h3>Developer Guides</h3> 67 * <p>For more information about creating AccessibilityServices, read the 68 * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a> 69 * developer guide.</p> 70 * </div> 71 * 72 * <h3>Lifecycle</h3> 73 * <p> 74 * The lifecycle of an accessibility service is managed exclusively by the system and 75 * follows the established service life cycle. Starting an accessibility service is triggered 76 * exclusively by the user explicitly turning the service on in device settings. After the system 77 * binds to a service, it calls {@link AccessibilityService#onServiceConnected()}. This method can 78 * be overridden by clients that want to perform post binding setup. 79 * </p> 80 * <p> 81 * An accessibility service stops either when the user turns it off in device settings or when 82 * it calls {@link AccessibilityService#disableSelf()}. 83 * </p> 84 * <h3>Declaration</h3> 85 * <p> 86 * An accessibility is declared as any other service in an AndroidManifest.xml, but it 87 * must do two things: 88 * <ul> 89 * <ol> 90 * Specify that it handles the "android.accessibilityservice.AccessibilityService" 91 * {@link android.content.Intent}. 92 * </ol> 93 * <ol> 94 * Request the {@link android.Manifest.permission#BIND_ACCESSIBILITY_SERVICE} permission to 95 * ensure that only the system can bind to it. 96 * </ol> 97 * </ul> 98 * If either of these items is missing, the system will ignore the accessibility service. 99 * Following is an example declaration: 100 * </p> 101 * <pre> <service android:name=".MyAccessibilityService" 102 * android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"> 103 * <intent-filter> 104 * <action android:name="android.accessibilityservice.AccessibilityService" /> 105 * </intent-filter> 106 * . . . 107 * </service></pre> 108 * <h3>Configuration</h3> 109 * <p> 110 * An accessibility service can be configured to receive specific types of accessibility events, 111 * listen only to specific packages, get events from each type only once in a given time frame, 112 * retrieve window content, specify a settings activity, etc. 113 * </p> 114 * <p> 115 * There are two approaches for configuring an accessibility service: 116 * </p> 117 * <ul> 118 * <li> 119 * Providing a {@link #SERVICE_META_DATA meta-data} entry in the manifest when declaring 120 * the service. A service declaration with a meta-data tag is presented below: 121 * <pre> <service android:name=".MyAccessibilityService"> 122 * <intent-filter> 123 * <action android:name="android.accessibilityservice.AccessibilityService" /> 124 * </intent-filter> 125 * <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibilityservice" /> 126 * </service></pre> 127 * <p class="note"> 128 * <strong>Note:</strong> This approach enables setting all properties. 129 * </p> 130 * <p> 131 * For more details refer to {@link #SERVICE_META_DATA} and 132 * <code><{@link android.R.styleable#AccessibilityService accessibility-service}></code>. 133 * </p> 134 * </li> 135 * <li> 136 * Calling {@link AccessibilityService#setServiceInfo(AccessibilityServiceInfo)}. Note 137 * that this method can be called any time to dynamically change the service configuration. 138 * <p class="note"> 139 * <strong>Note:</strong> This approach enables setting only dynamically configurable properties: 140 * {@link AccessibilityServiceInfo#eventTypes}, 141 * {@link AccessibilityServiceInfo#feedbackType}, 142 * {@link AccessibilityServiceInfo#flags}, 143 * {@link AccessibilityServiceInfo#notificationTimeout}, 144 * {@link AccessibilityServiceInfo#packageNames} 145 * </p> 146 * <p> 147 * For more details refer to {@link AccessibilityServiceInfo}. 148 * </p> 149 * </li> 150 * </ul> 151 * <h3>Retrieving window content</h3> 152 * <p> 153 * A service can specify in its declaration that it can retrieve window 154 * content which is represented as a tree of {@link AccessibilityWindowInfo} and 155 * {@link AccessibilityNodeInfo} objects. Note that 156 * declaring this capability requires that the service declares its configuration via 157 * an XML resource referenced by {@link #SERVICE_META_DATA}. 158 * </p> 159 * <p> 160 * Window content may be retrieved with 161 * {@link AccessibilityEvent#getSource() AccessibilityEvent.getSource()}, 162 * {@link AccessibilityService#findFocus(int)}, 163 * {@link AccessibilityService#getWindows()}, or 164 * {@link AccessibilityService#getRootInActiveWindow()}. 165 * </p> 166 * <p class="note"> 167 * <strong>Note</strong> An accessibility service may have requested to be notified for 168 * a subset of the event types, and thus be unaware when the node hierarchy has changed. It is also 169 * possible for a node to contain outdated information because the window content may change at any 170 * time. 171 * </p> 172 * <h3>Notification strategy</h3> 173 * <p> 174 * All accessibility services are notified of all events they have requested, regardless of their 175 * feedback type. 176 * </p> 177 * <p class="note"> 178 * <strong>Note:</strong> The event notification timeout is useful to avoid propagating 179 * events to the client too frequently since this is accomplished via an expensive 180 * interprocess call. One can think of the timeout as a criteria to determine when 181 * event generation has settled down.</p> 182 * <h3>Event types</h3> 183 * <ul> 184 * <li>{@link AccessibilityEvent#TYPE_VIEW_CLICKED}</li> 185 * <li>{@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED}</li> 186 * <li>{@link AccessibilityEvent#TYPE_VIEW_FOCUSED}</li> 187 * <li>{@link AccessibilityEvent#TYPE_VIEW_SELECTED}</li> 188 * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED}</li> 189 * <li>{@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}</li> 190 * <li>{@link AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED}</li> 191 * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START}</li> 192 * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END}</li> 193 * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER}</li> 194 * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}</li> 195 * <li>{@link AccessibilityEvent#TYPE_VIEW_SCROLLED}</li> 196 * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED}</li> 197 * <li>{@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED}</li> 198 * <li>{@link AccessibilityEvent#TYPE_ANNOUNCEMENT}</li> 199 * <li>{@link AccessibilityEvent#TYPE_GESTURE_DETECTION_START}</li> 200 * <li>{@link AccessibilityEvent#TYPE_GESTURE_DETECTION_END}</li> 201 * <li>{@link AccessibilityEvent#TYPE_TOUCH_INTERACTION_START}</li> 202 * <li>{@link AccessibilityEvent#TYPE_TOUCH_INTERACTION_END}</li> 203 * <li>{@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUSED}</li> 204 * <li>{@link AccessibilityEvent#TYPE_WINDOWS_CHANGED}</li> 205 * <li>{@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED}</li> 206 * </ul> 207 * <h3>Feedback types</h3> 208 * <ul> 209 * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}</li> 210 * <li>{@link AccessibilityServiceInfo#FEEDBACK_HAPTIC}</li> 211 * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}</li> 212 * <li>{@link AccessibilityServiceInfo#FEEDBACK_VISUAL}</li> 213 * <li>{@link AccessibilityServiceInfo#FEEDBACK_GENERIC}</li> 214 * <li>{@link AccessibilityServiceInfo#FEEDBACK_BRAILLE}</li> 215 * </ul> 216 * @see AccessibilityEvent 217 * @see AccessibilityServiceInfo 218 * @see android.view.accessibility.AccessibilityManager 219 */ 220 public abstract class AccessibilityService extends Service { 221 222 /** 223 * The user has performed a swipe up gesture on the touch screen. 224 */ 225 public static final int GESTURE_SWIPE_UP = 1; 226 227 /** 228 * The user has performed a swipe down gesture on the touch screen. 229 */ 230 public static final int GESTURE_SWIPE_DOWN = 2; 231 232 /** 233 * The user has performed a swipe left gesture on the touch screen. 234 */ 235 public static final int GESTURE_SWIPE_LEFT = 3; 236 237 /** 238 * The user has performed a swipe right gesture on the touch screen. 239 */ 240 public static final int GESTURE_SWIPE_RIGHT = 4; 241 242 /** 243 * The user has performed a swipe left and right gesture on the touch screen. 244 */ 245 public static final int GESTURE_SWIPE_LEFT_AND_RIGHT = 5; 246 247 /** 248 * The user has performed a swipe right and left gesture on the touch screen. 249 */ 250 public static final int GESTURE_SWIPE_RIGHT_AND_LEFT = 6; 251 252 /** 253 * The user has performed a swipe up and down gesture on the touch screen. 254 */ 255 public static final int GESTURE_SWIPE_UP_AND_DOWN = 7; 256 257 /** 258 * The user has performed a swipe down and up gesture on the touch screen. 259 */ 260 public static final int GESTURE_SWIPE_DOWN_AND_UP = 8; 261 262 /** 263 * The user has performed a left and up gesture on the touch screen. 264 */ 265 public static final int GESTURE_SWIPE_LEFT_AND_UP = 9; 266 267 /** 268 * The user has performed a left and down gesture on the touch screen. 269 */ 270 public static final int GESTURE_SWIPE_LEFT_AND_DOWN = 10; 271 272 /** 273 * The user has performed a right and up gesture on the touch screen. 274 */ 275 public static final int GESTURE_SWIPE_RIGHT_AND_UP = 11; 276 277 /** 278 * The user has performed a right and down gesture on the touch screen. 279 */ 280 public static final int GESTURE_SWIPE_RIGHT_AND_DOWN = 12; 281 282 /** 283 * The user has performed an up and left gesture on the touch screen. 284 */ 285 public static final int GESTURE_SWIPE_UP_AND_LEFT = 13; 286 287 /** 288 * The user has performed an up and right gesture on the touch screen. 289 */ 290 public static final int GESTURE_SWIPE_UP_AND_RIGHT = 14; 291 292 /** 293 * The user has performed an down and left gesture on the touch screen. 294 */ 295 public static final int GESTURE_SWIPE_DOWN_AND_LEFT = 15; 296 297 /** 298 * The user has performed an down and right gesture on the touch screen. 299 */ 300 public static final int GESTURE_SWIPE_DOWN_AND_RIGHT = 16; 301 302 /** 303 * The {@link Intent} that must be declared as handled by the service. 304 */ 305 public static final String SERVICE_INTERFACE = 306 "android.accessibilityservice.AccessibilityService"; 307 308 /** 309 * Name under which an AccessibilityService component publishes information 310 * about itself. This meta-data must reference an XML resource containing an 311 * <code><{@link android.R.styleable#AccessibilityService accessibility-service}></code> 312 * tag. This is a sample XML file configuring an accessibility service: 313 * <pre> <accessibility-service 314 * android:accessibilityEventTypes="typeViewClicked|typeViewFocused" 315 * android:packageNames="foo.bar, foo.baz" 316 * android:accessibilityFeedbackType="feedbackSpoken" 317 * android:notificationTimeout="100" 318 * android:accessibilityFlags="flagDefault" 319 * android:settingsActivity="foo.bar.TestBackActivity" 320 * android:canRetrieveWindowContent="true" 321 * android:canRequestTouchExplorationMode="true" 322 * . . . 323 * /></pre> 324 */ 325 public static final String SERVICE_META_DATA = "android.accessibilityservice"; 326 327 /** 328 * Action to go back. 329 */ 330 public static final int GLOBAL_ACTION_BACK = 1; 331 332 /** 333 * Action to go home. 334 */ 335 public static final int GLOBAL_ACTION_HOME = 2; 336 337 /** 338 * Action to toggle showing the overview of recent apps. Will fail on platforms that don't 339 * show recent apps. 340 */ 341 public static final int GLOBAL_ACTION_RECENTS = 3; 342 343 /** 344 * Action to open the notifications. 345 */ 346 public static final int GLOBAL_ACTION_NOTIFICATIONS = 4; 347 348 /** 349 * Action to open the quick settings. 350 */ 351 public static final int GLOBAL_ACTION_QUICK_SETTINGS = 5; 352 353 /** 354 * Action to open the power long-press dialog. 355 */ 356 public static final int GLOBAL_ACTION_POWER_DIALOG = 6; 357 358 /** 359 * Action to toggle docking the current app's window 360 */ 361 public static final int GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7; 362 363 /** 364 * Action to lock the screen 365 */ 366 public static final int GLOBAL_ACTION_LOCK_SCREEN = 8; 367 368 /** 369 * Action to take a screenshot 370 */ 371 public static final int GLOBAL_ACTION_TAKE_SCREENSHOT = 9; 372 373 private static final String LOG_TAG = "AccessibilityService"; 374 375 /** 376 * Interface used by IAccessibilityServiceWrapper to call the service from its main thread. 377 * @hide 378 */ 379 public interface Callbacks { onAccessibilityEvent(AccessibilityEvent event)380 void onAccessibilityEvent(AccessibilityEvent event); onInterrupt()381 void onInterrupt(); onServiceConnected()382 void onServiceConnected(); init(int connectionId, IBinder windowToken)383 void init(int connectionId, IBinder windowToken); onGesture(int gestureId)384 boolean onGesture(int gestureId); onKeyEvent(KeyEvent event)385 boolean onKeyEvent(KeyEvent event); 386 /** Magnification changed callbacks for different displays */ onMagnificationChanged(int displayId, @NonNull Region region, float scale, float centerX, float centerY)387 void onMagnificationChanged(int displayId, @NonNull Region region, 388 float scale, float centerX, float centerY); onSoftKeyboardShowModeChanged(int showMode)389 void onSoftKeyboardShowModeChanged(int showMode); onPerformGestureResult(int sequence, boolean completedSuccessfully)390 void onPerformGestureResult(int sequence, boolean completedSuccessfully); onFingerprintCapturingGesturesChanged(boolean active)391 void onFingerprintCapturingGesturesChanged(boolean active); onFingerprintGesture(int gesture)392 void onFingerprintGesture(int gesture); onAccessibilityButtonClicked()393 void onAccessibilityButtonClicked(); onAccessibilityButtonAvailabilityChanged(boolean available)394 void onAccessibilityButtonAvailabilityChanged(boolean available); 395 } 396 397 /** 398 * Annotations for Soft Keyboard show modes so tools can catch invalid show modes. 399 * @hide 400 */ 401 @Retention(RetentionPolicy.SOURCE) 402 @IntDef(prefix = { "SHOW_MODE_" }, value = { 403 SHOW_MODE_AUTO, 404 SHOW_MODE_HIDDEN, 405 SHOW_MODE_IGNORE_HARD_KEYBOARD 406 }) 407 public @interface SoftKeyboardShowMode {} 408 409 /** 410 * Allow the system to control when the soft keyboard is shown. 411 * @see SoftKeyboardController 412 */ 413 public static final int SHOW_MODE_AUTO = 0; 414 415 /** 416 * Never show the soft keyboard. 417 * @see SoftKeyboardController 418 */ 419 public static final int SHOW_MODE_HIDDEN = 1; 420 421 /** 422 * Allow the soft keyboard to be shown, even if a hard keyboard is connected 423 * @see SoftKeyboardController 424 */ 425 public static final int SHOW_MODE_IGNORE_HARD_KEYBOARD = 2; 426 427 /** 428 * Mask used to cover the show modes supported in public API 429 * @hide 430 */ 431 public static final int SHOW_MODE_MASK = 0x03; 432 433 /** 434 * Bit used to hold the old value of the hard IME setting to restore when a service is shut 435 * down. 436 * @hide 437 */ 438 public static final int SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE = 0x20000000; 439 440 /** 441 * Bit for show mode setting to indicate that the user has overridden the hard keyboard 442 * behavior. 443 * @hide 444 */ 445 public static final int SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN = 0x40000000; 446 447 private int mConnectionId = AccessibilityInteractionClient.NO_ID; 448 449 @UnsupportedAppUsage 450 private AccessibilityServiceInfo mInfo; 451 452 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 453 private IBinder mWindowToken; 454 455 private WindowManager mWindowManager; 456 457 /** List of magnification controllers, mapping from displayId -> MagnificationController. */ 458 private final SparseArray<MagnificationController> mMagnificationControllers = 459 new SparseArray<>(0); 460 private SoftKeyboardController mSoftKeyboardController; 461 private AccessibilityButtonController mAccessibilityButtonController; 462 463 private int mGestureStatusCallbackSequence; 464 465 private SparseArray<GestureResultCallbackInfo> mGestureStatusCallbackInfos; 466 467 private final Object mLock = new Object(); 468 469 private FingerprintGestureController mFingerprintGestureController; 470 471 /** 472 * Callback for {@link android.view.accessibility.AccessibilityEvent}s. 473 * 474 * @param event The new event. This event is owned by the caller and cannot be used after 475 * this method returns. Services wishing to use the event after this method returns should 476 * make a copy. 477 */ onAccessibilityEvent(AccessibilityEvent event)478 public abstract void onAccessibilityEvent(AccessibilityEvent event); 479 480 /** 481 * Callback for interrupting the accessibility feedback. 482 */ onInterrupt()483 public abstract void onInterrupt(); 484 485 /** 486 * Dispatches service connection to internal components first, then the 487 * client code. 488 */ dispatchServiceConnected()489 private void dispatchServiceConnected() { 490 synchronized (mLock) { 491 for (int i = 0; i < mMagnificationControllers.size(); i++) { 492 mMagnificationControllers.valueAt(i).onServiceConnectedLocked(); 493 } 494 } 495 if (mSoftKeyboardController != null) { 496 mSoftKeyboardController.onServiceConnected(); 497 } 498 499 // The client gets to handle service connection last, after we've set 500 // up any state upon which their code may rely. 501 onServiceConnected(); 502 } 503 504 /** 505 * This method is a part of the {@link AccessibilityService} lifecycle and is 506 * called after the system has successfully bound to the service. If is 507 * convenient to use this method for setting the {@link AccessibilityServiceInfo}. 508 * 509 * @see AccessibilityServiceInfo 510 * @see #setServiceInfo(AccessibilityServiceInfo) 511 */ onServiceConnected()512 protected void onServiceConnected() { 513 514 } 515 516 /** 517 * Called by the system when the user performs a specific gesture on the 518 * touch screen. 519 * 520 * <strong>Note:</strong> To receive gestures an accessibility service must 521 * request that the device is in touch exploration mode by setting the 522 * {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} 523 * flag. 524 * 525 * @param gestureId The unique id of the performed gesture. 526 * 527 * @return Whether the gesture was handled. 528 * 529 * @see #GESTURE_SWIPE_UP 530 * @see #GESTURE_SWIPE_UP_AND_LEFT 531 * @see #GESTURE_SWIPE_UP_AND_DOWN 532 * @see #GESTURE_SWIPE_UP_AND_RIGHT 533 * @see #GESTURE_SWIPE_DOWN 534 * @see #GESTURE_SWIPE_DOWN_AND_LEFT 535 * @see #GESTURE_SWIPE_DOWN_AND_UP 536 * @see #GESTURE_SWIPE_DOWN_AND_RIGHT 537 * @see #GESTURE_SWIPE_LEFT 538 * @see #GESTURE_SWIPE_LEFT_AND_UP 539 * @see #GESTURE_SWIPE_LEFT_AND_RIGHT 540 * @see #GESTURE_SWIPE_LEFT_AND_DOWN 541 * @see #GESTURE_SWIPE_RIGHT 542 * @see #GESTURE_SWIPE_RIGHT_AND_UP 543 * @see #GESTURE_SWIPE_RIGHT_AND_LEFT 544 * @see #GESTURE_SWIPE_RIGHT_AND_DOWN 545 */ onGesture(int gestureId)546 protected boolean onGesture(int gestureId) { 547 return false; 548 } 549 550 /** 551 * Callback that allows an accessibility service to observe the key events 552 * before they are passed to the rest of the system. This means that the events 553 * are first delivered here before they are passed to the device policy, the 554 * input method, or applications. 555 * <p> 556 * <strong>Note:</strong> It is important that key events are handled in such 557 * a way that the event stream that would be passed to the rest of the system 558 * is well-formed. For example, handling the down event but not the up event 559 * and vice versa would generate an inconsistent event stream. 560 * </p> 561 * <p> 562 * <strong>Note:</strong> The key events delivered in this method are copies 563 * and modifying them will have no effect on the events that will be passed 564 * to the system. This method is intended to perform purely filtering 565 * functionality. 566 * <p> 567 * 568 * @param event The event to be processed. This event is owned by the caller and cannot be used 569 * after this method returns. Services wishing to use the event after this method returns should 570 * make a copy. 571 * @return If true then the event will be consumed and not delivered to 572 * applications, otherwise it will be delivered as usual. 573 */ onKeyEvent(KeyEvent event)574 protected boolean onKeyEvent(KeyEvent event) { 575 return false; 576 } 577 578 /** 579 * Gets the windows on the screen. This method returns only the windows 580 * that a sighted user can interact with, as opposed to all windows. 581 * For example, if there is a modal dialog shown and the user cannot touch 582 * anything behind it, then only the modal window will be reported 583 * (assuming it is the top one). For convenience the returned windows 584 * are ordered in a descending layer order, which is the windows that 585 * are on top are reported first. Since the user can always 586 * interact with the window that has input focus by typing, the focused 587 * window is always returned (even if covered by a modal window). 588 * <p> 589 * <strong>Note:</strong> In order to access the windows your service has 590 * to declare the capability to retrieve window content by setting the 591 * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent} 592 * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}. 593 * Also the service has to opt-in to retrieve the interactive windows by 594 * setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} 595 * flag. 596 * </p> 597 * 598 * @return The windows if there are windows and the service is can retrieve 599 * them, otherwise an empty list. 600 */ getWindows()601 public List<AccessibilityWindowInfo> getWindows() { 602 return AccessibilityInteractionClient.getInstance().getWindows(mConnectionId); 603 } 604 605 /** 606 * Gets the root node in the currently active window if this service 607 * can retrieve window content. The active window is the one that the user 608 * is currently touching or the window with input focus, if the user is not 609 * touching any window. 610 * <p> 611 * The currently active window is defined as the window that most recently fired one 612 * of the following events: 613 * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}, 614 * {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER}, 615 * {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}. 616 * In other words, the last window shown that also has input focus. 617 * </p> 618 * <p> 619 * <strong>Note:</strong> In order to access the root node your service has 620 * to declare the capability to retrieve window content by setting the 621 * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent} 622 * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}. 623 * </p> 624 * 625 * @return The root node if this service can retrieve window content. 626 */ getRootInActiveWindow()627 public AccessibilityNodeInfo getRootInActiveWindow() { 628 return AccessibilityInteractionClient.getInstance().getRootInActiveWindow(mConnectionId); 629 } 630 631 /** 632 * Disables the service. After calling this method, the service will be disabled and settings 633 * will show that it is turned off. 634 */ disableSelf()635 public final void disableSelf() { 636 final IAccessibilityServiceConnection connection = 637 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); 638 if (connection != null) { 639 try { 640 connection.disableSelf(); 641 } catch (RemoteException re) { 642 throw new RuntimeException(re); 643 } 644 } 645 } 646 647 /** 648 * Returns the magnification controller, which may be used to query and 649 * modify the state of display magnification. 650 * <p> 651 * <strong>Note:</strong> In order to control magnification, your service 652 * must declare the capability by setting the 653 * {@link android.R.styleable#AccessibilityService_canControlMagnification} 654 * property in its meta-data. For more information, see 655 * {@link #SERVICE_META_DATA}. 656 * 657 * @return the magnification controller 658 */ 659 @NonNull getMagnificationController()660 public final MagnificationController getMagnificationController() { 661 return getMagnificationController(Display.DEFAULT_DISPLAY); 662 } 663 664 /** 665 * Returns the magnification controller of specified logical display, which may be used to 666 * query and modify the state of display magnification. 667 * <p> 668 * <strong>Note:</strong> In order to control magnification, your service 669 * must declare the capability by setting the 670 * {@link android.R.styleable#AccessibilityService_canControlMagnification} 671 * property in its meta-data. For more information, see 672 * {@link #SERVICE_META_DATA}. 673 * 674 * @param displayId The logic display id, use {@link Display#DEFAULT_DISPLAY} for 675 * default display. 676 * @return the magnification controller 677 * 678 * @hide 679 */ 680 @NonNull getMagnificationController(int displayId)681 public final MagnificationController getMagnificationController(int displayId) { 682 synchronized (mLock) { 683 MagnificationController controller = mMagnificationControllers.get(displayId); 684 if (controller == null) { 685 controller = new MagnificationController(this, mLock, displayId); 686 mMagnificationControllers.put(displayId, controller); 687 } 688 return controller; 689 } 690 } 691 692 /** 693 * Get the controller for fingerprint gestures. This feature requires {@link 694 * AccessibilityServiceInfo#CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES}. 695 * 696 *<strong>Note: </strong> The service must be connected before this method is called. 697 * 698 * @return The controller for fingerprint gestures, or {@code null} if gestures are unavailable. 699 */ 700 @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT) getFingerprintGestureController()701 public final @NonNull FingerprintGestureController getFingerprintGestureController() { 702 if (mFingerprintGestureController == null) { 703 mFingerprintGestureController = new FingerprintGestureController( 704 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId)); 705 } 706 return mFingerprintGestureController; 707 } 708 709 /** 710 * Dispatch a gesture to the touch screen. Any gestures currently in progress, whether from 711 * the user, this service, or another service, will be cancelled. 712 * <p> 713 * The gesture will be dispatched as if it were performed directly on the screen by a user, so 714 * the events may be affected by features such as magnification and explore by touch. 715 * </p> 716 * <p> 717 * <strong>Note:</strong> In order to dispatch gestures, your service 718 * must declare the capability by setting the 719 * {@link android.R.styleable#AccessibilityService_canPerformGestures} 720 * property in its meta-data. For more information, see 721 * {@link #SERVICE_META_DATA}. 722 * </p> 723 * 724 * @param gesture The gesture to dispatch 725 * @param callback The object to call back when the status of the gesture is known. If 726 * {@code null}, no status is reported. 727 * @param handler The handler on which to call back the {@code callback} object. If 728 * {@code null}, the object is called back on the service's main thread. 729 * 730 * @return {@code true} if the gesture is dispatched, {@code false} if not. 731 */ dispatchGesture(@onNull GestureDescription gesture, @Nullable GestureResultCallback callback, @Nullable Handler handler)732 public final boolean dispatchGesture(@NonNull GestureDescription gesture, 733 @Nullable GestureResultCallback callback, 734 @Nullable Handler handler) { 735 final IAccessibilityServiceConnection connection = 736 AccessibilityInteractionClient.getInstance().getConnection( 737 mConnectionId); 738 if (connection == null) { 739 return false; 740 } 741 List<GestureDescription.GestureStep> steps = 742 MotionEventGenerator.getGestureStepsFromGestureDescription(gesture, 100); 743 try { 744 synchronized (mLock) { 745 mGestureStatusCallbackSequence++; 746 if (callback != null) { 747 if (mGestureStatusCallbackInfos == null) { 748 mGestureStatusCallbackInfos = new SparseArray<>(); 749 } 750 GestureResultCallbackInfo callbackInfo = new GestureResultCallbackInfo(gesture, 751 callback, handler); 752 mGestureStatusCallbackInfos.put(mGestureStatusCallbackSequence, callbackInfo); 753 } 754 connection.sendGesture(mGestureStatusCallbackSequence, 755 new ParceledListSlice<>(steps)); 756 } 757 } catch (RemoteException re) { 758 throw new RuntimeException(re); 759 } 760 return true; 761 } 762 onPerformGestureResult(int sequence, final boolean completedSuccessfully)763 void onPerformGestureResult(int sequence, final boolean completedSuccessfully) { 764 if (mGestureStatusCallbackInfos == null) { 765 return; 766 } 767 GestureResultCallbackInfo callbackInfo; 768 synchronized (mLock) { 769 callbackInfo = mGestureStatusCallbackInfos.get(sequence); 770 } 771 final GestureResultCallbackInfo finalCallbackInfo = callbackInfo; 772 if ((callbackInfo != null) && (callbackInfo.gestureDescription != null) 773 && (callbackInfo.callback != null)) { 774 if (callbackInfo.handler != null) { 775 callbackInfo.handler.post(new Runnable() { 776 @Override 777 public void run() { 778 if (completedSuccessfully) { 779 finalCallbackInfo.callback 780 .onCompleted(finalCallbackInfo.gestureDescription); 781 } else { 782 finalCallbackInfo.callback 783 .onCancelled(finalCallbackInfo.gestureDescription); 784 } 785 } 786 }); 787 return; 788 } 789 if (completedSuccessfully) { 790 callbackInfo.callback.onCompleted(callbackInfo.gestureDescription); 791 } else { 792 callbackInfo.callback.onCancelled(callbackInfo.gestureDescription); 793 } 794 } 795 } 796 onMagnificationChanged(int displayId, @NonNull Region region, float scale, float centerX, float centerY)797 private void onMagnificationChanged(int displayId, @NonNull Region region, float scale, 798 float centerX, float centerY) { 799 MagnificationController controller; 800 synchronized (mLock) { 801 controller = mMagnificationControllers.get(displayId); 802 } 803 if (controller != null) { 804 controller.dispatchMagnificationChanged(region, scale, centerX, centerY); 805 } 806 } 807 808 /** 809 * Callback for fingerprint gesture handling 810 * @param active If gesture detection is active 811 */ onFingerprintCapturingGesturesChanged(boolean active)812 private void onFingerprintCapturingGesturesChanged(boolean active) { 813 getFingerprintGestureController().onGestureDetectionActiveChanged(active); 814 } 815 816 /** 817 * Callback for fingerprint gesture handling 818 * @param gesture The identifier for the gesture performed 819 */ onFingerprintGesture(int gesture)820 private void onFingerprintGesture(int gesture) { 821 getFingerprintGestureController().onGesture(gesture); 822 } 823 824 /** 825 * Used to control and query the state of display magnification. 826 */ 827 public static final class MagnificationController { 828 private final AccessibilityService mService; 829 private final int mDisplayId; 830 831 /** 832 * Map of listeners to their handlers. Lazily created when adding the 833 * first magnification listener. 834 */ 835 private ArrayMap<OnMagnificationChangedListener, Handler> mListeners; 836 private final Object mLock; 837 MagnificationController(@onNull AccessibilityService service, @NonNull Object lock, int displayId)838 MagnificationController(@NonNull AccessibilityService service, @NonNull Object lock, 839 int displayId) { 840 mService = service; 841 mLock = lock; 842 mDisplayId = displayId; 843 } 844 845 /** 846 * Called when the service is connected. 847 */ onServiceConnectedLocked()848 void onServiceConnectedLocked() { 849 if (mListeners != null && !mListeners.isEmpty()) { 850 setMagnificationCallbackEnabled(true); 851 } 852 } 853 854 /** 855 * Adds the specified change listener to the list of magnification 856 * change listeners. The callback will occur on the service's main 857 * thread. 858 * 859 * @param listener the listener to add, must be non-{@code null} 860 */ addListener(@onNull OnMagnificationChangedListener listener)861 public void addListener(@NonNull OnMagnificationChangedListener listener) { 862 addListener(listener, null); 863 } 864 865 /** 866 * Adds the specified change listener to the list of magnification 867 * change listeners. The callback will occur on the specified 868 * {@link Handler}'s thread, or on the service's main thread if the 869 * handler is {@code null}. 870 * 871 * @param listener the listener to add, must be non-null 872 * @param handler the handler on which the callback should execute, or 873 * {@code null} to execute on the service's main thread 874 */ addListener(@onNull OnMagnificationChangedListener listener, @Nullable Handler handler)875 public void addListener(@NonNull OnMagnificationChangedListener listener, 876 @Nullable Handler handler) { 877 synchronized (mLock) { 878 if (mListeners == null) { 879 mListeners = new ArrayMap<>(); 880 } 881 882 final boolean shouldEnableCallback = mListeners.isEmpty(); 883 mListeners.put(listener, handler); 884 885 if (shouldEnableCallback) { 886 // This may fail if the service is not connected yet, but if we 887 // still have listeners when it connects then we can try again. 888 setMagnificationCallbackEnabled(true); 889 } 890 } 891 } 892 893 /** 894 * Removes the specified change listener from the list of magnification change listeners. 895 * 896 * @param listener the listener to remove, must be non-null 897 * @return {@code true} if the listener was removed, {@code false} otherwise 898 */ removeListener(@onNull OnMagnificationChangedListener listener)899 public boolean removeListener(@NonNull OnMagnificationChangedListener listener) { 900 if (mListeners == null) { 901 return false; 902 } 903 904 synchronized (mLock) { 905 final int keyIndex = mListeners.indexOfKey(listener); 906 final boolean hasKey = keyIndex >= 0; 907 if (hasKey) { 908 mListeners.removeAt(keyIndex); 909 } 910 911 if (hasKey && mListeners.isEmpty()) { 912 // We just removed the last listener, so we don't need 913 // callbacks from the service anymore. 914 setMagnificationCallbackEnabled(false); 915 } 916 917 return hasKey; 918 } 919 } 920 setMagnificationCallbackEnabled(boolean enabled)921 private void setMagnificationCallbackEnabled(boolean enabled) { 922 final IAccessibilityServiceConnection connection = 923 AccessibilityInteractionClient.getInstance().getConnection( 924 mService.mConnectionId); 925 if (connection != null) { 926 try { 927 connection.setMagnificationCallbackEnabled(mDisplayId, enabled); 928 } catch (RemoteException re) { 929 throw new RuntimeException(re); 930 } 931 } 932 } 933 934 /** 935 * Dispatches magnification changes to any registered listeners. This 936 * should be called on the service's main thread. 937 */ dispatchMagnificationChanged(final @NonNull Region region, final float scale, final float centerX, final float centerY)938 void dispatchMagnificationChanged(final @NonNull Region region, final float scale, 939 final float centerX, final float centerY) { 940 final ArrayMap<OnMagnificationChangedListener, Handler> entries; 941 synchronized (mLock) { 942 if (mListeners == null || mListeners.isEmpty()) { 943 Slog.d(LOG_TAG, "Received magnification changed " 944 + "callback with no listeners registered!"); 945 setMagnificationCallbackEnabled(false); 946 return; 947 } 948 949 // Listeners may remove themselves. Perform a shallow copy to avoid concurrent 950 // modification. 951 entries = new ArrayMap<>(mListeners); 952 } 953 954 for (int i = 0, count = entries.size(); i < count; i++) { 955 final OnMagnificationChangedListener listener = entries.keyAt(i); 956 final Handler handler = entries.valueAt(i); 957 if (handler != null) { 958 handler.post(new Runnable() { 959 @Override 960 public void run() { 961 listener.onMagnificationChanged(MagnificationController.this, 962 region, scale, centerX, centerY); 963 } 964 }); 965 } else { 966 // We're already on the main thread, just run the listener. 967 listener.onMagnificationChanged(this, region, scale, centerX, centerY); 968 } 969 } 970 } 971 972 /** 973 * Returns the current magnification scale. 974 * <p> 975 * <strong>Note:</strong> If the service is not yet connected (e.g. 976 * {@link AccessibilityService#onServiceConnected()} has not yet been 977 * called) or the service has been disconnected, this method will 978 * return a default value of {@code 1.0f}. 979 * 980 * @return the current magnification scale 981 */ getScale()982 public float getScale() { 983 final IAccessibilityServiceConnection connection = 984 AccessibilityInteractionClient.getInstance().getConnection( 985 mService.mConnectionId); 986 if (connection != null) { 987 try { 988 return connection.getMagnificationScale(mDisplayId); 989 } catch (RemoteException re) { 990 Log.w(LOG_TAG, "Failed to obtain scale", re); 991 re.rethrowFromSystemServer(); 992 } 993 } 994 return 1.0f; 995 } 996 997 /** 998 * Returns the unscaled screen-relative X coordinate of the focal 999 * center of the magnified region. This is the point around which 1000 * zooming occurs and is guaranteed to lie within the magnified 1001 * region. 1002 * <p> 1003 * <strong>Note:</strong> If the service is not yet connected (e.g. 1004 * {@link AccessibilityService#onServiceConnected()} has not yet been 1005 * called) or the service has been disconnected, this method will 1006 * return a default value of {@code 0.0f}. 1007 * 1008 * @return the unscaled screen-relative X coordinate of the center of 1009 * the magnified region 1010 */ getCenterX()1011 public float getCenterX() { 1012 final IAccessibilityServiceConnection connection = 1013 AccessibilityInteractionClient.getInstance().getConnection( 1014 mService.mConnectionId); 1015 if (connection != null) { 1016 try { 1017 return connection.getMagnificationCenterX(mDisplayId); 1018 } catch (RemoteException re) { 1019 Log.w(LOG_TAG, "Failed to obtain center X", re); 1020 re.rethrowFromSystemServer(); 1021 } 1022 } 1023 return 0.0f; 1024 } 1025 1026 /** 1027 * Returns the unscaled screen-relative Y coordinate of the focal 1028 * center of the magnified region. This is the point around which 1029 * zooming occurs and is guaranteed to lie within the magnified 1030 * region. 1031 * <p> 1032 * <strong>Note:</strong> If the service is not yet connected (e.g. 1033 * {@link AccessibilityService#onServiceConnected()} has not yet been 1034 * called) or the service has been disconnected, this method will 1035 * return a default value of {@code 0.0f}. 1036 * 1037 * @return the unscaled screen-relative Y coordinate of the center of 1038 * the magnified region 1039 */ getCenterY()1040 public float getCenterY() { 1041 final IAccessibilityServiceConnection connection = 1042 AccessibilityInteractionClient.getInstance().getConnection( 1043 mService.mConnectionId); 1044 if (connection != null) { 1045 try { 1046 return connection.getMagnificationCenterY(mDisplayId); 1047 } catch (RemoteException re) { 1048 Log.w(LOG_TAG, "Failed to obtain center Y", re); 1049 re.rethrowFromSystemServer(); 1050 } 1051 } 1052 return 0.0f; 1053 } 1054 1055 /** 1056 * Returns the region of the screen currently active for magnification. Changes to 1057 * magnification scale and center only affect this portion of the screen. The rest of the 1058 * screen, for example input methods, cannot be magnified. This region is relative to the 1059 * unscaled screen and is independent of the scale and center point. 1060 * <p> 1061 * The returned region will be empty if magnification is not active. Magnification is active 1062 * if magnification gestures are enabled or if a service is running that can control 1063 * magnification. 1064 * <p> 1065 * <strong>Note:</strong> If the service is not yet connected (e.g. 1066 * {@link AccessibilityService#onServiceConnected()} has not yet been 1067 * called) or the service has been disconnected, this method will 1068 * return an empty region. 1069 * 1070 * @return the region of the screen currently active for magnification, or an empty region 1071 * if magnification is not active. 1072 */ 1073 @NonNull getMagnificationRegion()1074 public Region getMagnificationRegion() { 1075 final IAccessibilityServiceConnection connection = 1076 AccessibilityInteractionClient.getInstance().getConnection( 1077 mService.mConnectionId); 1078 if (connection != null) { 1079 try { 1080 return connection.getMagnificationRegion(mDisplayId); 1081 } catch (RemoteException re) { 1082 Log.w(LOG_TAG, "Failed to obtain magnified region", re); 1083 re.rethrowFromSystemServer(); 1084 } 1085 } 1086 return Region.obtain(); 1087 } 1088 1089 /** 1090 * Resets magnification scale and center to their default (e.g. no 1091 * magnification) values. 1092 * <p> 1093 * <strong>Note:</strong> If the service is not yet connected (e.g. 1094 * {@link AccessibilityService#onServiceConnected()} has not yet been 1095 * called) or the service has been disconnected, this method will have 1096 * no effect and return {@code false}. 1097 * 1098 * @param animate {@code true} to animate from the current scale and 1099 * center or {@code false} to reset the scale and center 1100 * immediately 1101 * @return {@code true} on success, {@code false} on failure 1102 */ reset(boolean animate)1103 public boolean reset(boolean animate) { 1104 final IAccessibilityServiceConnection connection = 1105 AccessibilityInteractionClient.getInstance().getConnection( 1106 mService.mConnectionId); 1107 if (connection != null) { 1108 try { 1109 return connection.resetMagnification(mDisplayId, animate); 1110 } catch (RemoteException re) { 1111 Log.w(LOG_TAG, "Failed to reset", re); 1112 re.rethrowFromSystemServer(); 1113 } 1114 } 1115 return false; 1116 } 1117 1118 /** 1119 * Sets the magnification scale. 1120 * <p> 1121 * <strong>Note:</strong> If the service is not yet connected (e.g. 1122 * {@link AccessibilityService#onServiceConnected()} has not yet been 1123 * called) or the service has been disconnected, this method will have 1124 * no effect and return {@code false}. 1125 * 1126 * @param scale the magnification scale to set, must be >= 1 and <= 8 1127 * @param animate {@code true} to animate from the current scale or 1128 * {@code false} to set the scale immediately 1129 * @return {@code true} on success, {@code false} on failure 1130 */ setScale(float scale, boolean animate)1131 public boolean setScale(float scale, boolean animate) { 1132 final IAccessibilityServiceConnection connection = 1133 AccessibilityInteractionClient.getInstance().getConnection( 1134 mService.mConnectionId); 1135 if (connection != null) { 1136 try { 1137 return connection.setMagnificationScaleAndCenter(mDisplayId, 1138 scale, Float.NaN, Float.NaN, animate); 1139 } catch (RemoteException re) { 1140 Log.w(LOG_TAG, "Failed to set scale", re); 1141 re.rethrowFromSystemServer(); 1142 } 1143 } 1144 return false; 1145 } 1146 1147 /** 1148 * Sets the center of the magnified viewport. 1149 * <p> 1150 * <strong>Note:</strong> If the service is not yet connected (e.g. 1151 * {@link AccessibilityService#onServiceConnected()} has not yet been 1152 * called) or the service has been disconnected, this method will have 1153 * no effect and return {@code false}. 1154 * 1155 * @param centerX the unscaled screen-relative X coordinate on which to 1156 * center the viewport 1157 * @param centerY the unscaled screen-relative Y coordinate on which to 1158 * center the viewport 1159 * @param animate {@code true} to animate from the current viewport 1160 * center or {@code false} to set the center immediately 1161 * @return {@code true} on success, {@code false} on failure 1162 */ setCenter(float centerX, float centerY, boolean animate)1163 public boolean setCenter(float centerX, float centerY, boolean animate) { 1164 final IAccessibilityServiceConnection connection = 1165 AccessibilityInteractionClient.getInstance().getConnection( 1166 mService.mConnectionId); 1167 if (connection != null) { 1168 try { 1169 return connection.setMagnificationScaleAndCenter(mDisplayId, 1170 Float.NaN, centerX, centerY, animate); 1171 } catch (RemoteException re) { 1172 Log.w(LOG_TAG, "Failed to set center", re); 1173 re.rethrowFromSystemServer(); 1174 } 1175 } 1176 return false; 1177 } 1178 1179 /** 1180 * Listener for changes in the state of magnification. 1181 */ 1182 public interface OnMagnificationChangedListener { 1183 /** 1184 * Called when the magnified region, scale, or center changes. 1185 * 1186 * @param controller the magnification controller 1187 * @param region the magnification region 1188 * @param scale the new scale 1189 * @param centerX the new X coordinate, in unscaled coordinates, around which 1190 * magnification is focused 1191 * @param centerY the new Y coordinate, in unscaled coordinates, around which 1192 * magnification is focused 1193 */ onMagnificationChanged(@onNull MagnificationController controller, @NonNull Region region, float scale, float centerX, float centerY)1194 void onMagnificationChanged(@NonNull MagnificationController controller, 1195 @NonNull Region region, float scale, float centerX, float centerY); 1196 } 1197 } 1198 1199 /** 1200 * Returns the soft keyboard controller, which may be used to query and modify the soft keyboard 1201 * show mode. 1202 * 1203 * @return the soft keyboard controller 1204 */ 1205 @NonNull getSoftKeyboardController()1206 public final SoftKeyboardController getSoftKeyboardController() { 1207 synchronized (mLock) { 1208 if (mSoftKeyboardController == null) { 1209 mSoftKeyboardController = new SoftKeyboardController(this, mLock); 1210 } 1211 return mSoftKeyboardController; 1212 } 1213 } 1214 onSoftKeyboardShowModeChanged(int showMode)1215 private void onSoftKeyboardShowModeChanged(int showMode) { 1216 if (mSoftKeyboardController != null) { 1217 mSoftKeyboardController.dispatchSoftKeyboardShowModeChanged(showMode); 1218 } 1219 } 1220 1221 /** 1222 * Used to control, query, and listen for changes to the soft keyboard show mode. 1223 * <p> 1224 * Accessibility services may request to override the decisions normally made about whether or 1225 * not the soft keyboard is shown. 1226 * <p> 1227 * If multiple services make conflicting requests, the last request is honored. A service may 1228 * register a listener to find out if the mode has changed under it. 1229 * <p> 1230 * If the user takes action to override the behavior behavior requested by an accessibility 1231 * service, the user's request takes precendence, the show mode will be reset to 1232 * {@link AccessibilityService#SHOW_MODE_AUTO}, and services will no longer be able to control 1233 * that aspect of the soft keyboard's behavior. 1234 * <p> 1235 * Note: Because soft keyboards are independent apps, the framework does not have total control 1236 * over their behavior. They may choose to show themselves, or not, without regard to requests 1237 * made here. So the framework will make a best effort to deliver the behavior requested, but 1238 * cannot guarantee success. 1239 * 1240 * @see AccessibilityService#SHOW_MODE_AUTO 1241 * @see AccessibilityService#SHOW_MODE_HIDDEN 1242 * @see AccessibilityService#SHOW_MODE_IGNORE_HARD_KEYBOARD 1243 */ 1244 public static final class SoftKeyboardController { 1245 private final AccessibilityService mService; 1246 1247 /** 1248 * Map of listeners to their handlers. Lazily created when adding the first 1249 * soft keyboard change listener. 1250 */ 1251 private ArrayMap<OnShowModeChangedListener, Handler> mListeners; 1252 private final Object mLock; 1253 SoftKeyboardController(@onNull AccessibilityService service, @NonNull Object lock)1254 SoftKeyboardController(@NonNull AccessibilityService service, @NonNull Object lock) { 1255 mService = service; 1256 mLock = lock; 1257 } 1258 1259 /** 1260 * Called when the service is connected. 1261 */ onServiceConnected()1262 void onServiceConnected() { 1263 synchronized(mLock) { 1264 if (mListeners != null && !mListeners.isEmpty()) { 1265 setSoftKeyboardCallbackEnabled(true); 1266 } 1267 } 1268 } 1269 1270 /** 1271 * Adds the specified change listener to the list of show mode change listeners. The 1272 * callback will occur on the service's main thread. Listener is not called on registration. 1273 */ addOnShowModeChangedListener(@onNull OnShowModeChangedListener listener)1274 public void addOnShowModeChangedListener(@NonNull OnShowModeChangedListener listener) { 1275 addOnShowModeChangedListener(listener, null); 1276 } 1277 1278 /** 1279 * Adds the specified change listener to the list of soft keyboard show mode change 1280 * listeners. The callback will occur on the specified {@link Handler}'s thread, or on the 1281 * services's main thread if the handler is {@code null}. 1282 * 1283 * @param listener the listener to add, must be non-null 1284 * @param handler the handler on which to callback should execute, or {@code null} to 1285 * execute on the service's main thread 1286 */ addOnShowModeChangedListener(@onNull OnShowModeChangedListener listener, @Nullable Handler handler)1287 public void addOnShowModeChangedListener(@NonNull OnShowModeChangedListener listener, 1288 @Nullable Handler handler) { 1289 synchronized (mLock) { 1290 if (mListeners == null) { 1291 mListeners = new ArrayMap<>(); 1292 } 1293 1294 final boolean shouldEnableCallback = mListeners.isEmpty(); 1295 mListeners.put(listener, handler); 1296 1297 if (shouldEnableCallback) { 1298 // This may fail if the service is not connected yet, but if we still have 1299 // listeners when it connects, we can try again. 1300 setSoftKeyboardCallbackEnabled(true); 1301 } 1302 } 1303 } 1304 1305 /** 1306 * Removes the specified change listener from the list of keyboard show mode change 1307 * listeners. 1308 * 1309 * @param listener the listener to remove, must be non-null 1310 * @return {@code true} if the listener was removed, {@code false} otherwise 1311 */ removeOnShowModeChangedListener( @onNull OnShowModeChangedListener listener)1312 public boolean removeOnShowModeChangedListener( 1313 @NonNull OnShowModeChangedListener listener) { 1314 if (mListeners == null) { 1315 return false; 1316 } 1317 1318 synchronized (mLock) { 1319 final int keyIndex = mListeners.indexOfKey(listener); 1320 final boolean hasKey = keyIndex >= 0; 1321 if (hasKey) { 1322 mListeners.removeAt(keyIndex); 1323 } 1324 1325 if (hasKey && mListeners.isEmpty()) { 1326 // We just removed the last listener, so we don't need callbacks from the 1327 // service anymore. 1328 setSoftKeyboardCallbackEnabled(false); 1329 } 1330 1331 return hasKey; 1332 } 1333 } 1334 setSoftKeyboardCallbackEnabled(boolean enabled)1335 private void setSoftKeyboardCallbackEnabled(boolean enabled) { 1336 final IAccessibilityServiceConnection connection = 1337 AccessibilityInteractionClient.getInstance().getConnection( 1338 mService.mConnectionId); 1339 if (connection != null) { 1340 try { 1341 connection.setSoftKeyboardCallbackEnabled(enabled); 1342 } catch (RemoteException re) { 1343 throw new RuntimeException(re); 1344 } 1345 } 1346 } 1347 1348 /** 1349 * Dispatches the soft keyboard show mode change to any registered listeners. This should 1350 * be called on the service's main thread. 1351 */ dispatchSoftKeyboardShowModeChanged(final int showMode)1352 void dispatchSoftKeyboardShowModeChanged(final int showMode) { 1353 final ArrayMap<OnShowModeChangedListener, Handler> entries; 1354 synchronized (mLock) { 1355 if (mListeners == null || mListeners.isEmpty()) { 1356 Slog.w(LOG_TAG, "Received soft keyboard show mode changed callback" 1357 + " with no listeners registered!"); 1358 setSoftKeyboardCallbackEnabled(false); 1359 return; 1360 } 1361 1362 // Listeners may remove themselves. Perform a shallow copy to avoid concurrent 1363 // modification. 1364 entries = new ArrayMap<>(mListeners); 1365 } 1366 1367 for (int i = 0, count = entries.size(); i < count; i++) { 1368 final OnShowModeChangedListener listener = entries.keyAt(i); 1369 final Handler handler = entries.valueAt(i); 1370 if (handler != null) { 1371 handler.post(new Runnable() { 1372 @Override 1373 public void run() { 1374 listener.onShowModeChanged(SoftKeyboardController.this, showMode); 1375 } 1376 }); 1377 } else { 1378 // We're already on the main thread, just run the listener. 1379 listener.onShowModeChanged(this, showMode); 1380 } 1381 } 1382 } 1383 1384 /** 1385 * Returns the show mode of the soft keyboard. 1386 * 1387 * @return the current soft keyboard show mode 1388 * 1389 * @see AccessibilityService#SHOW_MODE_AUTO 1390 * @see AccessibilityService#SHOW_MODE_HIDDEN 1391 * @see AccessibilityService#SHOW_MODE_IGNORE_HARD_KEYBOARD 1392 */ 1393 @SoftKeyboardShowMode getShowMode()1394 public int getShowMode() { 1395 final IAccessibilityServiceConnection connection = 1396 AccessibilityInteractionClient.getInstance().getConnection( 1397 mService.mConnectionId); 1398 if (connection != null) { 1399 try { 1400 return connection.getSoftKeyboardShowMode(); 1401 } catch (RemoteException re) { 1402 Log.w(LOG_TAG, "Failed to set soft keyboard behavior", re); 1403 re.rethrowFromSystemServer(); 1404 } 1405 } 1406 return SHOW_MODE_AUTO; 1407 } 1408 1409 /** 1410 * Sets the soft keyboard show mode. 1411 * <p> 1412 * <strong>Note:</strong> If the service is not yet connected (e.g. 1413 * {@link AccessibilityService#onServiceConnected()} has not yet been called) or the 1414 * service has been disconnected, this method will have no effect and return {@code false}. 1415 * 1416 * @param showMode the new show mode for the soft keyboard 1417 * @return {@code true} on success 1418 * 1419 * @see AccessibilityService#SHOW_MODE_AUTO 1420 * @see AccessibilityService#SHOW_MODE_HIDDEN 1421 * @see AccessibilityService#SHOW_MODE_IGNORE_HARD_KEYBOARD 1422 */ setShowMode(@oftKeyboardShowMode int showMode)1423 public boolean setShowMode(@SoftKeyboardShowMode int showMode) { 1424 final IAccessibilityServiceConnection connection = 1425 AccessibilityInteractionClient.getInstance().getConnection( 1426 mService.mConnectionId); 1427 if (connection != null) { 1428 try { 1429 return connection.setSoftKeyboardShowMode(showMode); 1430 } catch (RemoteException re) { 1431 Log.w(LOG_TAG, "Failed to set soft keyboard behavior", re); 1432 re.rethrowFromSystemServer(); 1433 } 1434 } 1435 return false; 1436 } 1437 1438 /** 1439 * Listener for changes in the soft keyboard show mode. 1440 */ 1441 public interface OnShowModeChangedListener { 1442 /** 1443 * Called when the soft keyboard behavior changes. The default show mode is 1444 * {@code SHOW_MODE_AUTO}, where the soft keyboard is shown when a text input field is 1445 * focused. An AccessibilityService can also request the show mode 1446 * {@code SHOW_MODE_HIDDEN}, where the soft keyboard is never shown. 1447 * 1448 * @param controller the soft keyboard controller 1449 * @param showMode the current soft keyboard show mode 1450 */ onShowModeChanged(@onNull SoftKeyboardController controller, @SoftKeyboardShowMode int showMode)1451 void onShowModeChanged(@NonNull SoftKeyboardController controller, 1452 @SoftKeyboardShowMode int showMode); 1453 } 1454 } 1455 1456 /** 1457 * Returns the controller for the accessibility button within the system's navigation area. 1458 * This instance may be used to query the accessibility button's state and register listeners 1459 * for interactions with and state changes for the accessibility button when 1460 * {@link AccessibilityServiceInfo#FLAG_REQUEST_ACCESSIBILITY_BUTTON} is set. 1461 * <p> 1462 * <strong>Note:</strong> Not all devices are capable of displaying the accessibility button 1463 * within a navigation area, and as such, use of this class should be considered only as an 1464 * optional feature or shortcut on supported device implementations. 1465 * </p> 1466 * 1467 * @return the accessibility button controller for this {@link AccessibilityService} 1468 */ 1469 @NonNull getAccessibilityButtonController()1470 public final AccessibilityButtonController getAccessibilityButtonController() { 1471 synchronized (mLock) { 1472 if (mAccessibilityButtonController == null) { 1473 mAccessibilityButtonController = new AccessibilityButtonController( 1474 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId)); 1475 } 1476 return mAccessibilityButtonController; 1477 } 1478 } 1479 onAccessibilityButtonClicked()1480 private void onAccessibilityButtonClicked() { 1481 getAccessibilityButtonController().dispatchAccessibilityButtonClicked(); 1482 } 1483 onAccessibilityButtonAvailabilityChanged(boolean available)1484 private void onAccessibilityButtonAvailabilityChanged(boolean available) { 1485 getAccessibilityButtonController().dispatchAccessibilityButtonAvailabilityChanged( 1486 available); 1487 } 1488 1489 /** 1490 * Performs a global action. Such an action can be performed 1491 * at any moment regardless of the current application or user 1492 * location in that application. For example going back, going 1493 * home, opening recents, etc. 1494 * 1495 * @param action The action to perform. 1496 * @return Whether the action was successfully performed. 1497 * 1498 * @see #GLOBAL_ACTION_BACK 1499 * @see #GLOBAL_ACTION_HOME 1500 * @see #GLOBAL_ACTION_NOTIFICATIONS 1501 * @see #GLOBAL_ACTION_RECENTS 1502 */ performGlobalAction(int action)1503 public final boolean performGlobalAction(int action) { 1504 IAccessibilityServiceConnection connection = 1505 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); 1506 if (connection != null) { 1507 try { 1508 return connection.performGlobalAction(action); 1509 } catch (RemoteException re) { 1510 Log.w(LOG_TAG, "Error while calling performGlobalAction", re); 1511 re.rethrowFromSystemServer(); 1512 } 1513 } 1514 return false; 1515 } 1516 1517 /** 1518 * Find the view that has the specified focus type. The search is performed 1519 * across all windows. 1520 * <p> 1521 * <strong>Note:</strong> In order to access the windows your service has 1522 * to declare the capability to retrieve window content by setting the 1523 * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent} 1524 * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}. 1525 * Also the service has to opt-in to retrieve the interactive windows by 1526 * setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} 1527 * flag. Otherwise, the search will be performed only in the active window. 1528 * </p> 1529 * 1530 * @param focus The focus to find. One of {@link AccessibilityNodeInfo#FOCUS_INPUT} or 1531 * {@link AccessibilityNodeInfo#FOCUS_ACCESSIBILITY}. 1532 * @return The node info of the focused view or null. 1533 * 1534 * @see AccessibilityNodeInfo#FOCUS_INPUT 1535 * @see AccessibilityNodeInfo#FOCUS_ACCESSIBILITY 1536 */ findFocus(int focus)1537 public AccessibilityNodeInfo findFocus(int focus) { 1538 return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId, 1539 AccessibilityWindowInfo.ANY_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID, focus); 1540 } 1541 1542 /** 1543 * Gets the an {@link AccessibilityServiceInfo} describing this 1544 * {@link AccessibilityService}. This method is useful if one wants 1545 * to change some of the dynamically configurable properties at 1546 * runtime. 1547 * 1548 * @return The accessibility service info. 1549 * 1550 * @see AccessibilityServiceInfo 1551 */ getServiceInfo()1552 public final AccessibilityServiceInfo getServiceInfo() { 1553 IAccessibilityServiceConnection connection = 1554 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); 1555 if (connection != null) { 1556 try { 1557 return connection.getServiceInfo(); 1558 } catch (RemoteException re) { 1559 Log.w(LOG_TAG, "Error while getting AccessibilityServiceInfo", re); 1560 re.rethrowFromSystemServer(); 1561 } 1562 } 1563 return null; 1564 } 1565 1566 /** 1567 * Sets the {@link AccessibilityServiceInfo} that describes this service. 1568 * <p> 1569 * Note: You can call this method any time but the info will be picked up after 1570 * the system has bound to this service and when this method is called thereafter. 1571 * 1572 * @param info The info. 1573 */ setServiceInfo(AccessibilityServiceInfo info)1574 public final void setServiceInfo(AccessibilityServiceInfo info) { 1575 mInfo = info; 1576 sendServiceInfo(); 1577 } 1578 1579 /** 1580 * Sets the {@link AccessibilityServiceInfo} for this service if the latter is 1581 * properly set and there is an {@link IAccessibilityServiceConnection} to the 1582 * AccessibilityManagerService. 1583 */ sendServiceInfo()1584 private void sendServiceInfo() { 1585 IAccessibilityServiceConnection connection = 1586 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); 1587 if (mInfo != null && connection != null) { 1588 try { 1589 connection.setServiceInfo(mInfo); 1590 mInfo = null; 1591 AccessibilityInteractionClient.getInstance().clearCache(); 1592 } catch (RemoteException re) { 1593 Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re); 1594 re.rethrowFromSystemServer(); 1595 } 1596 } 1597 } 1598 1599 @Override getSystemService(@erviceName @onNull String name)1600 public Object getSystemService(@ServiceName @NonNull String name) { 1601 if (getBaseContext() == null) { 1602 throw new IllegalStateException( 1603 "System services not available to Activities before onCreate()"); 1604 } 1605 1606 // Guarantee that we always return the same window manager instance. 1607 if (WINDOW_SERVICE.equals(name)) { 1608 if (mWindowManager == null) { 1609 mWindowManager = (WindowManager) getBaseContext().getSystemService(name); 1610 } 1611 return mWindowManager; 1612 } 1613 return super.getSystemService(name); 1614 } 1615 1616 /** 1617 * Implement to return the implementation of the internal accessibility 1618 * service interface. 1619 */ 1620 @Override onBind(Intent intent)1621 public final IBinder onBind(Intent intent) { 1622 return new IAccessibilityServiceClientWrapper(this, getMainLooper(), new Callbacks() { 1623 @Override 1624 public void onServiceConnected() { 1625 AccessibilityService.this.dispatchServiceConnected(); 1626 } 1627 1628 @Override 1629 public void onInterrupt() { 1630 AccessibilityService.this.onInterrupt(); 1631 } 1632 1633 @Override 1634 public void onAccessibilityEvent(AccessibilityEvent event) { 1635 AccessibilityService.this.onAccessibilityEvent(event); 1636 } 1637 1638 @Override 1639 public void init(int connectionId, IBinder windowToken) { 1640 mConnectionId = connectionId; 1641 mWindowToken = windowToken; 1642 1643 // The client may have already obtained the window manager, so 1644 // update the default token on whatever manager we gave them. 1645 final WindowManagerImpl wm = (WindowManagerImpl) getSystemService(WINDOW_SERVICE); 1646 wm.setDefaultToken(windowToken); 1647 } 1648 1649 @Override 1650 public boolean onGesture(int gestureId) { 1651 return AccessibilityService.this.onGesture(gestureId); 1652 } 1653 1654 @Override 1655 public boolean onKeyEvent(KeyEvent event) { 1656 return AccessibilityService.this.onKeyEvent(event); 1657 } 1658 1659 @Override 1660 public void onMagnificationChanged(int displayId, @NonNull Region region, 1661 float scale, float centerX, float centerY) { 1662 AccessibilityService.this.onMagnificationChanged(displayId, region, scale, 1663 centerX, centerY); 1664 } 1665 1666 @Override 1667 public void onSoftKeyboardShowModeChanged(int showMode) { 1668 AccessibilityService.this.onSoftKeyboardShowModeChanged(showMode); 1669 } 1670 1671 @Override 1672 public void onPerformGestureResult(int sequence, boolean completedSuccessfully) { 1673 AccessibilityService.this.onPerformGestureResult(sequence, completedSuccessfully); 1674 } 1675 1676 @Override 1677 public void onFingerprintCapturingGesturesChanged(boolean active) { 1678 AccessibilityService.this.onFingerprintCapturingGesturesChanged(active); 1679 } 1680 1681 @Override 1682 public void onFingerprintGesture(int gesture) { 1683 AccessibilityService.this.onFingerprintGesture(gesture); 1684 } 1685 1686 @Override 1687 public void onAccessibilityButtonClicked() { 1688 AccessibilityService.this.onAccessibilityButtonClicked(); 1689 } 1690 1691 @Override 1692 public void onAccessibilityButtonAvailabilityChanged(boolean available) { 1693 AccessibilityService.this.onAccessibilityButtonAvailabilityChanged(available); 1694 } 1695 }); 1696 } 1697 1698 /** 1699 * Implements the internal {@link IAccessibilityServiceClient} interface to convert 1700 * incoming calls to it back to calls on an {@link AccessibilityService}. 1701 * 1702 * @hide 1703 */ 1704 public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub 1705 implements HandlerCaller.Callback { 1706 private static final int DO_INIT = 1; 1707 private static final int DO_ON_INTERRUPT = 2; 1708 private static final int DO_ON_ACCESSIBILITY_EVENT = 3; 1709 private static final int DO_ON_GESTURE = 4; 1710 private static final int DO_CLEAR_ACCESSIBILITY_CACHE = 5; 1711 private static final int DO_ON_KEY_EVENT = 6; 1712 private static final int DO_ON_MAGNIFICATION_CHANGED = 7; 1713 private static final int DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED = 8; 1714 private static final int DO_GESTURE_COMPLETE = 9; 1715 private static final int DO_ON_FINGERPRINT_ACTIVE_CHANGED = 10; 1716 private static final int DO_ON_FINGERPRINT_GESTURE = 11; 1717 private static final int DO_ACCESSIBILITY_BUTTON_CLICKED = 12; 1718 private static final int DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED = 13; 1719 1720 private final HandlerCaller mCaller; 1721 1722 private final Callbacks mCallback; 1723 1724 private int mConnectionId = AccessibilityInteractionClient.NO_ID; 1725 1726 public IAccessibilityServiceClientWrapper(Context context, Looper looper, 1727 Callbacks callback) { 1728 mCallback = callback; 1729 mCaller = new HandlerCaller(context, looper, this, true /*asyncHandler*/); 1730 } 1731 1732 public void init(IAccessibilityServiceConnection connection, int connectionId, 1733 IBinder windowToken) { 1734 Message message = mCaller.obtainMessageIOO(DO_INIT, connectionId, 1735 connection, windowToken); 1736 mCaller.sendMessage(message); 1737 } 1738 1739 public void onInterrupt() { 1740 Message message = mCaller.obtainMessage(DO_ON_INTERRUPT); 1741 mCaller.sendMessage(message); 1742 } 1743 1744 public void onAccessibilityEvent(AccessibilityEvent event, boolean serviceWantsEvent) { 1745 Message message = mCaller.obtainMessageBO( 1746 DO_ON_ACCESSIBILITY_EVENT, serviceWantsEvent, event); 1747 mCaller.sendMessage(message); 1748 } 1749 1750 public void onGesture(int gestureId) { 1751 Message message = mCaller.obtainMessageI(DO_ON_GESTURE, gestureId); 1752 mCaller.sendMessage(message); 1753 } 1754 1755 public void clearAccessibilityCache() { 1756 Message message = mCaller.obtainMessage(DO_CLEAR_ACCESSIBILITY_CACHE); 1757 mCaller.sendMessage(message); 1758 } 1759 1760 @Override 1761 public void onKeyEvent(KeyEvent event, int sequence) { 1762 Message message = mCaller.obtainMessageIO(DO_ON_KEY_EVENT, sequence, event); 1763 mCaller.sendMessage(message); 1764 } 1765 1766 /** Magnification changed callbacks for different displays */ 1767 public void onMagnificationChanged(int displayId, @NonNull Region region, 1768 float scale, float centerX, float centerY) { 1769 final SomeArgs args = SomeArgs.obtain(); 1770 args.arg1 = region; 1771 args.arg2 = scale; 1772 args.arg3 = centerX; 1773 args.arg4 = centerY; 1774 args.argi1 = displayId; 1775 1776 final Message message = mCaller.obtainMessageO(DO_ON_MAGNIFICATION_CHANGED, args); 1777 mCaller.sendMessage(message); 1778 } 1779 1780 public void onSoftKeyboardShowModeChanged(int showMode) { 1781 final Message message = 1782 mCaller.obtainMessageI(DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED, showMode); 1783 mCaller.sendMessage(message); 1784 } 1785 1786 public void onPerformGestureResult(int sequence, boolean successfully) { 1787 Message message = mCaller.obtainMessageII(DO_GESTURE_COMPLETE, sequence, 1788 successfully ? 1 : 0); 1789 mCaller.sendMessage(message); 1790 } 1791 1792 public void onFingerprintCapturingGesturesChanged(boolean active) { 1793 mCaller.sendMessage(mCaller.obtainMessageI( 1794 DO_ON_FINGERPRINT_ACTIVE_CHANGED, active ? 1 : 0)); 1795 } 1796 1797 public void onFingerprintGesture(int gesture) { 1798 mCaller.sendMessage(mCaller.obtainMessageI(DO_ON_FINGERPRINT_GESTURE, gesture)); 1799 } 1800 1801 public void onAccessibilityButtonClicked() { 1802 final Message message = mCaller.obtainMessage(DO_ACCESSIBILITY_BUTTON_CLICKED); 1803 mCaller.sendMessage(message); 1804 } 1805 1806 public void onAccessibilityButtonAvailabilityChanged(boolean available) { 1807 final Message message = mCaller.obtainMessageI( 1808 DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED, (available ? 1 : 0)); 1809 mCaller.sendMessage(message); 1810 } 1811 1812 @Override 1813 public void executeMessage(Message message) { 1814 switch (message.what) { 1815 case DO_ON_ACCESSIBILITY_EVENT: { 1816 AccessibilityEvent event = (AccessibilityEvent) message.obj; 1817 boolean serviceWantsEvent = message.arg1 != 0; 1818 if (event != null) { 1819 // Send the event to AccessibilityCache via AccessibilityInteractionClient 1820 AccessibilityInteractionClient.getInstance().onAccessibilityEvent(event); 1821 if (serviceWantsEvent 1822 && (mConnectionId != AccessibilityInteractionClient.NO_ID)) { 1823 // Send the event to AccessibilityService 1824 mCallback.onAccessibilityEvent(event); 1825 } 1826 // Make sure the event is recycled. 1827 try { 1828 event.recycle(); 1829 } catch (IllegalStateException ise) { 1830 /* ignore - best effort */ 1831 } 1832 } 1833 } return; 1834 1835 case DO_ON_INTERRUPT: { 1836 if (mConnectionId != AccessibilityInteractionClient.NO_ID) { 1837 mCallback.onInterrupt(); 1838 } 1839 } return; 1840 1841 case DO_INIT: { 1842 mConnectionId = message.arg1; 1843 SomeArgs args = (SomeArgs) message.obj; 1844 IAccessibilityServiceConnection connection = 1845 (IAccessibilityServiceConnection) args.arg1; 1846 IBinder windowToken = (IBinder) args.arg2; 1847 args.recycle(); 1848 if (connection != null) { 1849 AccessibilityInteractionClient.getInstance().addConnection(mConnectionId, 1850 connection); 1851 mCallback.init(mConnectionId, windowToken); 1852 mCallback.onServiceConnected(); 1853 } else { 1854 AccessibilityInteractionClient.getInstance().removeConnection( 1855 mConnectionId); 1856 mConnectionId = AccessibilityInteractionClient.NO_ID; 1857 AccessibilityInteractionClient.getInstance().clearCache(); 1858 mCallback.init(AccessibilityInteractionClient.NO_ID, null); 1859 } 1860 } return; 1861 1862 case DO_ON_GESTURE: { 1863 if (mConnectionId != AccessibilityInteractionClient.NO_ID) { 1864 final int gestureId = message.arg1; 1865 mCallback.onGesture(gestureId); 1866 } 1867 } return; 1868 1869 case DO_CLEAR_ACCESSIBILITY_CACHE: { 1870 AccessibilityInteractionClient.getInstance().clearCache(); 1871 } return; 1872 1873 case DO_ON_KEY_EVENT: { 1874 KeyEvent event = (KeyEvent) message.obj; 1875 try { 1876 IAccessibilityServiceConnection connection = AccessibilityInteractionClient 1877 .getInstance().getConnection(mConnectionId); 1878 if (connection != null) { 1879 final boolean result = mCallback.onKeyEvent(event); 1880 final int sequence = message.arg1; 1881 try { 1882 connection.setOnKeyEventResult(result, sequence); 1883 } catch (RemoteException re) { 1884 /* ignore */ 1885 } 1886 } 1887 } finally { 1888 // Make sure the event is recycled. 1889 try { 1890 event.recycle(); 1891 } catch (IllegalStateException ise) { 1892 /* ignore - best effort */ 1893 } 1894 } 1895 } return; 1896 1897 case DO_ON_MAGNIFICATION_CHANGED: { 1898 if (mConnectionId != AccessibilityInteractionClient.NO_ID) { 1899 final SomeArgs args = (SomeArgs) message.obj; 1900 final Region region = (Region) args.arg1; 1901 final float scale = (float) args.arg2; 1902 final float centerX = (float) args.arg3; 1903 final float centerY = (float) args.arg4; 1904 final int displayId = args.argi1; 1905 args.recycle(); 1906 mCallback.onMagnificationChanged(displayId, region, scale, 1907 centerX, centerY); 1908 } 1909 } return; 1910 1911 case DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED: { 1912 if (mConnectionId != AccessibilityInteractionClient.NO_ID) { 1913 final int showMode = (int) message.arg1; 1914 mCallback.onSoftKeyboardShowModeChanged(showMode); 1915 } 1916 } return; 1917 1918 case DO_GESTURE_COMPLETE: { 1919 if (mConnectionId != AccessibilityInteractionClient.NO_ID) { 1920 final boolean successfully = message.arg2 == 1; 1921 mCallback.onPerformGestureResult(message.arg1, successfully); 1922 } 1923 } return; 1924 case DO_ON_FINGERPRINT_ACTIVE_CHANGED: { 1925 if (mConnectionId != AccessibilityInteractionClient.NO_ID) { 1926 mCallback.onFingerprintCapturingGesturesChanged(message.arg1 == 1); 1927 } 1928 } return; 1929 case DO_ON_FINGERPRINT_GESTURE: { 1930 if (mConnectionId != AccessibilityInteractionClient.NO_ID) { 1931 mCallback.onFingerprintGesture(message.arg1); 1932 } 1933 } return; 1934 1935 case (DO_ACCESSIBILITY_BUTTON_CLICKED): { 1936 if (mConnectionId != AccessibilityInteractionClient.NO_ID) { 1937 mCallback.onAccessibilityButtonClicked(); 1938 } 1939 } return; 1940 1941 case (DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED): { 1942 if (mConnectionId != AccessibilityInteractionClient.NO_ID) { 1943 final boolean available = (message.arg1 != 0); 1944 mCallback.onAccessibilityButtonAvailabilityChanged(available); 1945 } 1946 } return; 1947 1948 default : 1949 Log.w(LOG_TAG, "Unknown message type " + message.what); 1950 } 1951 } 1952 } 1953 1954 /** 1955 * Class used to report status of dispatched gestures 1956 */ 1957 public static abstract class GestureResultCallback { 1958 /** Called when the gesture has completed successfully 1959 * 1960 * @param gestureDescription The description of the gesture that completed. 1961 */ 1962 public void onCompleted(GestureDescription gestureDescription) { 1963 } 1964 1965 /** Called when the gesture was cancelled 1966 * 1967 * @param gestureDescription The description of the gesture that was cancelled. 1968 */ 1969 public void onCancelled(GestureDescription gestureDescription) { 1970 } 1971 } 1972 1973 /* Object to keep track of gesture result callbacks */ 1974 private static class GestureResultCallbackInfo { 1975 GestureDescription gestureDescription; 1976 GestureResultCallback callback; 1977 Handler handler; 1978 1979 GestureResultCallbackInfo(GestureDescription gestureDescription, 1980 GestureResultCallback callback, Handler handler) { 1981 this.gestureDescription = gestureDescription; 1982 this.callback = callback; 1983 this.handler = handler; 1984 } 1985 } 1986 } 1987