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