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