AccessibilityService.java revision 7c01afdd18fd4e81c0bfff2f9062b752a13576a5
1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.accessibilityservice; 18 19import android.accessibilityservice.GestureDescription.MotionEventGenerator; 20import android.annotation.IntDef; 21import android.annotation.NonNull; 22import android.annotation.Nullable; 23import android.annotation.RequiresPermission; 24import android.app.Service; 25import android.content.Context; 26import android.content.Intent; 27import android.content.pm.ParceledListSlice; 28import android.graphics.Region; 29import android.hardware.fingerprint.FingerprintManager; 30import android.os.Handler; 31import android.os.IBinder; 32import android.os.Looper; 33import android.os.Message; 34import android.os.RemoteException; 35import android.provider.Settings; 36import android.util.ArrayMap; 37import android.util.Log; 38import android.util.Slog; 39import android.util.SparseArray; 40import android.view.KeyEvent; 41import android.view.WindowManager; 42import android.view.WindowManagerImpl; 43import android.view.accessibility.AccessibilityEvent; 44import android.view.accessibility.AccessibilityInteractionClient; 45import android.view.accessibility.AccessibilityNodeInfo; 46import android.view.accessibility.AccessibilityWindowInfo; 47 48import com.android.internal.os.HandlerCaller; 49import com.android.internal.os.SomeArgs; 50 51import java.lang.annotation.Retention; 52import java.lang.annotation.RetentionPolicy; 53import java.util.List; 54 55import 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 */ 221public 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 void onAccessibilityEvent(AccessibilityEvent event) {} 425 426 /** 427 * Callback for interrupting the accessibility feedback. 428 */ 429 public 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 @Nullable FingerprintGestureController getFingerprintGestureController() { 623 if ((mFingerprintGestureController == null) 624 && getPackageManager().hasSystemFeature(FEATURE_FINGERPRINT)) { 625 FingerprintManager fingerprintManager = getSystemService(FingerprintManager.class); 626 if ((fingerprintManager != null) && fingerprintManager.isHardwareDetected()) { 627 AccessibilityServiceInfo info = getServiceInfo(); 628 int fingerprintCapabilityMask = 629 AccessibilityServiceInfo.CAPABILITY_CAN_CAPTURE_FINGERPRINT_GESTURES; 630 if ((info.getCapabilities() & fingerprintCapabilityMask) != 0) { 631 mFingerprintGestureController = new FingerprintGestureController( 632 AccessibilityInteractionClient.getInstance() 633 .getConnection(mConnectionId)); 634 } 635 } 636 } 637 return mFingerprintGestureController; 638 } 639 640 /** 641 * Dispatch a gesture to the touch screen. Any gestures currently in progress, whether from 642 * the user, this service, or another service, will be cancelled. 643 * <p> 644 * The gesture will be dispatched as if it were performed directly on the screen by a user, so 645 * the events may be affected by features such as magnification and explore by touch. 646 * </p> 647 * <p> 648 * <strong>Note:</strong> In order to dispatch gestures, your service 649 * must declare the capability by setting the 650 * {@link android.R.styleable#AccessibilityService_canPerformGestures} 651 * property in its meta-data. For more information, see 652 * {@link #SERVICE_META_DATA}. 653 * </p> 654 * 655 * @param gesture The gesture to dispatch 656 * @param callback The object to call back when the status of the gesture is known. If 657 * {@code null}, no status is reported. 658 * @param handler The handler on which to call back the {@code callback} object. If 659 * {@code null}, the object is called back on the service's main thread. 660 * 661 * @return {@code true} if the gesture is dispatched, {@code false} if not. 662 */ 663 public final boolean dispatchGesture(@NonNull GestureDescription gesture, 664 @Nullable GestureResultCallback callback, 665 @Nullable Handler handler) { 666 final IAccessibilityServiceConnection connection = 667 AccessibilityInteractionClient.getInstance().getConnection( 668 mConnectionId); 669 if (connection == null) { 670 return false; 671 } 672 List<GestureDescription.GestureStep> steps = 673 MotionEventGenerator.getGestureStepsFromGestureDescription(gesture, 100); 674 try { 675 synchronized (mLock) { 676 mGestureStatusCallbackSequence++; 677 if (callback != null) { 678 if (mGestureStatusCallbackInfos == null) { 679 mGestureStatusCallbackInfos = new SparseArray<>(); 680 } 681 GestureResultCallbackInfo callbackInfo = new GestureResultCallbackInfo(gesture, 682 callback, handler); 683 mGestureStatusCallbackInfos.put(mGestureStatusCallbackSequence, callbackInfo); 684 } 685 connection.sendGesture(mGestureStatusCallbackSequence, 686 new ParceledListSlice<>(steps)); 687 } 688 } catch (RemoteException re) { 689 throw new RuntimeException(re); 690 } 691 return true; 692 } 693 694 void onPerformGestureResult(int sequence, final boolean completedSuccessfully) { 695 if (mGestureStatusCallbackInfos == null) { 696 return; 697 } 698 GestureResultCallbackInfo callbackInfo; 699 synchronized (mLock) { 700 callbackInfo = mGestureStatusCallbackInfos.get(sequence); 701 } 702 final GestureResultCallbackInfo finalCallbackInfo = callbackInfo; 703 if ((callbackInfo != null) && (callbackInfo.gestureDescription != null) 704 && (callbackInfo.callback != null)) { 705 if (callbackInfo.handler != null) { 706 callbackInfo.handler.post(new Runnable() { 707 @Override 708 public void run() { 709 if (completedSuccessfully) { 710 finalCallbackInfo.callback 711 .onCompleted(finalCallbackInfo.gestureDescription); 712 } else { 713 finalCallbackInfo.callback 714 .onCancelled(finalCallbackInfo.gestureDescription); 715 } 716 } 717 }); 718 return; 719 } 720 if (completedSuccessfully) { 721 callbackInfo.callback.onCompleted(callbackInfo.gestureDescription); 722 } else { 723 callbackInfo.callback.onCancelled(callbackInfo.gestureDescription); 724 } 725 } 726 } 727 728 private void onMagnificationChanged(@NonNull Region region, float scale, 729 float centerX, float centerY) { 730 if (mMagnificationController != null) { 731 mMagnificationController.dispatchMagnificationChanged( 732 region, scale, centerX, centerY); 733 } 734 } 735 736 /** 737 * Callback for fingerprint gesture handling 738 * @param active If gesture detection is active 739 */ 740 private void onFingerprintCapturingGesturesChanged(boolean active) { 741 getFingerprintGestureController().onGestureDetectionActiveChanged(active); 742 } 743 744 /** 745 * Callback for fingerprint gesture handling 746 * @param gesture The identifier for the gesture performed 747 */ 748 private void onFingerprintGesture(int gesture) { 749 getFingerprintGestureController().onGesture(gesture); 750 } 751 752 /** 753 * Used to control and query the state of display magnification. 754 */ 755 public static final class MagnificationController { 756 private final AccessibilityService mService; 757 758 /** 759 * Map of listeners to their handlers. Lazily created when adding the 760 * first magnification listener. 761 */ 762 private ArrayMap<OnMagnificationChangedListener, Handler> mListeners; 763 private final Object mLock; 764 765 MagnificationController(@NonNull AccessibilityService service, @NonNull Object lock) { 766 mService = service; 767 mLock = lock; 768 } 769 770 /** 771 * Called when the service is connected. 772 */ 773 void onServiceConnected() { 774 synchronized (mLock) { 775 if (mListeners != null && !mListeners.isEmpty()) { 776 setMagnificationCallbackEnabled(true); 777 } 778 } 779 } 780 781 /** 782 * Adds the specified change listener to the list of magnification 783 * change listeners. The callback will occur on the service's main 784 * thread. 785 * 786 * @param listener the listener to add, must be non-{@code null} 787 */ 788 public void addListener(@NonNull OnMagnificationChangedListener listener) { 789 addListener(listener, null); 790 } 791 792 /** 793 * Adds the specified change listener to the list of magnification 794 * change listeners. The callback will occur on the specified 795 * {@link Handler}'s thread, or on the service's main thread if the 796 * handler is {@code null}. 797 * 798 * @param listener the listener to add, must be non-null 799 * @param handler the handler on which the callback should execute, or 800 * {@code null} to execute on the service's main thread 801 */ 802 public void addListener(@NonNull OnMagnificationChangedListener listener, 803 @Nullable Handler handler) { 804 synchronized (mLock) { 805 if (mListeners == null) { 806 mListeners = new ArrayMap<>(); 807 } 808 809 final boolean shouldEnableCallback = mListeners.isEmpty(); 810 mListeners.put(listener, handler); 811 812 if (shouldEnableCallback) { 813 // This may fail if the service is not connected yet, but if we 814 // still have listeners when it connects then we can try again. 815 setMagnificationCallbackEnabled(true); 816 } 817 } 818 } 819 820 /** 821 * Removes the specified change listener from the list of magnification change listeners. 822 * 823 * @param listener the listener to remove, must be non-null 824 * @return {@code true} if the listener was removed, {@code false} otherwise 825 */ 826 public boolean removeListener(@NonNull OnMagnificationChangedListener listener) { 827 if (mListeners == null) { 828 return false; 829 } 830 831 synchronized (mLock) { 832 final int keyIndex = mListeners.indexOfKey(listener); 833 final boolean hasKey = keyIndex >= 0; 834 if (hasKey) { 835 mListeners.removeAt(keyIndex); 836 } 837 838 if (hasKey && mListeners.isEmpty()) { 839 // We just removed the last listener, so we don't need 840 // callbacks from the service anymore. 841 setMagnificationCallbackEnabled(false); 842 } 843 844 return hasKey; 845 } 846 } 847 848 private void setMagnificationCallbackEnabled(boolean enabled) { 849 final IAccessibilityServiceConnection connection = 850 AccessibilityInteractionClient.getInstance().getConnection( 851 mService.mConnectionId); 852 if (connection != null) { 853 try { 854 connection.setMagnificationCallbackEnabled(enabled); 855 } catch (RemoteException re) { 856 throw new RuntimeException(re); 857 } 858 } 859 } 860 861 /** 862 * Dispatches magnification changes to any registered listeners. This 863 * should be called on the service's main thread. 864 */ 865 void dispatchMagnificationChanged(final @NonNull Region region, final float scale, 866 final float centerX, final float centerY) { 867 final ArrayMap<OnMagnificationChangedListener, Handler> entries; 868 synchronized (mLock) { 869 if (mListeners == null || mListeners.isEmpty()) { 870 Slog.d(LOG_TAG, "Received magnification changed " 871 + "callback with no listeners registered!"); 872 setMagnificationCallbackEnabled(false); 873 return; 874 } 875 876 // Listeners may remove themselves. Perform a shallow copy to avoid concurrent 877 // modification. 878 entries = new ArrayMap<>(mListeners); 879 } 880 881 for (int i = 0, count = entries.size(); i < count; i++) { 882 final OnMagnificationChangedListener listener = entries.keyAt(i); 883 final Handler handler = entries.valueAt(i); 884 if (handler != null) { 885 handler.post(new Runnable() { 886 @Override 887 public void run() { 888 listener.onMagnificationChanged(MagnificationController.this, 889 region, scale, centerX, centerY); 890 } 891 }); 892 } else { 893 // We're already on the main thread, just run the listener. 894 listener.onMagnificationChanged(this, region, scale, centerX, centerY); 895 } 896 } 897 } 898 899 /** 900 * Returns the current magnification scale. 901 * <p> 902 * <strong>Note:</strong> If the service is not yet connected (e.g. 903 * {@link AccessibilityService#onServiceConnected()} has not yet been 904 * called) or the service has been disconnected, this method will 905 * return a default value of {@code 1.0f}. 906 * 907 * @return the current magnification scale 908 */ 909 public float getScale() { 910 final IAccessibilityServiceConnection connection = 911 AccessibilityInteractionClient.getInstance().getConnection( 912 mService.mConnectionId); 913 if (connection != null) { 914 try { 915 return connection.getMagnificationScale(); 916 } catch (RemoteException re) { 917 Log.w(LOG_TAG, "Failed to obtain scale", re); 918 re.rethrowFromSystemServer(); 919 } 920 } 921 return 1.0f; 922 } 923 924 /** 925 * Returns the unscaled screen-relative X coordinate of the focal 926 * center of the magnified region. This is the point around which 927 * zooming occurs and is guaranteed to lie within the magnified 928 * region. 929 * <p> 930 * <strong>Note:</strong> If the service is not yet connected (e.g. 931 * {@link AccessibilityService#onServiceConnected()} has not yet been 932 * called) or the service has been disconnected, this method will 933 * return a default value of {@code 0.0f}. 934 * 935 * @return the unscaled screen-relative X coordinate of the center of 936 * the magnified region 937 */ 938 public float getCenterX() { 939 final IAccessibilityServiceConnection connection = 940 AccessibilityInteractionClient.getInstance().getConnection( 941 mService.mConnectionId); 942 if (connection != null) { 943 try { 944 return connection.getMagnificationCenterX(); 945 } catch (RemoteException re) { 946 Log.w(LOG_TAG, "Failed to obtain center X", re); 947 re.rethrowFromSystemServer(); 948 } 949 } 950 return 0.0f; 951 } 952 953 /** 954 * Returns the unscaled screen-relative Y coordinate of the focal 955 * center of the magnified region. This is the point around which 956 * zooming occurs and is guaranteed to lie within the magnified 957 * region. 958 * <p> 959 * <strong>Note:</strong> If the service is not yet connected (e.g. 960 * {@link AccessibilityService#onServiceConnected()} has not yet been 961 * called) or the service has been disconnected, this method will 962 * return a default value of {@code 0.0f}. 963 * 964 * @return the unscaled screen-relative Y coordinate of the center of 965 * the magnified region 966 */ 967 public float getCenterY() { 968 final IAccessibilityServiceConnection connection = 969 AccessibilityInteractionClient.getInstance().getConnection( 970 mService.mConnectionId); 971 if (connection != null) { 972 try { 973 return connection.getMagnificationCenterY(); 974 } catch (RemoteException re) { 975 Log.w(LOG_TAG, "Failed to obtain center Y", re); 976 re.rethrowFromSystemServer(); 977 } 978 } 979 return 0.0f; 980 } 981 982 /** 983 * Returns the region of the screen currently active for magnification. Changes to 984 * magnification scale and center only affect this portion of the screen. The rest of the 985 * screen, for example input methods, cannot be magnified. This region is relative to the 986 * unscaled screen and is independent of the scale and center point. 987 * <p> 988 * The returned region will be empty if magnification is not active. Magnification is active 989 * if magnification gestures are enabled or if a service is running that can control 990 * magnification. 991 * <p> 992 * <strong>Note:</strong> If the service is not yet connected (e.g. 993 * {@link AccessibilityService#onServiceConnected()} has not yet been 994 * called) or the service has been disconnected, this method will 995 * return an empty region. 996 * 997 * @return the region of the screen currently active for magnification, or an empty region 998 * if magnification is not active. 999 */ 1000 @NonNull 1001 public Region getMagnificationRegion() { 1002 final IAccessibilityServiceConnection connection = 1003 AccessibilityInteractionClient.getInstance().getConnection( 1004 mService.mConnectionId); 1005 if (connection != null) { 1006 try { 1007 return connection.getMagnificationRegion(); 1008 } catch (RemoteException re) { 1009 Log.w(LOG_TAG, "Failed to obtain magnified region", re); 1010 re.rethrowFromSystemServer(); 1011 } 1012 } 1013 return Region.obtain(); 1014 } 1015 1016 /** 1017 * Resets magnification scale and center to their default (e.g. no 1018 * magnification) values. 1019 * <p> 1020 * <strong>Note:</strong> If the service is not yet connected (e.g. 1021 * {@link AccessibilityService#onServiceConnected()} has not yet been 1022 * called) or the service has been disconnected, this method will have 1023 * no effect and return {@code false}. 1024 * 1025 * @param animate {@code true} to animate from the current scale and 1026 * center or {@code false} to reset the scale and center 1027 * immediately 1028 * @return {@code true} on success, {@code false} on failure 1029 */ 1030 public boolean reset(boolean animate) { 1031 final IAccessibilityServiceConnection connection = 1032 AccessibilityInteractionClient.getInstance().getConnection( 1033 mService.mConnectionId); 1034 if (connection != null) { 1035 try { 1036 return connection.resetMagnification(animate); 1037 } catch (RemoteException re) { 1038 Log.w(LOG_TAG, "Failed to reset", re); 1039 re.rethrowFromSystemServer(); 1040 } 1041 } 1042 return false; 1043 } 1044 1045 /** 1046 * Sets the magnification scale. 1047 * <p> 1048 * <strong>Note:</strong> If the service is not yet connected (e.g. 1049 * {@link AccessibilityService#onServiceConnected()} has not yet been 1050 * called) or the service has been disconnected, this method will have 1051 * no effect and return {@code false}. 1052 * 1053 * @param scale the magnification scale to set, must be >= 1 and <= 5 1054 * @param animate {@code true} to animate from the current scale or 1055 * {@code false} to set the scale immediately 1056 * @return {@code true} on success, {@code false} on failure 1057 */ 1058 public boolean setScale(float scale, boolean animate) { 1059 final IAccessibilityServiceConnection connection = 1060 AccessibilityInteractionClient.getInstance().getConnection( 1061 mService.mConnectionId); 1062 if (connection != null) { 1063 try { 1064 return connection.setMagnificationScaleAndCenter( 1065 scale, Float.NaN, Float.NaN, animate); 1066 } catch (RemoteException re) { 1067 Log.w(LOG_TAG, "Failed to set scale", re); 1068 re.rethrowFromSystemServer(); 1069 } 1070 } 1071 return false; 1072 } 1073 1074 /** 1075 * Sets the center of the magnified viewport. 1076 * <p> 1077 * <strong>Note:</strong> If the service is not yet connected (e.g. 1078 * {@link AccessibilityService#onServiceConnected()} has not yet been 1079 * called) or the service has been disconnected, this method will have 1080 * no effect and return {@code false}. 1081 * 1082 * @param centerX the unscaled screen-relative X coordinate on which to 1083 * center the viewport 1084 * @param centerY the unscaled screen-relative Y coordinate on which to 1085 * center the viewport 1086 * @param animate {@code true} to animate from the current viewport 1087 * center or {@code false} to set the center immediately 1088 * @return {@code true} on success, {@code false} on failure 1089 */ 1090 public boolean setCenter(float centerX, float centerY, boolean animate) { 1091 final IAccessibilityServiceConnection connection = 1092 AccessibilityInteractionClient.getInstance().getConnection( 1093 mService.mConnectionId); 1094 if (connection != null) { 1095 try { 1096 return connection.setMagnificationScaleAndCenter( 1097 Float.NaN, centerX, centerY, animate); 1098 } catch (RemoteException re) { 1099 Log.w(LOG_TAG, "Failed to set center", re); 1100 re.rethrowFromSystemServer(); 1101 } 1102 } 1103 return false; 1104 } 1105 1106 /** 1107 * Listener for changes in the state of magnification. 1108 */ 1109 public interface OnMagnificationChangedListener { 1110 /** 1111 * Called when the magnified region, scale, or center changes. 1112 * 1113 * @param controller the magnification controller 1114 * @param region the magnification region 1115 * @param scale the new scale 1116 * @param centerX the new X coordinate, in unscaled coordinates, around which 1117 * magnification is focused 1118 * @param centerY the new Y coordinate, in unscaled coordinates, around which 1119 * magnification is focused 1120 */ 1121 void onMagnificationChanged(@NonNull MagnificationController controller, 1122 @NonNull Region region, float scale, float centerX, float centerY); 1123 } 1124 } 1125 1126 /** 1127 * Returns the soft keyboard controller, which may be used to query and modify the soft keyboard 1128 * show mode. 1129 * 1130 * @return the soft keyboard controller 1131 */ 1132 @NonNull 1133 public final SoftKeyboardController getSoftKeyboardController() { 1134 synchronized (mLock) { 1135 if (mSoftKeyboardController == null) { 1136 mSoftKeyboardController = new SoftKeyboardController(this, mLock); 1137 } 1138 return mSoftKeyboardController; 1139 } 1140 } 1141 1142 private void onSoftKeyboardShowModeChanged(int showMode) { 1143 if (mSoftKeyboardController != null) { 1144 mSoftKeyboardController.dispatchSoftKeyboardShowModeChanged(showMode); 1145 } 1146 } 1147 1148 /** 1149 * Used to control and query the soft keyboard show mode. 1150 */ 1151 public static final class SoftKeyboardController { 1152 private final AccessibilityService mService; 1153 1154 /** 1155 * Map of listeners to their handlers. Lazily created when adding the first 1156 * soft keyboard change listener. 1157 */ 1158 private ArrayMap<OnShowModeChangedListener, Handler> mListeners; 1159 private final Object mLock; 1160 1161 SoftKeyboardController(@NonNull AccessibilityService service, @NonNull Object lock) { 1162 mService = service; 1163 mLock = lock; 1164 } 1165 1166 /** 1167 * Called when the service is connected. 1168 */ 1169 void onServiceConnected() { 1170 synchronized(mLock) { 1171 if (mListeners != null && !mListeners.isEmpty()) { 1172 setSoftKeyboardCallbackEnabled(true); 1173 } 1174 } 1175 } 1176 1177 /** 1178 * Adds the specified change listener to the list of show mode change listeners. The 1179 * callback will occur on the service's main thread. Listener is not called on registration. 1180 */ 1181 public void addOnShowModeChangedListener(@NonNull OnShowModeChangedListener listener) { 1182 addOnShowModeChangedListener(listener, null); 1183 } 1184 1185 /** 1186 * Adds the specified change listener to the list of soft keyboard show mode change 1187 * listeners. The callback will occur on the specified {@link Handler}'s thread, or on the 1188 * services's main thread if the handler is {@code null}. 1189 * 1190 * @param listener the listener to add, must be non-null 1191 * @param handler the handler on which to callback should execute, or {@code null} to 1192 * execute on the service's main thread 1193 */ 1194 public void addOnShowModeChangedListener(@NonNull OnShowModeChangedListener listener, 1195 @Nullable Handler handler) { 1196 synchronized (mLock) { 1197 if (mListeners == null) { 1198 mListeners = new ArrayMap<>(); 1199 } 1200 1201 final boolean shouldEnableCallback = mListeners.isEmpty(); 1202 mListeners.put(listener, handler); 1203 1204 if (shouldEnableCallback) { 1205 // This may fail if the service is not connected yet, but if we still have 1206 // listeners when it connects, we can try again. 1207 setSoftKeyboardCallbackEnabled(true); 1208 } 1209 } 1210 } 1211 1212 /** 1213 * Removes the specified change listener from the list of keyboard show mode change 1214 * listeners. 1215 * 1216 * @param listener the listener to remove, must be non-null 1217 * @return {@code true} if the listener was removed, {@code false} otherwise 1218 */ 1219 public boolean removeOnShowModeChangedListener(@NonNull OnShowModeChangedListener listener) { 1220 if (mListeners == null) { 1221 return false; 1222 } 1223 1224 synchronized (mLock) { 1225 final int keyIndex = mListeners.indexOfKey(listener); 1226 final boolean hasKey = keyIndex >= 0; 1227 if (hasKey) { 1228 mListeners.removeAt(keyIndex); 1229 } 1230 1231 if (hasKey && mListeners.isEmpty()) { 1232 // We just removed the last listener, so we don't need callbacks from the 1233 // service anymore. 1234 setSoftKeyboardCallbackEnabled(false); 1235 } 1236 1237 return hasKey; 1238 } 1239 } 1240 1241 private void setSoftKeyboardCallbackEnabled(boolean enabled) { 1242 final IAccessibilityServiceConnection connection = 1243 AccessibilityInteractionClient.getInstance().getConnection( 1244 mService.mConnectionId); 1245 if (connection != null) { 1246 try { 1247 connection.setSoftKeyboardCallbackEnabled(enabled); 1248 } catch (RemoteException re) { 1249 throw new RuntimeException(re); 1250 } 1251 } 1252 } 1253 1254 /** 1255 * Dispatches the soft keyboard show mode change to any registered listeners. This should 1256 * be called on the service's main thread. 1257 */ 1258 void dispatchSoftKeyboardShowModeChanged(final int showMode) { 1259 final ArrayMap<OnShowModeChangedListener, Handler> entries; 1260 synchronized (mLock) { 1261 if (mListeners == null || mListeners.isEmpty()) { 1262 Slog.w(LOG_TAG, "Received soft keyboard show mode changed callback" 1263 + " with no listeners registered!"); 1264 setSoftKeyboardCallbackEnabled(false); 1265 return; 1266 } 1267 1268 // Listeners may remove themselves. Perform a shallow copy to avoid concurrent 1269 // modification. 1270 entries = new ArrayMap<>(mListeners); 1271 } 1272 1273 for (int i = 0, count = entries.size(); i < count; i++) { 1274 final OnShowModeChangedListener listener = entries.keyAt(i); 1275 final Handler handler = entries.valueAt(i); 1276 if (handler != null) { 1277 handler.post(new Runnable() { 1278 @Override 1279 public void run() { 1280 listener.onShowModeChanged(SoftKeyboardController.this, showMode); 1281 } 1282 }); 1283 } else { 1284 // We're already on the main thread, just run the listener. 1285 listener.onShowModeChanged(this, showMode); 1286 } 1287 } 1288 } 1289 1290 /** 1291 * Returns the show mode of the soft keyboard. The default show mode is 1292 * {@code SHOW_MODE_AUTO}, where the soft keyboard is shown when a text input field is 1293 * focused. An AccessibilityService can also request the show mode 1294 * {@code SHOW_MODE_HIDDEN}, where the soft keyboard is never shown. 1295 * 1296 * @return the current soft keyboard show mode 1297 */ 1298 @SoftKeyboardShowMode 1299 public int getShowMode() { 1300 try { 1301 return Settings.Secure.getInt(mService.getContentResolver(), 1302 Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE); 1303 } catch (Settings.SettingNotFoundException e) { 1304 Log.v(LOG_TAG, "Failed to obtain the soft keyboard mode", e); 1305 // The settings hasn't been changed yet, so it's value is null. Return the default. 1306 return 0; 1307 } 1308 } 1309 1310 /** 1311 * Sets the soft keyboard show mode. The default show mode is 1312 * {@code SHOW_MODE_AUTO}, where the soft keyboard is shown when a text input field is 1313 * focused. An AccessibilityService can also request the show mode 1314 * {@code SHOW_MODE_HIDDEN}, where the soft keyboard is never shown. The 1315 * The lastto this method will be honored, regardless of any previous calls (including those 1316 * made by other AccessibilityServices). 1317 * <p> 1318 * <strong>Note:</strong> If the service is not yet connected (e.g. 1319 * {@link AccessibilityService#onServiceConnected()} has not yet been called) or the 1320 * service has been disconnected, this method will have no effect and return {@code false}. 1321 * 1322 * @param showMode the new show mode for the soft keyboard 1323 * @return {@code true} on success 1324 */ 1325 public boolean setShowMode(@SoftKeyboardShowMode int showMode) { 1326 final IAccessibilityServiceConnection connection = 1327 AccessibilityInteractionClient.getInstance().getConnection( 1328 mService.mConnectionId); 1329 if (connection != null) { 1330 try { 1331 return connection.setSoftKeyboardShowMode(showMode); 1332 } catch (RemoteException re) { 1333 Log.w(LOG_TAG, "Failed to set soft keyboard behavior", re); 1334 re.rethrowFromSystemServer(); 1335 } 1336 } 1337 return false; 1338 } 1339 1340 /** 1341 * Listener for changes in the soft keyboard show mode. 1342 */ 1343 public interface OnShowModeChangedListener { 1344 /** 1345 * Called when the soft keyboard behavior changes. The default show mode is 1346 * {@code SHOW_MODE_AUTO}, where the soft keyboard is shown when a text input field is 1347 * focused. An AccessibilityService can also request the show mode 1348 * {@code SHOW_MODE_HIDDEN}, where the soft keyboard is never shown. 1349 * 1350 * @param controller the soft keyboard controller 1351 * @param showMode the current soft keyboard show mode 1352 */ 1353 void onShowModeChanged(@NonNull SoftKeyboardController controller, 1354 @SoftKeyboardShowMode int showMode); 1355 } 1356 } 1357 1358 /** 1359 * Returns the controller for the accessibility button within the system's navigation area. 1360 * This instance may be used to query the accessibility button's state and register listeners 1361 * for interactions with and state changes for the accessibility button when 1362 * {@link AccessibilityServiceInfo#FLAG_REQUEST_ACCESSIBILITY_BUTTON} is set. 1363 * <p> 1364 * <strong>Note:</strong> Not all devices are capable of displaying the accessibility button 1365 * within a navigation area, and as such, use of this class should be considered only as an 1366 * optional feature or shortcut on supported device implementations. 1367 * </p> 1368 * 1369 * @return the accessibility button controller for this {@link AccessibilityService} 1370 */ 1371 @NonNull 1372 public final AccessibilityButtonController getAccessibilityButtonController() { 1373 synchronized (mLock) { 1374 if (mAccessibilityButtonController == null) { 1375 mAccessibilityButtonController = new AccessibilityButtonController( 1376 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId)); 1377 } 1378 return mAccessibilityButtonController; 1379 } 1380 } 1381 1382 private void onAccessibilityButtonClicked() { 1383 getAccessibilityButtonController().dispatchAccessibilityButtonClicked(); 1384 } 1385 1386 private void onAccessibilityButtonAvailabilityChanged(boolean available) { 1387 getAccessibilityButtonController().dispatchAccessibilityButtonAvailabilityChanged( 1388 available); 1389 } 1390 1391 /** 1392 * Performs a global action. Such an action can be performed 1393 * at any moment regardless of the current application or user 1394 * location in that application. For example going back, going 1395 * home, opening recents, etc. 1396 * 1397 * @param action The action to perform. 1398 * @return Whether the action was successfully performed. 1399 * 1400 * @see #GLOBAL_ACTION_BACK 1401 * @see #GLOBAL_ACTION_HOME 1402 * @see #GLOBAL_ACTION_NOTIFICATIONS 1403 * @see #GLOBAL_ACTION_RECENTS 1404 */ 1405 public final boolean performGlobalAction(int action) { 1406 IAccessibilityServiceConnection connection = 1407 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); 1408 if (connection != null) { 1409 try { 1410 return connection.performGlobalAction(action); 1411 } catch (RemoteException re) { 1412 Log.w(LOG_TAG, "Error while calling performGlobalAction", re); 1413 re.rethrowFromSystemServer(); 1414 } 1415 } 1416 return false; 1417 } 1418 1419 /** 1420 * Find the view that has the specified focus type. The search is performed 1421 * across all windows. 1422 * <p> 1423 * <strong>Note:</strong> In order to access the windows your service has 1424 * to declare the capability to retrieve window content by setting the 1425 * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent} 1426 * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}. 1427 * Also the service has to opt-in to retrieve the interactive windows by 1428 * setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} 1429 * flag. Otherwise, the search will be performed only in the active window. 1430 * </p> 1431 * 1432 * @param focus The focus to find. One of {@link AccessibilityNodeInfo#FOCUS_INPUT} or 1433 * {@link AccessibilityNodeInfo#FOCUS_ACCESSIBILITY}. 1434 * @return The node info of the focused view or null. 1435 * 1436 * @see AccessibilityNodeInfo#FOCUS_INPUT 1437 * @see AccessibilityNodeInfo#FOCUS_ACCESSIBILITY 1438 */ 1439 public AccessibilityNodeInfo findFocus(int focus) { 1440 return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId, 1441 AccessibilityNodeInfo.ANY_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID, focus); 1442 } 1443 1444 /** 1445 * Gets the an {@link AccessibilityServiceInfo} describing this 1446 * {@link AccessibilityService}. This method is useful if one wants 1447 * to change some of the dynamically configurable properties at 1448 * runtime. 1449 * 1450 * @return The accessibility service info. 1451 * 1452 * @see AccessibilityServiceInfo 1453 */ 1454 public final AccessibilityServiceInfo getServiceInfo() { 1455 IAccessibilityServiceConnection connection = 1456 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); 1457 if (connection != null) { 1458 try { 1459 return connection.getServiceInfo(); 1460 } catch (RemoteException re) { 1461 Log.w(LOG_TAG, "Error while getting AccessibilityServiceInfo", re); 1462 re.rethrowFromSystemServer(); 1463 } 1464 } 1465 return null; 1466 } 1467 1468 /** 1469 * Sets the {@link AccessibilityServiceInfo} that describes this service. 1470 * <p> 1471 * Note: You can call this method any time but the info will be picked up after 1472 * the system has bound to this service and when this method is called thereafter. 1473 * 1474 * @param info The info. 1475 */ 1476 public final void setServiceInfo(AccessibilityServiceInfo info) { 1477 mInfo = info; 1478 sendServiceInfo(); 1479 } 1480 1481 /** 1482 * Sets the {@link AccessibilityServiceInfo} for this service if the latter is 1483 * properly set and there is an {@link IAccessibilityServiceConnection} to the 1484 * AccessibilityManagerService. 1485 */ 1486 private void sendServiceInfo() { 1487 IAccessibilityServiceConnection connection = 1488 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); 1489 if (mInfo != null && connection != null) { 1490 try { 1491 connection.setServiceInfo(mInfo); 1492 mInfo = null; 1493 AccessibilityInteractionClient.getInstance().clearCache(); 1494 } catch (RemoteException re) { 1495 Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re); 1496 re.rethrowFromSystemServer(); 1497 } 1498 } 1499 } 1500 1501 @Override 1502 public Object getSystemService(@ServiceName @NonNull String name) { 1503 if (getBaseContext() == null) { 1504 throw new IllegalStateException( 1505 "System services not available to Activities before onCreate()"); 1506 } 1507 1508 // Guarantee that we always return the same window manager instance. 1509 if (WINDOW_SERVICE.equals(name)) { 1510 if (mWindowManager == null) { 1511 mWindowManager = (WindowManager) getBaseContext().getSystemService(name); 1512 } 1513 return mWindowManager; 1514 } 1515 return super.getSystemService(name); 1516 } 1517 1518 /** 1519 * Implement to return the implementation of the internal accessibility 1520 * service interface. 1521 */ 1522 @Override 1523 public final IBinder onBind(Intent intent) { 1524 return new IAccessibilityServiceClientWrapper(this, getMainLooper(), new Callbacks() { 1525 @Override 1526 public void onServiceConnected() { 1527 AccessibilityService.this.dispatchServiceConnected(); 1528 } 1529 1530 @Override 1531 public void onInterrupt() { 1532 AccessibilityService.this.onInterrupt(); 1533 } 1534 1535 @Override 1536 public void onAccessibilityEvent(AccessibilityEvent event) { 1537 AccessibilityService.this.onAccessibilityEvent(event); 1538 } 1539 1540 @Override 1541 public void init(int connectionId, IBinder windowToken) { 1542 mConnectionId = connectionId; 1543 mWindowToken = windowToken; 1544 1545 // The client may have already obtained the window manager, so 1546 // update the default token on whatever manager we gave them. 1547 final WindowManagerImpl wm = (WindowManagerImpl) getSystemService(WINDOW_SERVICE); 1548 wm.setDefaultToken(windowToken); 1549 } 1550 1551 @Override 1552 public boolean onGesture(int gestureId) { 1553 return AccessibilityService.this.onGesture(gestureId); 1554 } 1555 1556 @Override 1557 public boolean onKeyEvent(KeyEvent event) { 1558 return AccessibilityService.this.onKeyEvent(event); 1559 } 1560 1561 @Override 1562 public void onMagnificationChanged(@NonNull Region region, 1563 float scale, float centerX, float centerY) { 1564 AccessibilityService.this.onMagnificationChanged(region, scale, centerX, centerY); 1565 } 1566 1567 @Override 1568 public void onSoftKeyboardShowModeChanged(int showMode) { 1569 AccessibilityService.this.onSoftKeyboardShowModeChanged(showMode); 1570 } 1571 1572 @Override 1573 public void onPerformGestureResult(int sequence, boolean completedSuccessfully) { 1574 AccessibilityService.this.onPerformGestureResult(sequence, completedSuccessfully); 1575 } 1576 1577 @Override 1578 public void onFingerprintCapturingGesturesChanged(boolean active) { 1579 AccessibilityService.this.onFingerprintCapturingGesturesChanged(active); 1580 } 1581 1582 @Override 1583 public void onFingerprintGesture(int gesture) { 1584 AccessibilityService.this.onFingerprintGesture(gesture); 1585 } 1586 1587 @Override 1588 public void onAccessibilityButtonClicked() { 1589 AccessibilityService.this.onAccessibilityButtonClicked(); 1590 } 1591 1592 @Override 1593 public void onAccessibilityButtonAvailabilityChanged(boolean available) { 1594 AccessibilityService.this.onAccessibilityButtonAvailabilityChanged(available); 1595 } 1596 }); 1597 } 1598 1599 /** 1600 * Implements the internal {@link IAccessibilityServiceClient} interface to convert 1601 * incoming calls to it back to calls on an {@link AccessibilityService}. 1602 * 1603 * @hide 1604 */ 1605 public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub 1606 implements HandlerCaller.Callback { 1607 private static final int DO_INIT = 1; 1608 private static final int DO_ON_INTERRUPT = 2; 1609 private static final int DO_ON_ACCESSIBILITY_EVENT = 3; 1610 private static final int DO_ON_GESTURE = 4; 1611 private static final int DO_CLEAR_ACCESSIBILITY_CACHE = 5; 1612 private static final int DO_ON_KEY_EVENT = 6; 1613 private static final int DO_ON_MAGNIFICATION_CHANGED = 7; 1614 private static final int DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED = 8; 1615 private static final int DO_GESTURE_COMPLETE = 9; 1616 private static final int DO_ON_FINGERPRINT_ACTIVE_CHANGED = 10; 1617 private static final int DO_ON_FINGERPRINT_GESTURE = 11; 1618 private static final int DO_ACCESSIBILITY_BUTTON_CLICKED = 12; 1619 private static final int DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED = 13; 1620 1621 private final HandlerCaller mCaller; 1622 1623 private final Callbacks mCallback; 1624 1625 private int mConnectionId; 1626 1627 public IAccessibilityServiceClientWrapper(Context context, Looper looper, 1628 Callbacks callback) { 1629 mCallback = callback; 1630 mCaller = new HandlerCaller(context, looper, this, true /*asyncHandler*/); 1631 } 1632 1633 public void init(IAccessibilityServiceConnection connection, int connectionId, 1634 IBinder windowToken) { 1635 Message message = mCaller.obtainMessageIOO(DO_INIT, connectionId, 1636 connection, windowToken); 1637 mCaller.sendMessage(message); 1638 } 1639 1640 public void onInterrupt() { 1641 Message message = mCaller.obtainMessage(DO_ON_INTERRUPT); 1642 mCaller.sendMessage(message); 1643 } 1644 1645 public void onAccessibilityEvent(AccessibilityEvent event, boolean serviceWantsEvent) { 1646 Message message = mCaller.obtainMessageBO( 1647 DO_ON_ACCESSIBILITY_EVENT, serviceWantsEvent, event); 1648 mCaller.sendMessage(message); 1649 } 1650 1651 public void onGesture(int gestureId) { 1652 Message message = mCaller.obtainMessageI(DO_ON_GESTURE, gestureId); 1653 mCaller.sendMessage(message); 1654 } 1655 1656 public void clearAccessibilityCache() { 1657 Message message = mCaller.obtainMessage(DO_CLEAR_ACCESSIBILITY_CACHE); 1658 mCaller.sendMessage(message); 1659 } 1660 1661 @Override 1662 public void onKeyEvent(KeyEvent event, int sequence) { 1663 Message message = mCaller.obtainMessageIO(DO_ON_KEY_EVENT, sequence, event); 1664 mCaller.sendMessage(message); 1665 } 1666 1667 public void onMagnificationChanged(@NonNull Region region, 1668 float scale, float centerX, float centerY) { 1669 final SomeArgs args = SomeArgs.obtain(); 1670 args.arg1 = region; 1671 args.arg2 = scale; 1672 args.arg3 = centerX; 1673 args.arg4 = centerY; 1674 1675 final Message message = mCaller.obtainMessageO(DO_ON_MAGNIFICATION_CHANGED, args); 1676 mCaller.sendMessage(message); 1677 } 1678 1679 public void onSoftKeyboardShowModeChanged(int showMode) { 1680 final Message message = 1681 mCaller.obtainMessageI(DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED, showMode); 1682 mCaller.sendMessage(message); 1683 } 1684 1685 public void onPerformGestureResult(int sequence, boolean successfully) { 1686 Message message = mCaller.obtainMessageII(DO_GESTURE_COMPLETE, sequence, 1687 successfully ? 1 : 0); 1688 mCaller.sendMessage(message); 1689 } 1690 1691 public void onFingerprintCapturingGesturesChanged(boolean active) { 1692 mCaller.sendMessage(mCaller.obtainMessageI( 1693 DO_ON_FINGERPRINT_ACTIVE_CHANGED, active ? 1 : 0)); 1694 } 1695 1696 public void onFingerprintGesture(int gesture) { 1697 mCaller.sendMessage(mCaller.obtainMessageI(DO_ON_FINGERPRINT_GESTURE, gesture)); 1698 } 1699 1700 public void onAccessibilityButtonClicked() { 1701 final Message message = mCaller.obtainMessage(DO_ACCESSIBILITY_BUTTON_CLICKED); 1702 mCaller.sendMessage(message); 1703 } 1704 1705 public void onAccessibilityButtonAvailabilityChanged(boolean available) { 1706 final Message message = mCaller.obtainMessageI( 1707 DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED, (available ? 1 : 0)); 1708 mCaller.sendMessage(message); 1709 } 1710 1711 @Override 1712 public void executeMessage(Message message) { 1713 switch (message.what) { 1714 case DO_ON_ACCESSIBILITY_EVENT: { 1715 AccessibilityEvent event = (AccessibilityEvent) message.obj; 1716 boolean serviceWantsEvent = message.arg1 != 0; 1717 if (event != null) { 1718 // Send the event to AccessibilityCache via AccessibilityInteractionClient 1719 AccessibilityInteractionClient.getInstance().onAccessibilityEvent(event); 1720 if (serviceWantsEvent) { 1721 // Send the event to AccessibilityService 1722 mCallback.onAccessibilityEvent(event); 1723 } 1724 // Make sure the event is recycled. 1725 try { 1726 event.recycle(); 1727 } catch (IllegalStateException ise) { 1728 /* ignore - best effort */ 1729 } 1730 } 1731 } return; 1732 1733 case DO_ON_INTERRUPT: { 1734 mCallback.onInterrupt(); 1735 } return; 1736 1737 case DO_INIT: { 1738 mConnectionId = message.arg1; 1739 SomeArgs args = (SomeArgs) message.obj; 1740 IAccessibilityServiceConnection connection = 1741 (IAccessibilityServiceConnection) args.arg1; 1742 IBinder windowToken = (IBinder) args.arg2; 1743 args.recycle(); 1744 if (connection != null) { 1745 AccessibilityInteractionClient.getInstance().addConnection(mConnectionId, 1746 connection); 1747 mCallback.init(mConnectionId, windowToken); 1748 mCallback.onServiceConnected(); 1749 } else { 1750 AccessibilityInteractionClient.getInstance().removeConnection( 1751 mConnectionId); 1752 mConnectionId = AccessibilityInteractionClient.NO_ID; 1753 AccessibilityInteractionClient.getInstance().clearCache(); 1754 mCallback.init(AccessibilityInteractionClient.NO_ID, null); 1755 } 1756 } return; 1757 1758 case DO_ON_GESTURE: { 1759 final int gestureId = message.arg1; 1760 mCallback.onGesture(gestureId); 1761 } return; 1762 1763 case DO_CLEAR_ACCESSIBILITY_CACHE: { 1764 AccessibilityInteractionClient.getInstance().clearCache(); 1765 } return; 1766 1767 case DO_ON_KEY_EVENT: { 1768 KeyEvent event = (KeyEvent) message.obj; 1769 try { 1770 IAccessibilityServiceConnection connection = AccessibilityInteractionClient 1771 .getInstance().getConnection(mConnectionId); 1772 if (connection != null) { 1773 final boolean result = mCallback.onKeyEvent(event); 1774 final int sequence = message.arg1; 1775 try { 1776 connection.setOnKeyEventResult(result, sequence); 1777 } catch (RemoteException re) { 1778 /* ignore */ 1779 } 1780 } 1781 } finally { 1782 // Make sure the event is recycled. 1783 try { 1784 event.recycle(); 1785 } catch (IllegalStateException ise) { 1786 /* ignore - best effort */ 1787 } 1788 } 1789 } return; 1790 1791 case DO_ON_MAGNIFICATION_CHANGED: { 1792 final SomeArgs args = (SomeArgs) message.obj; 1793 final Region region = (Region) args.arg1; 1794 final float scale = (float) args.arg2; 1795 final float centerX = (float) args.arg3; 1796 final float centerY = (float) args.arg4; 1797 mCallback.onMagnificationChanged(region, scale, centerX, centerY); 1798 } return; 1799 1800 case DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED: { 1801 final int showMode = (int) message.arg1; 1802 mCallback.onSoftKeyboardShowModeChanged(showMode); 1803 } return; 1804 1805 case DO_GESTURE_COMPLETE: { 1806 final boolean successfully = message.arg2 == 1; 1807 mCallback.onPerformGestureResult(message.arg1, successfully); 1808 } return; 1809 case DO_ON_FINGERPRINT_ACTIVE_CHANGED: { 1810 mCallback.onFingerprintCapturingGesturesChanged(message.arg1 == 1); 1811 } return; 1812 case DO_ON_FINGERPRINT_GESTURE: { 1813 mCallback.onFingerprintGesture(message.arg1); 1814 } return; 1815 1816 case (DO_ACCESSIBILITY_BUTTON_CLICKED): { 1817 mCallback.onAccessibilityButtonClicked(); 1818 } return; 1819 1820 case (DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED): { 1821 final boolean available = (message.arg1 != 0); 1822 mCallback.onAccessibilityButtonAvailabilityChanged(available); 1823 } return; 1824 1825 default : 1826 Log.w(LOG_TAG, "Unknown message type " + message.what); 1827 } 1828 } 1829 } 1830 1831 /** 1832 * Class used to report status of dispatched gestures 1833 */ 1834 public static abstract class GestureResultCallback { 1835 /** Called when the gesture has completed successfully 1836 * 1837 * @param gestureDescription The description of the gesture that completed. 1838 */ 1839 public void onCompleted(GestureDescription gestureDescription) { 1840 } 1841 1842 /** Called when the gesture was cancelled 1843 * 1844 * @param gestureDescription The description of the gesture that was cancelled. 1845 */ 1846 public void onCancelled(GestureDescription gestureDescription) { 1847 } 1848 } 1849 1850 /* Object to keep track of gesture result callbacks */ 1851 private static class GestureResultCallbackInfo { 1852 GestureDescription gestureDescription; 1853 GestureResultCallback callback; 1854 Handler handler; 1855 1856 GestureResultCallbackInfo(GestureDescription gestureDescription, 1857 GestureResultCallback callback, Handler handler) { 1858 this.gestureDescription = gestureDescription; 1859 this.callback = callback; 1860 this.handler = handler; 1861 } 1862 } 1863} 1864