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