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