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