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