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