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 17 package android.accessibilityservice; 18 19 import android.accessibilityservice.GestureDescription.MotionEventGenerator; 20 import android.annotation.IntDef; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.app.Service; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.pm.ParceledListSlice; 27 import android.graphics.Region; 28 import android.os.Handler; 29 import android.os.IBinder; 30 import android.os.Looper; 31 import android.os.Message; 32 import android.os.RemoteException; 33 import android.provider.Settings; 34 import android.util.ArrayMap; 35 import android.util.Log; 36 import android.util.Pair; 37 import android.util.Slog; 38 import android.util.SparseArray; 39 import android.view.KeyEvent; 40 import android.view.MotionEvent; 41 import android.view.WindowManager; 42 import android.view.WindowManagerImpl; 43 import android.view.accessibility.AccessibilityEvent; 44 import android.view.accessibility.AccessibilityInteractionClient; 45 import android.view.accessibility.AccessibilityNodeInfo; 46 import android.view.accessibility.AccessibilityWindowInfo; 47 48 import com.android.internal.os.HandlerCaller; 49 import com.android.internal.os.SomeArgs; 50 51 import java.lang.annotation.Retention; 52 import java.lang.annotation.RetentionPolicy; 53 import 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 */ 219 public 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 * The gesture will be dispatched as if it were performed directly on the screen by a user, so 604 * the events may be affected by features such as magnification and explore by touch. 605 * </p> 606 * <p> 607 * <strong>Note:</strong> In order to dispatch gestures, your service 608 * must declare the capability by setting the 609 * {@link android.R.styleable#AccessibilityService_canPerformGestures} 610 * property in its meta-data. For more information, see 611 * {@link #SERVICE_META_DATA}. 612 * </p> 613 * 614 * @param gesture The gesture to dispatch 615 * @param callback The object to call back when the status of the gesture is known. If 616 * {@code null}, no status is reported. 617 * @param handler The handler on which to call back the {@code callback} object. If 618 * {@code null}, the object is called back on the service's main thread. 619 * 620 * @return {@code true} if the gesture is dispatched, {@code false} if not. 621 */ 622 public final boolean dispatchGesture(@NonNull GestureDescription gesture, 623 @Nullable GestureResultCallback callback, 624 @Nullable Handler handler) { 625 final IAccessibilityServiceConnection connection = 626 AccessibilityInteractionClient.getInstance().getConnection( 627 mConnectionId); 628 if (connection == null) { 629 return false; 630 } 631 List<GestureDescription.GestureStep> steps = 632 MotionEventGenerator.getGestureStepsFromGestureDescription(gesture, 100); 633 try { 634 synchronized (mLock) { 635 mGestureStatusCallbackSequence++; 636 if (callback != null) { 637 if (mGestureStatusCallbackInfos == null) { 638 mGestureStatusCallbackInfos = new SparseArray<>(); 639 } 640 GestureResultCallbackInfo callbackInfo = new GestureResultCallbackInfo(gesture, 641 callback, handler); 642 mGestureStatusCallbackInfos.put(mGestureStatusCallbackSequence, callbackInfo); 643 } 644 connection.sendGesture(mGestureStatusCallbackSequence, 645 new ParceledListSlice<>(steps)); 646 } 647 } catch (RemoteException re) { 648 throw new RuntimeException(re); 649 } 650 return true; 651 } 652 653 void onPerformGestureResult(int sequence, final boolean completedSuccessfully) { 654 if (mGestureStatusCallbackInfos == null) { 655 return; 656 } 657 GestureResultCallbackInfo callbackInfo; 658 synchronized (mLock) { 659 callbackInfo = mGestureStatusCallbackInfos.get(sequence); 660 } 661 final GestureResultCallbackInfo finalCallbackInfo = callbackInfo; 662 if ((callbackInfo != null) && (callbackInfo.gestureDescription != null) 663 && (callbackInfo.callback != null)) { 664 if (callbackInfo.handler != null) { 665 callbackInfo.handler.post(new Runnable() { 666 @Override 667 public void run() { 668 if (completedSuccessfully) { 669 finalCallbackInfo.callback 670 .onCompleted(finalCallbackInfo.gestureDescription); 671 } else { 672 finalCallbackInfo.callback 673 .onCancelled(finalCallbackInfo.gestureDescription); 674 } 675 } 676 }); 677 return; 678 } 679 if (completedSuccessfully) { 680 callbackInfo.callback.onCompleted(callbackInfo.gestureDescription); 681 } else { 682 callbackInfo.callback.onCancelled(callbackInfo.gestureDescription); 683 } 684 } 685 } 686 687 private void onMagnificationChanged(@NonNull Region region, float scale, 688 float centerX, float centerY) { 689 if (mMagnificationController != null) { 690 mMagnificationController.dispatchMagnificationChanged( 691 region, scale, centerX, centerY); 692 } 693 } 694 695 /** 696 * Used to control and query the state of display magnification. 697 */ 698 public static final class MagnificationController { 699 private final AccessibilityService mService; 700 701 /** 702 * Map of listeners to their handlers. Lazily created when adding the 703 * first magnification listener. 704 */ 705 private ArrayMap<OnMagnificationChangedListener, Handler> mListeners; 706 private final Object mLock; 707 708 MagnificationController(@NonNull AccessibilityService service, @NonNull Object lock) { 709 mService = service; 710 mLock = lock; 711 } 712 713 /** 714 * Called when the service is connected. 715 */ 716 void onServiceConnected() { 717 synchronized (mLock) { 718 if (mListeners != null && !mListeners.isEmpty()) { 719 setMagnificationCallbackEnabled(true); 720 } 721 } 722 } 723 724 /** 725 * Adds the specified change listener to the list of magnification 726 * change listeners. The callback will occur on the service's main 727 * thread. 728 * 729 * @param listener the listener to add, must be non-{@code null} 730 */ 731 public void addListener(@NonNull OnMagnificationChangedListener listener) { 732 addListener(listener, null); 733 } 734 735 /** 736 * Adds the specified change listener to the list of magnification 737 * change listeners. The callback will occur on the specified 738 * {@link Handler}'s thread, or on the service's main thread if the 739 * handler is {@code null}. 740 * 741 * @param listener the listener to add, must be non-null 742 * @param handler the handler on which the callback should execute, or 743 * {@code null} to execute on the service's main thread 744 */ 745 public void addListener(@NonNull OnMagnificationChangedListener listener, 746 @Nullable Handler handler) { 747 synchronized (mLock) { 748 if (mListeners == null) { 749 mListeners = new ArrayMap<>(); 750 } 751 752 final boolean shouldEnableCallback = mListeners.isEmpty(); 753 mListeners.put(listener, handler); 754 755 if (shouldEnableCallback) { 756 // This may fail if the service is not connected yet, but if we 757 // still have listeners when it connects then we can try again. 758 setMagnificationCallbackEnabled(true); 759 } 760 } 761 } 762 763 /** 764 * Removes all instances of the specified change listener from the list 765 * of magnification change listeners. 766 * 767 * @param listener the listener to remove, must be non-null 768 * @return {@code true} if at least one instance of the listener was 769 * removed 770 */ 771 public boolean removeListener(@NonNull OnMagnificationChangedListener listener) { 772 if (mListeners == null) { 773 return false; 774 } 775 776 synchronized (mLock) { 777 final int keyIndex = mListeners.indexOfKey(listener); 778 final boolean hasKey = keyIndex >= 0; 779 if (hasKey) { 780 mListeners.removeAt(keyIndex); 781 } 782 783 if (hasKey && mListeners.isEmpty()) { 784 // We just removed the last listener, so we don't need 785 // callbacks from the service anymore. 786 setMagnificationCallbackEnabled(false); 787 } 788 789 return hasKey; 790 } 791 } 792 793 private void setMagnificationCallbackEnabled(boolean enabled) { 794 final IAccessibilityServiceConnection connection = 795 AccessibilityInteractionClient.getInstance().getConnection( 796 mService.mConnectionId); 797 if (connection != null) { 798 try { 799 connection.setMagnificationCallbackEnabled(enabled); 800 } catch (RemoteException re) { 801 throw new RuntimeException(re); 802 } 803 } 804 } 805 806 /** 807 * Dispatches magnification changes to any registered listeners. This 808 * should be called on the service's main thread. 809 */ 810 void dispatchMagnificationChanged(final @NonNull Region region, final float scale, 811 final float centerX, final float centerY) { 812 final ArrayMap<OnMagnificationChangedListener, Handler> entries; 813 synchronized (mLock) { 814 if (mListeners == null || mListeners.isEmpty()) { 815 Slog.d(LOG_TAG, "Received magnification changed " 816 + "callback with no listeners registered!"); 817 setMagnificationCallbackEnabled(false); 818 return; 819 } 820 821 // Listeners may remove themselves. Perform a shallow copy to avoid concurrent 822 // modification. 823 entries = new ArrayMap<>(mListeners); 824 } 825 826 for (int i = 0, count = entries.size(); i < count; i++) { 827 final OnMagnificationChangedListener listener = entries.keyAt(i); 828 final Handler handler = entries.valueAt(i); 829 if (handler != null) { 830 handler.post(new Runnable() { 831 @Override 832 public void run() { 833 listener.onMagnificationChanged(MagnificationController.this, 834 region, scale, centerX, centerY); 835 } 836 }); 837 } else { 838 // We're already on the main thread, just run the listener. 839 listener.onMagnificationChanged(this, region, scale, centerX, centerY); 840 } 841 } 842 } 843 844 /** 845 * Returns the current magnification scale. 846 * <p> 847 * <strong>Note:</strong> If the service is not yet connected (e.g. 848 * {@link AccessibilityService#onServiceConnected()} has not yet been 849 * called) or the service has been disconnected, this method will 850 * return a default value of {@code 1.0f}. 851 * 852 * @return the current magnification scale 853 */ 854 public float getScale() { 855 final IAccessibilityServiceConnection connection = 856 AccessibilityInteractionClient.getInstance().getConnection( 857 mService.mConnectionId); 858 if (connection != null) { 859 try { 860 return connection.getMagnificationScale(); 861 } catch (RemoteException re) { 862 Log.w(LOG_TAG, "Failed to obtain scale", re); 863 re.rethrowFromSystemServer(); 864 } 865 } 866 return 1.0f; 867 } 868 869 /** 870 * Returns the unscaled screen-relative X coordinate of the focal 871 * center of the magnified region. This is the point around which 872 * zooming occurs and is guaranteed to lie within the magnified 873 * region. 874 * <p> 875 * <strong>Note:</strong> If the service is not yet connected (e.g. 876 * {@link AccessibilityService#onServiceConnected()} has not yet been 877 * called) or the service has been disconnected, this method will 878 * return a default value of {@code 0.0f}. 879 * 880 * @return the unscaled screen-relative X coordinate of the center of 881 * the magnified region 882 */ 883 public float getCenterX() { 884 final IAccessibilityServiceConnection connection = 885 AccessibilityInteractionClient.getInstance().getConnection( 886 mService.mConnectionId); 887 if (connection != null) { 888 try { 889 return connection.getMagnificationCenterX(); 890 } catch (RemoteException re) { 891 Log.w(LOG_TAG, "Failed to obtain center X", re); 892 re.rethrowFromSystemServer(); 893 } 894 } 895 return 0.0f; 896 } 897 898 /** 899 * Returns the unscaled screen-relative Y coordinate of the focal 900 * center of the magnified region. This is the point around which 901 * zooming occurs and is guaranteed to lie within the magnified 902 * region. 903 * <p> 904 * <strong>Note:</strong> If the service is not yet connected (e.g. 905 * {@link AccessibilityService#onServiceConnected()} has not yet been 906 * called) or the service has been disconnected, this method will 907 * return a default value of {@code 0.0f}. 908 * 909 * @return the unscaled screen-relative Y coordinate of the center of 910 * the magnified region 911 */ 912 public float getCenterY() { 913 final IAccessibilityServiceConnection connection = 914 AccessibilityInteractionClient.getInstance().getConnection( 915 mService.mConnectionId); 916 if (connection != null) { 917 try { 918 return connection.getMagnificationCenterY(); 919 } catch (RemoteException re) { 920 Log.w(LOG_TAG, "Failed to obtain center Y", re); 921 re.rethrowFromSystemServer(); 922 } 923 } 924 return 0.0f; 925 } 926 927 /** 928 * Returns the region of the screen currently active for magnification. Changes to 929 * magnification scale and center only affect this portion of the screen. The rest of the 930 * screen, for example input methods, cannot be magnified. This region is relative to the 931 * unscaled screen and is independent of the scale and center point. 932 * <p> 933 * The returned region will be empty if magnification is not active. Magnification is active 934 * if magnification gestures are enabled or if a service is running that can control 935 * magnification. 936 * <p> 937 * <strong>Note:</strong> If the service is not yet connected (e.g. 938 * {@link AccessibilityService#onServiceConnected()} has not yet been 939 * called) or the service has been disconnected, this method will 940 * return an empty region. 941 * 942 * @return the region of the screen currently active for magnification, or an empty region 943 * if magnification is not active. 944 */ 945 @NonNull 946 public Region getMagnificationRegion() { 947 final IAccessibilityServiceConnection connection = 948 AccessibilityInteractionClient.getInstance().getConnection( 949 mService.mConnectionId); 950 if (connection != null) { 951 try { 952 return connection.getMagnificationRegion(); 953 } catch (RemoteException re) { 954 Log.w(LOG_TAG, "Failed to obtain magnified region", re); 955 re.rethrowFromSystemServer(); 956 } 957 } 958 return Region.obtain(); 959 } 960 961 /** 962 * Resets magnification scale and center to their default (e.g. no 963 * magnification) values. 964 * <p> 965 * <strong>Note:</strong> If the service is not yet connected (e.g. 966 * {@link AccessibilityService#onServiceConnected()} has not yet been 967 * called) or the service has been disconnected, this method will have 968 * no effect and return {@code false}. 969 * 970 * @param animate {@code true} to animate from the current scale and 971 * center or {@code false} to reset the scale and center 972 * immediately 973 * @return {@code true} on success, {@code false} on failure 974 */ 975 public boolean reset(boolean animate) { 976 final IAccessibilityServiceConnection connection = 977 AccessibilityInteractionClient.getInstance().getConnection( 978 mService.mConnectionId); 979 if (connection != null) { 980 try { 981 return connection.resetMagnification(animate); 982 } catch (RemoteException re) { 983 Log.w(LOG_TAG, "Failed to reset", re); 984 re.rethrowFromSystemServer(); 985 } 986 } 987 return false; 988 } 989 990 /** 991 * Sets the magnification scale. 992 * <p> 993 * <strong>Note:</strong> If the service is not yet connected (e.g. 994 * {@link AccessibilityService#onServiceConnected()} has not yet been 995 * called) or the service has been disconnected, this method will have 996 * no effect and return {@code false}. 997 * 998 * @param scale the magnification scale to set, must be >= 1 and <= 5 999 * @param animate {@code true} to animate from the current scale or 1000 * {@code false} to set the scale immediately 1001 * @return {@code true} on success, {@code false} on failure 1002 */ 1003 public boolean setScale(float scale, boolean animate) { 1004 final IAccessibilityServiceConnection connection = 1005 AccessibilityInteractionClient.getInstance().getConnection( 1006 mService.mConnectionId); 1007 if (connection != null) { 1008 try { 1009 return connection.setMagnificationScaleAndCenter( 1010 scale, Float.NaN, Float.NaN, animate); 1011 } catch (RemoteException re) { 1012 Log.w(LOG_TAG, "Failed to set scale", re); 1013 re.rethrowFromSystemServer(); 1014 } 1015 } 1016 return false; 1017 } 1018 1019 /** 1020 * Sets the center of the magnified viewport. 1021 * <p> 1022 * <strong>Note:</strong> If the service is not yet connected (e.g. 1023 * {@link AccessibilityService#onServiceConnected()} has not yet been 1024 * called) or the service has been disconnected, this method will have 1025 * no effect and return {@code false}. 1026 * 1027 * @param centerX the unscaled screen-relative X coordinate on which to 1028 * center the viewport 1029 * @param centerY the unscaled screen-relative Y coordinate on which to 1030 * center the viewport 1031 * @param animate {@code true} to animate from the current viewport 1032 * center or {@code false} to set the center immediately 1033 * @return {@code true} on success, {@code false} on failure 1034 */ 1035 public boolean setCenter(float centerX, float centerY, boolean animate) { 1036 final IAccessibilityServiceConnection connection = 1037 AccessibilityInteractionClient.getInstance().getConnection( 1038 mService.mConnectionId); 1039 if (connection != null) { 1040 try { 1041 return connection.setMagnificationScaleAndCenter( 1042 Float.NaN, centerX, centerY, animate); 1043 } catch (RemoteException re) { 1044 Log.w(LOG_TAG, "Failed to set center", re); 1045 re.rethrowFromSystemServer(); 1046 } 1047 } 1048 return false; 1049 } 1050 1051 /** 1052 * Listener for changes in the state of magnification. 1053 */ 1054 public interface OnMagnificationChangedListener { 1055 /** 1056 * Called when the magnified region, scale, or center changes. 1057 * 1058 * @param controller the magnification controller 1059 * @param region the magnification region 1060 * @param scale the new scale 1061 * @param centerX the new X coordinate, in unscaled coordinates, around which 1062 * magnification is focused 1063 * @param centerY the new Y coordinate, in unscaled coordinates, around which 1064 * magnification is focused 1065 */ 1066 void onMagnificationChanged(@NonNull MagnificationController controller, 1067 @NonNull Region region, float scale, float centerX, float centerY); 1068 } 1069 } 1070 1071 /** 1072 * Returns the soft keyboard controller, which may be used to query and modify the soft keyboard 1073 * show mode. 1074 * 1075 * @return the soft keyboard controller 1076 */ 1077 @NonNull 1078 public final SoftKeyboardController getSoftKeyboardController() { 1079 synchronized (mLock) { 1080 if (mSoftKeyboardController == null) { 1081 mSoftKeyboardController = new SoftKeyboardController(this, mLock); 1082 } 1083 return mSoftKeyboardController; 1084 } 1085 } 1086 1087 private void onSoftKeyboardShowModeChanged(int showMode) { 1088 if (mSoftKeyboardController != null) { 1089 mSoftKeyboardController.dispatchSoftKeyboardShowModeChanged(showMode); 1090 } 1091 } 1092 1093 /** 1094 * Used to control and query the soft keyboard show mode. 1095 */ 1096 public static final class SoftKeyboardController { 1097 private final AccessibilityService mService; 1098 1099 /** 1100 * Map of listeners to their handlers. Lazily created when adding the first 1101 * soft keyboard change listener. 1102 */ 1103 private ArrayMap<OnShowModeChangedListener, Handler> mListeners; 1104 private final Object mLock; 1105 1106 SoftKeyboardController(@NonNull AccessibilityService service, @NonNull Object lock) { 1107 mService = service; 1108 mLock = lock; 1109 } 1110 1111 /** 1112 * Called when the service is connected. 1113 */ 1114 void onServiceConnected() { 1115 synchronized(mLock) { 1116 if (mListeners != null && !mListeners.isEmpty()) { 1117 setSoftKeyboardCallbackEnabled(true); 1118 } 1119 } 1120 } 1121 1122 /** 1123 * Adds the specified change listener to the list of show mode change listeners. The 1124 * callback will occur on the service's main thread. Listener is not called on registration. 1125 */ 1126 public void addOnShowModeChangedListener(@NonNull OnShowModeChangedListener listener) { 1127 addOnShowModeChangedListener(listener, null); 1128 } 1129 1130 /** 1131 * Adds the specified change listener to the list of soft keyboard show mode change 1132 * listeners. The callback will occur on the specified {@link Handler}'s thread, or on the 1133 * services's main thread if the handler is {@code null}. 1134 * 1135 * @param listener the listener to add, must be non-null 1136 * @param handler the handler on which to callback should execute, or {@code null} to 1137 * execute on the service's main thread 1138 */ 1139 public void addOnShowModeChangedListener(@NonNull OnShowModeChangedListener listener, 1140 @Nullable Handler handler) { 1141 synchronized (mLock) { 1142 if (mListeners == null) { 1143 mListeners = new ArrayMap<>(); 1144 } 1145 1146 final boolean shouldEnableCallback = mListeners.isEmpty(); 1147 mListeners.put(listener, handler); 1148 1149 if (shouldEnableCallback) { 1150 // This may fail if the service is not connected yet, but if we still have 1151 // listeners when it connects, we can try again. 1152 setSoftKeyboardCallbackEnabled(true); 1153 } 1154 } 1155 } 1156 1157 /** 1158 * Removes all instances of the specified change listener from the list of magnification 1159 * change listeners. 1160 * 1161 * @param listener the listener to remove, must be non-null 1162 * @return {@code true} if at least one instance of the listener was removed 1163 */ 1164 public boolean removeOnShowModeChangedListener(@NonNull OnShowModeChangedListener listener) { 1165 if (mListeners == null) { 1166 return false; 1167 } 1168 1169 synchronized (mLock) { 1170 final int keyIndex = mListeners.indexOfKey(listener); 1171 final boolean hasKey = keyIndex >= 0; 1172 if (hasKey) { 1173 mListeners.removeAt(keyIndex); 1174 } 1175 1176 if (hasKey && mListeners.isEmpty()) { 1177 // We just removed the last listener, so we don't need callbacks from the 1178 // service anymore. 1179 setSoftKeyboardCallbackEnabled(false); 1180 } 1181 1182 return hasKey; 1183 } 1184 } 1185 1186 private void setSoftKeyboardCallbackEnabled(boolean enabled) { 1187 final IAccessibilityServiceConnection connection = 1188 AccessibilityInteractionClient.getInstance().getConnection( 1189 mService.mConnectionId); 1190 if (connection != null) { 1191 try { 1192 connection.setSoftKeyboardCallbackEnabled(enabled); 1193 } catch (RemoteException re) { 1194 throw new RuntimeException(re); 1195 } 1196 } 1197 } 1198 1199 /** 1200 * Dispatches the soft keyboard show mode change to any registered listeners. This should 1201 * be called on the service's main thread. 1202 */ 1203 void dispatchSoftKeyboardShowModeChanged(final int showMode) { 1204 final ArrayMap<OnShowModeChangedListener, Handler> entries; 1205 synchronized (mLock) { 1206 if (mListeners == null || mListeners.isEmpty()) { 1207 Slog.d(LOG_TAG, "Received soft keyboard show mode changed callback" 1208 + " with no listeners registered!"); 1209 setSoftKeyboardCallbackEnabled(false); 1210 return; 1211 } 1212 1213 // Listeners may remove themselves. Perform a shallow copy to avoid concurrent 1214 // modification. 1215 entries = new ArrayMap<>(mListeners); 1216 } 1217 1218 for (int i = 0, count = entries.size(); i < count; i++) { 1219 final OnShowModeChangedListener listener = entries.keyAt(i); 1220 final Handler handler = entries.valueAt(i); 1221 if (handler != null) { 1222 handler.post(new Runnable() { 1223 @Override 1224 public void run() { 1225 listener.onShowModeChanged(SoftKeyboardController.this, showMode); 1226 } 1227 }); 1228 } else { 1229 // We're already on the main thread, just run the listener. 1230 listener.onShowModeChanged(this, showMode); 1231 } 1232 } 1233 } 1234 1235 /** 1236 * Returns the show mode of the soft keyboard. 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. 1240 * 1241 * @return the current soft keyboard show mode 1242 */ 1243 @SoftKeyboardShowMode 1244 public int getShowMode() { 1245 try { 1246 return Settings.Secure.getInt(mService.getContentResolver(), 1247 Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE); 1248 } catch (Settings.SettingNotFoundException e) { 1249 Log.v(LOG_TAG, "Failed to obtain the soft keyboard mode", e); 1250 // The settings hasn't been changed yet, so it's value is null. Return the default. 1251 return 0; 1252 } 1253 } 1254 1255 /** 1256 * Sets the soft keyboard show mode. The default show mode is 1257 * {@code SHOW_MODE_AUTO}, where the soft keyboard is shown when a text input field is 1258 * focused. An AccessibilityService can also request the show mode 1259 * {@code SHOW_MODE_HIDDEN}, where the soft keyboard is never shown. The 1260 * The lastto this method will be honored, regardless of any previous calls (including those 1261 * made by other AccessibilityServices). 1262 * <p> 1263 * <strong>Note:</strong> If the service is not yet conected (e.g. 1264 * {@link AccessibilityService#onServiceConnected()} has not yet been called) or the 1265 * service has been disconnected, this method will hav no effect and return {@code false}. 1266 * 1267 * @param showMode the new show mode for the soft keyboard 1268 * @return {@code true} on success 1269 */ 1270 public boolean setShowMode(@SoftKeyboardShowMode int showMode) { 1271 final IAccessibilityServiceConnection connection = 1272 AccessibilityInteractionClient.getInstance().getConnection( 1273 mService.mConnectionId); 1274 if (connection != null) { 1275 try { 1276 return connection.setSoftKeyboardShowMode(showMode); 1277 } catch (RemoteException re) { 1278 Log.w(LOG_TAG, "Failed to set soft keyboard behavior", re); 1279 re.rethrowFromSystemServer(); 1280 } 1281 } 1282 return false; 1283 } 1284 1285 /** 1286 * Listener for changes in the soft keyboard show mode. 1287 */ 1288 public interface OnShowModeChangedListener { 1289 /** 1290 * Called when the soft keyboard behavior changes. The default show mode is 1291 * {@code SHOW_MODE_AUTO}, where the soft keyboard is shown when a text input field is 1292 * focused. An AccessibilityService can also request the show mode 1293 * {@code SHOW_MODE_HIDDEN}, where the soft keyboard is never shown. 1294 * 1295 * @param controller the soft keyboard controller 1296 * @param showMode the current soft keyboard show mode 1297 */ 1298 void onShowModeChanged(@NonNull SoftKeyboardController controller, 1299 @SoftKeyboardShowMode int showMode); 1300 } 1301 } 1302 1303 /** 1304 * Performs a global action. Such an action can be performed 1305 * at any moment regardless of the current application or user 1306 * location in that application. For example going back, going 1307 * home, opening recents, etc. 1308 * 1309 * @param action The action to perform. 1310 * @return Whether the action was successfully performed. 1311 * 1312 * @see #GLOBAL_ACTION_BACK 1313 * @see #GLOBAL_ACTION_HOME 1314 * @see #GLOBAL_ACTION_NOTIFICATIONS 1315 * @see #GLOBAL_ACTION_RECENTS 1316 */ 1317 public final boolean performGlobalAction(int action) { 1318 IAccessibilityServiceConnection connection = 1319 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); 1320 if (connection != null) { 1321 try { 1322 return connection.performGlobalAction(action); 1323 } catch (RemoteException re) { 1324 Log.w(LOG_TAG, "Error while calling performGlobalAction", re); 1325 re.rethrowFromSystemServer(); 1326 } 1327 } 1328 return false; 1329 } 1330 1331 /** 1332 * Find the view that has the specified focus type. The search is performed 1333 * across all windows. 1334 * <p> 1335 * <strong>Note:</strong> In order to access the windows your service has 1336 * to declare the capability to retrieve window content by setting the 1337 * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent} 1338 * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}. 1339 * Also the service has to opt-in to retrieve the interactive windows by 1340 * setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} 1341 * flag. Otherwise, the search will be performed only in the active window. 1342 * </p> 1343 * 1344 * @param focus The focus to find. One of {@link AccessibilityNodeInfo#FOCUS_INPUT} or 1345 * {@link AccessibilityNodeInfo#FOCUS_ACCESSIBILITY}. 1346 * @return The node info of the focused view or null. 1347 * 1348 * @see AccessibilityNodeInfo#FOCUS_INPUT 1349 * @see AccessibilityNodeInfo#FOCUS_ACCESSIBILITY 1350 */ 1351 public AccessibilityNodeInfo findFocus(int focus) { 1352 return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId, 1353 AccessibilityNodeInfo.ANY_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID, focus); 1354 } 1355 1356 /** 1357 * Gets the an {@link AccessibilityServiceInfo} describing this 1358 * {@link AccessibilityService}. This method is useful if one wants 1359 * to change some of the dynamically configurable properties at 1360 * runtime. 1361 * 1362 * @return The accessibility service info. 1363 * 1364 * @see AccessibilityServiceInfo 1365 */ 1366 public final AccessibilityServiceInfo getServiceInfo() { 1367 IAccessibilityServiceConnection connection = 1368 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); 1369 if (connection != null) { 1370 try { 1371 return connection.getServiceInfo(); 1372 } catch (RemoteException re) { 1373 Log.w(LOG_TAG, "Error while getting AccessibilityServiceInfo", re); 1374 re.rethrowFromSystemServer(); 1375 } 1376 } 1377 return null; 1378 } 1379 1380 /** 1381 * Sets the {@link AccessibilityServiceInfo} that describes this service. 1382 * <p> 1383 * Note: You can call this method any time but the info will be picked up after 1384 * the system has bound to this service and when this method is called thereafter. 1385 * 1386 * @param info The info. 1387 */ 1388 public final void setServiceInfo(AccessibilityServiceInfo info) { 1389 mInfo = info; 1390 sendServiceInfo(); 1391 } 1392 1393 /** 1394 * Sets the {@link AccessibilityServiceInfo} for this service if the latter is 1395 * properly set and there is an {@link IAccessibilityServiceConnection} to the 1396 * AccessibilityManagerService. 1397 */ 1398 private void sendServiceInfo() { 1399 IAccessibilityServiceConnection connection = 1400 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); 1401 if (mInfo != null && connection != null) { 1402 try { 1403 connection.setServiceInfo(mInfo); 1404 mInfo = null; 1405 AccessibilityInteractionClient.getInstance().clearCache(); 1406 } catch (RemoteException re) { 1407 Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re); 1408 re.rethrowFromSystemServer(); 1409 } 1410 } 1411 } 1412 1413 @Override 1414 public Object getSystemService(@ServiceName @NonNull String name) { 1415 if (getBaseContext() == null) { 1416 throw new IllegalStateException( 1417 "System services not available to Activities before onCreate()"); 1418 } 1419 1420 // Guarantee that we always return the same window manager instance. 1421 if (WINDOW_SERVICE.equals(name)) { 1422 if (mWindowManager == null) { 1423 mWindowManager = (WindowManager) getBaseContext().getSystemService(name); 1424 } 1425 return mWindowManager; 1426 } 1427 return super.getSystemService(name); 1428 } 1429 1430 /** 1431 * Implement to return the implementation of the internal accessibility 1432 * service interface. 1433 */ 1434 @Override 1435 public final IBinder onBind(Intent intent) { 1436 return new IAccessibilityServiceClientWrapper(this, getMainLooper(), new Callbacks() { 1437 @Override 1438 public void onServiceConnected() { 1439 AccessibilityService.this.dispatchServiceConnected(); 1440 } 1441 1442 @Override 1443 public void onInterrupt() { 1444 AccessibilityService.this.onInterrupt(); 1445 } 1446 1447 @Override 1448 public void onAccessibilityEvent(AccessibilityEvent event) { 1449 AccessibilityService.this.onAccessibilityEvent(event); 1450 } 1451 1452 @Override 1453 public void init(int connectionId, IBinder windowToken) { 1454 mConnectionId = connectionId; 1455 mWindowToken = windowToken; 1456 1457 // The client may have already obtained the window manager, so 1458 // update the default token on whatever manager we gave them. 1459 final WindowManagerImpl wm = (WindowManagerImpl) getSystemService(WINDOW_SERVICE); 1460 wm.setDefaultToken(windowToken); 1461 } 1462 1463 @Override 1464 public boolean onGesture(int gestureId) { 1465 return AccessibilityService.this.onGesture(gestureId); 1466 } 1467 1468 @Override 1469 public boolean onKeyEvent(KeyEvent event) { 1470 return AccessibilityService.this.onKeyEvent(event); 1471 } 1472 1473 @Override 1474 public void onMagnificationChanged(@NonNull Region region, 1475 float scale, float centerX, float centerY) { 1476 AccessibilityService.this.onMagnificationChanged(region, scale, centerX, centerY); 1477 } 1478 1479 @Override 1480 public void onSoftKeyboardShowModeChanged(int showMode) { 1481 AccessibilityService.this.onSoftKeyboardShowModeChanged(showMode); 1482 } 1483 1484 @Override 1485 public void onPerformGestureResult(int sequence, boolean completedSuccessfully) { 1486 AccessibilityService.this.onPerformGestureResult(sequence, completedSuccessfully); 1487 } 1488 }); 1489 } 1490 1491 /** 1492 * Implements the internal {@link IAccessibilityServiceClient} interface to convert 1493 * incoming calls to it back to calls on an {@link AccessibilityService}. 1494 * 1495 * @hide 1496 */ 1497 public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub 1498 implements HandlerCaller.Callback { 1499 private static final int DO_INIT = 1; 1500 private static final int DO_ON_INTERRUPT = 2; 1501 private static final int DO_ON_ACCESSIBILITY_EVENT = 3; 1502 private static final int DO_ON_GESTURE = 4; 1503 private static final int DO_CLEAR_ACCESSIBILITY_CACHE = 5; 1504 private static final int DO_ON_KEY_EVENT = 6; 1505 private static final int DO_ON_MAGNIFICATION_CHANGED = 7; 1506 private static final int DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED = 8; 1507 private static final int DO_GESTURE_COMPLETE = 9; 1508 1509 private final HandlerCaller mCaller; 1510 1511 private final Callbacks mCallback; 1512 1513 private int mConnectionId; 1514 1515 public IAccessibilityServiceClientWrapper(Context context, Looper looper, 1516 Callbacks callback) { 1517 mCallback = callback; 1518 mCaller = new HandlerCaller(context, looper, this, true /*asyncHandler*/); 1519 } 1520 1521 public void init(IAccessibilityServiceConnection connection, int connectionId, 1522 IBinder windowToken) { 1523 Message message = mCaller.obtainMessageIOO(DO_INIT, connectionId, 1524 connection, windowToken); 1525 mCaller.sendMessage(message); 1526 } 1527 1528 public void onInterrupt() { 1529 Message message = mCaller.obtainMessage(DO_ON_INTERRUPT); 1530 mCaller.sendMessage(message); 1531 } 1532 1533 public void onAccessibilityEvent(AccessibilityEvent event) { 1534 Message message = mCaller.obtainMessageO(DO_ON_ACCESSIBILITY_EVENT, event); 1535 mCaller.sendMessage(message); 1536 } 1537 1538 public void onGesture(int gestureId) { 1539 Message message = mCaller.obtainMessageI(DO_ON_GESTURE, gestureId); 1540 mCaller.sendMessage(message); 1541 } 1542 1543 public void clearAccessibilityCache() { 1544 Message message = mCaller.obtainMessage(DO_CLEAR_ACCESSIBILITY_CACHE); 1545 mCaller.sendMessage(message); 1546 } 1547 1548 @Override 1549 public void onKeyEvent(KeyEvent event, int sequence) { 1550 Message message = mCaller.obtainMessageIO(DO_ON_KEY_EVENT, sequence, event); 1551 mCaller.sendMessage(message); 1552 } 1553 1554 public void onMagnificationChanged(@NonNull Region region, 1555 float scale, float centerX, float centerY) { 1556 final SomeArgs args = SomeArgs.obtain(); 1557 args.arg1 = region; 1558 args.arg2 = scale; 1559 args.arg3 = centerX; 1560 args.arg4 = centerY; 1561 1562 final Message message = mCaller.obtainMessageO(DO_ON_MAGNIFICATION_CHANGED, args); 1563 mCaller.sendMessage(message); 1564 } 1565 1566 public void onSoftKeyboardShowModeChanged(int showMode) { 1567 final Message message = 1568 mCaller.obtainMessageI(DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED, showMode); 1569 mCaller.sendMessage(message); 1570 } 1571 1572 public void onPerformGestureResult(int sequence, boolean successfully) { 1573 Message message = mCaller.obtainMessageII(DO_GESTURE_COMPLETE, sequence, 1574 successfully ? 1 : 0); 1575 mCaller.sendMessage(message); 1576 } 1577 1578 @Override 1579 public void executeMessage(Message message) { 1580 switch (message.what) { 1581 case DO_ON_ACCESSIBILITY_EVENT: { 1582 AccessibilityEvent event = (AccessibilityEvent) message.obj; 1583 if (event != null) { 1584 AccessibilityInteractionClient.getInstance().onAccessibilityEvent(event); 1585 mCallback.onAccessibilityEvent(event); 1586 // Make sure the event is recycled. 1587 try { 1588 event.recycle(); 1589 } catch (IllegalStateException ise) { 1590 /* ignore - best effort */ 1591 } 1592 } 1593 } return; 1594 1595 case DO_ON_INTERRUPT: { 1596 mCallback.onInterrupt(); 1597 } return; 1598 1599 case DO_INIT: { 1600 mConnectionId = message.arg1; 1601 SomeArgs args = (SomeArgs) message.obj; 1602 IAccessibilityServiceConnection connection = 1603 (IAccessibilityServiceConnection) args.arg1; 1604 IBinder windowToken = (IBinder) args.arg2; 1605 args.recycle(); 1606 if (connection != null) { 1607 AccessibilityInteractionClient.getInstance().addConnection(mConnectionId, 1608 connection); 1609 mCallback.init(mConnectionId, windowToken); 1610 mCallback.onServiceConnected(); 1611 } else { 1612 AccessibilityInteractionClient.getInstance().removeConnection( 1613 mConnectionId); 1614 mConnectionId = AccessibilityInteractionClient.NO_ID; 1615 AccessibilityInteractionClient.getInstance().clearCache(); 1616 mCallback.init(AccessibilityInteractionClient.NO_ID, null); 1617 } 1618 } return; 1619 1620 case DO_ON_GESTURE: { 1621 final int gestureId = message.arg1; 1622 mCallback.onGesture(gestureId); 1623 } return; 1624 1625 case DO_CLEAR_ACCESSIBILITY_CACHE: { 1626 AccessibilityInteractionClient.getInstance().clearCache(); 1627 } return; 1628 1629 case DO_ON_KEY_EVENT: { 1630 KeyEvent event = (KeyEvent) message.obj; 1631 try { 1632 IAccessibilityServiceConnection connection = AccessibilityInteractionClient 1633 .getInstance().getConnection(mConnectionId); 1634 if (connection != null) { 1635 final boolean result = mCallback.onKeyEvent(event); 1636 final int sequence = message.arg1; 1637 try { 1638 connection.setOnKeyEventResult(result, sequence); 1639 } catch (RemoteException re) { 1640 /* ignore */ 1641 } 1642 } 1643 } finally { 1644 // Make sure the event is recycled. 1645 try { 1646 event.recycle(); 1647 } catch (IllegalStateException ise) { 1648 /* ignore - best effort */ 1649 } 1650 } 1651 } return; 1652 1653 case DO_ON_MAGNIFICATION_CHANGED: { 1654 final SomeArgs args = (SomeArgs) message.obj; 1655 final Region region = (Region) args.arg1; 1656 final float scale = (float) args.arg2; 1657 final float centerX = (float) args.arg3; 1658 final float centerY = (float) args.arg4; 1659 mCallback.onMagnificationChanged(region, scale, centerX, centerY); 1660 } return; 1661 1662 case DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED: { 1663 final int showMode = (int) message.arg1; 1664 mCallback.onSoftKeyboardShowModeChanged(showMode); 1665 } return; 1666 1667 case DO_GESTURE_COMPLETE: { 1668 final boolean successfully = message.arg2 == 1; 1669 mCallback.onPerformGestureResult(message.arg1, successfully); 1670 } return; 1671 1672 default : 1673 Log.w(LOG_TAG, "Unknown message type " + message.what); 1674 } 1675 } 1676 } 1677 1678 /** 1679 * Class used to report status of dispatched gestures 1680 */ 1681 public static abstract class GestureResultCallback { 1682 /** Called when the gesture has completed successfully 1683 * 1684 * @param gestureDescription The description of the gesture that completed. 1685 */ 1686 public void onCompleted(GestureDescription gestureDescription) { 1687 } 1688 1689 /** Called when the gesture was cancelled 1690 * 1691 * @param gestureDescription The description of the gesture that was cancelled. 1692 */ 1693 public void onCancelled(GestureDescription gestureDescription) { 1694 } 1695 } 1696 1697 /* Object to keep track of gesture result callbacks */ 1698 private static class GestureResultCallbackInfo { 1699 GestureDescription gestureDescription; 1700 GestureResultCallback callback; 1701 Handler handler; 1702 1703 GestureResultCallbackInfo(GestureDescription gestureDescription, 1704 GestureResultCallback callback, Handler handler) { 1705 this.gestureDescription = gestureDescription; 1706 this.callback = callback; 1707 this.handler = handler; 1708 } 1709 } 1710 } 1711