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