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