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