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