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