AccessibilityService.java revision a6b64f5099b7be6e8384958d8bcddb97bb06ec93
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 * Returns the magnification controller, which may be used to query and 545 * modify the state of display magnification. 546 * <p> 547 * <strong>Note:</strong> In order to control magnification, your service 548 * must declare the capability by setting the 549 * {@link android.R.styleable#AccessibilityService_canControlMagnification} 550 * property in its meta-data. For more information, see 551 * {@link #SERVICE_META_DATA}. 552 * 553 * @return the magnification controller 554 */ 555 @NonNull 556 public final MagnificationController getMagnificationController() { 557 if (mMagnificationController == null) { 558 mMagnificationController = new MagnificationController(this); 559 } 560 return mMagnificationController; 561 } 562 563 /** 564 * Dispatch a gesture to the touch screen. Any gestures currently in progress, whether from 565 * the user, this service, or another service, will be cancelled. 566 * <p> 567 * <strong>Note:</strong> In order to dispatch gestures, your service 568 * must declare the capability by setting the 569 * {@link android.R.styleable#AccessibilityService_canPerformGestures} 570 * property in its meta-data. For more information, see 571 * {@link #SERVICE_META_DATA}. 572 * 573 * @param gesture The gesture to dispatch 574 * @param callback The object to call back when the status of the gesture is known. If 575 * {@code null}, no status is reported. 576 * @param handler The handler on which to call back the {@code callback} object. If 577 * {@code null}, the object is called back on the service's main thread. 578 * 579 * @return {@code true} if the gesture is dispatched, {@code false} if not. 580 */ 581 public final boolean dispatchGesture(@NonNull GestureDescription gesture, 582 @Nullable GestureResultCallback callback, 583 @Nullable Handler handler) { 584 final IAccessibilityServiceConnection connection = 585 AccessibilityInteractionClient.getInstance().getConnection( 586 mConnectionId); 587 if (connection == null) { 588 return false; 589 } 590 List<MotionEvent> events = MotionEventGenerator.getMotionEventsFromGestureDescription( 591 gesture, 100); 592 try { 593 synchronized (mLock) { 594 connection.sendMotionEvents(++mGestureStatusCallbackSequence, 595 new ParceledListSlice<>(events)); 596 if (callback != null) { 597 if (mGestureStatusCallbackInfos == null) { 598 mGestureStatusCallbackInfos = new SparseArray<>(); 599 } 600 GestureResultCallbackInfo callbackInfo = new GestureResultCallbackInfo(gesture, 601 callback, handler); 602 mGestureStatusCallbackInfos.put(mGestureStatusCallbackSequence, callbackInfo); 603 } 604 } 605 } catch (RemoteException re) { 606 throw new RuntimeException(re); 607 } 608 return true; 609 } 610 611 void onPerformGestureResult(int sequence, final boolean completedSuccessfully) { 612 if (mGestureStatusCallbackInfos == null) { 613 return; 614 } 615 GestureResultCallbackInfo callbackInfo; 616 synchronized (mLock) { 617 callbackInfo = mGestureStatusCallbackInfos.get(sequence); 618 } 619 final GestureResultCallbackInfo finalCallbackInfo = callbackInfo; 620 if ((callbackInfo != null) && (callbackInfo.gestureDescription != null) 621 && (callbackInfo.callback != null)) { 622 if (callbackInfo.handler != null) { 623 callbackInfo.handler.post(new Runnable() { 624 @Override 625 public void run() { 626 if (completedSuccessfully) { 627 finalCallbackInfo.callback 628 .onCompleted(finalCallbackInfo.gestureDescription); 629 } else { 630 finalCallbackInfo.callback 631 .onCancelled(finalCallbackInfo.gestureDescription); 632 } 633 } 634 }); 635 return; 636 } 637 if (completedSuccessfully) { 638 callbackInfo.callback.onCompleted(callbackInfo.gestureDescription); 639 } else { 640 callbackInfo.callback.onCancelled(callbackInfo.gestureDescription); 641 } 642 } 643 } 644 645 private void onMagnificationChanged(@NonNull Region region, float scale, 646 float centerX, float centerY) { 647 if (mMagnificationController != null) { 648 mMagnificationController.dispatchMagnificationChanged( 649 region, scale, centerX, centerY); 650 } 651 } 652 653 /** 654 * Used to control and query the state of display magnification. 655 */ 656 public static final class MagnificationController { 657 private final AccessibilityService mService; 658 659 /** 660 * Map of listeners to their handlers. Lazily created when adding the 661 * first magnification listener. 662 */ 663 private ArrayMap<OnMagnificationChangedListener, Handler> mListeners; 664 665 MagnificationController(@NonNull AccessibilityService service) { 666 mService = service; 667 } 668 669 /** 670 * Called when the service is connected. 671 */ 672 void onServiceConnected() { 673 if (mListeners != null && !mListeners.isEmpty()) { 674 setMagnificationCallbackEnabled(true); 675 } 676 } 677 678 /** 679 * Adds the specified change listener to the list of magnification 680 * change listeners. The callback will occur on the service's main 681 * thread. 682 * 683 * @param listener the listener to add, must be non-{@code null} 684 */ 685 public void addListener(@NonNull OnMagnificationChangedListener listener) { 686 addListener(listener, null); 687 } 688 689 /** 690 * Adds the specified change listener to the list of magnification 691 * change listeners. The callback will occur on the specified 692 * {@link Handler}'s thread, or on the service's main thread if the 693 * handler is {@code null}. 694 * 695 * @param listener the listener to add, must be non-null 696 * @param handler the handler on which the callback should execute, or 697 * {@code null} to execute on the service's main thread 698 */ 699 public void addListener(@NonNull OnMagnificationChangedListener listener, 700 @Nullable Handler handler) { 701 if (mListeners == null) { 702 mListeners = new ArrayMap<>(); 703 } 704 705 final boolean shouldEnableCallback = mListeners.isEmpty(); 706 mListeners.put(listener, handler); 707 708 if (shouldEnableCallback) { 709 // This may fail if the service is not connected yet, but if we 710 // still have listeners when it connects then we can try again. 711 setMagnificationCallbackEnabled(true); 712 } 713 } 714 715 /** 716 * Removes all instances of the specified change listener from the list 717 * of magnification change listeners. 718 * 719 * @param listener the listener to remove, must be non-null 720 * @return {@code true} if at least one instance of the listener was 721 * removed 722 */ 723 public boolean removeListener(@NonNull OnMagnificationChangedListener listener) { 724 if (mListeners == null) { 725 return false; 726 } 727 728 final int keyIndex = mListeners.indexOfKey(listener); 729 final boolean hasKey = keyIndex >= 0; 730 if (hasKey) { 731 mListeners.removeAt(keyIndex); 732 } 733 734 if (hasKey && mListeners.isEmpty()) { 735 // We just removed the last listener, so we don't need 736 // callbacks from the service anymore. 737 setMagnificationCallbackEnabled(false); 738 } 739 740 return hasKey; 741 } 742 743 private void setMagnificationCallbackEnabled(boolean enabled) { 744 final IAccessibilityServiceConnection connection = 745 AccessibilityInteractionClient.getInstance().getConnection( 746 mService.mConnectionId); 747 if (connection != null) { 748 try { 749 connection.setMagnificationCallbackEnabled(enabled); 750 } catch (RemoteException re) { 751 throw new RuntimeException(re); 752 } 753 } 754 } 755 756 /** 757 * Dispatches magnification changes to any registered listeners. This 758 * should be called on the service's main thread. 759 */ 760 void dispatchMagnificationChanged(final @NonNull Region region, final float scale, 761 final float centerX, final float centerY) { 762 if (mListeners == null || mListeners.isEmpty()) { 763 Slog.d(LOG_TAG, "Received magnification changed " 764 + "callback with no listeners registered!"); 765 setMagnificationCallbackEnabled(false); 766 return; 767 } 768 769 // Listeners may remove themselves. Perform a shallow copy to avoid 770 // concurrent modification. 771 final ArrayMap<OnMagnificationChangedListener, Handler> entries = 772 new ArrayMap<>(mListeners); 773 774 for (int i = 0, count = entries.size(); i < count; i++) { 775 final OnMagnificationChangedListener listener = entries.keyAt(i); 776 final Handler handler = entries.valueAt(i); 777 if (handler != null) { 778 handler.post(new Runnable() { 779 @Override 780 public void run() { 781 listener.onMagnificationChanged(MagnificationController.this, 782 region, scale, centerX, centerY); 783 } 784 }); 785 } else { 786 // We're already on the main thread, just run the listener. 787 listener.onMagnificationChanged(this, region, scale, centerX, centerY); 788 } 789 } 790 } 791 792 /** 793 * Returns the current magnification scale. 794 * <p> 795 * <strong>Note:</strong> If the service is not yet connected (e.g. 796 * {@link AccessibilityService#onServiceConnected()} has not yet been 797 * called) or the service has been disconnected, this method will 798 * return a default value of {@code 1.0f}. 799 * 800 * @return the current magnification scale 801 */ 802 public float getScale() { 803 final IAccessibilityServiceConnection connection = 804 AccessibilityInteractionClient.getInstance().getConnection( 805 mService.mConnectionId); 806 if (connection != null) { 807 try { 808 return connection.getMagnificationScale(); 809 } catch (RemoteException re) { 810 Log.w(LOG_TAG, "Failed to obtain scale", re); 811 } 812 } 813 return 1.0f; 814 } 815 816 /** 817 * Returns the unscaled screen-relative X coordinate of the focal 818 * center of the magnified region. This is the point around which 819 * zooming occurs and is guaranteed to lie within the magnified 820 * region. 821 * <p> 822 * <strong>Note:</strong> If the service is not yet connected (e.g. 823 * {@link AccessibilityService#onServiceConnected()} has not yet been 824 * called) or the service has been disconnected, this method will 825 * return a default value of {@code 0.0f}. 826 * 827 * @return the unscaled screen-relative X coordinate of the center of 828 * the magnified region 829 */ 830 public float getCenterX() { 831 final IAccessibilityServiceConnection connection = 832 AccessibilityInteractionClient.getInstance().getConnection( 833 mService.mConnectionId); 834 if (connection != null) { 835 try { 836 return connection.getMagnificationCenterX(); 837 } catch (RemoteException re) { 838 Log.w(LOG_TAG, "Failed to obtain center X", re); 839 } 840 } 841 return 0.0f; 842 } 843 844 /** 845 * Returns the unscaled screen-relative Y coordinate of the focal 846 * center of the magnified region. This is the point around which 847 * zooming occurs and is guaranteed to lie within the magnified 848 * region. 849 * <p> 850 * <strong>Note:</strong> If the service is not yet connected (e.g. 851 * {@link AccessibilityService#onServiceConnected()} has not yet been 852 * called) or the service has been disconnected, this method will 853 * return a default value of {@code 0.0f}. 854 * 855 * @return the unscaled screen-relative Y coordinate of the center of 856 * the magnified region 857 */ 858 public float getCenterY() { 859 final IAccessibilityServiceConnection connection = 860 AccessibilityInteractionClient.getInstance().getConnection( 861 mService.mConnectionId); 862 if (connection != null) { 863 try { 864 return connection.getMagnificationCenterY(); 865 } catch (RemoteException re) { 866 Log.w(LOG_TAG, "Failed to obtain center Y", re); 867 } 868 } 869 return 0.0f; 870 } 871 872 /** 873 * Returns the region of the screen currently being magnified. If 874 * magnification is not enabled, the returned region will be empty. 875 * <p> 876 * <strong>Note:</strong> If the service is not yet connected (e.g. 877 * {@link AccessibilityService#onServiceConnected()} has not yet been 878 * called) or the service has been disconnected, this method will 879 * return an empty region. 880 * 881 * @return the screen-relative bounds of the magnified region 882 */ 883 @NonNull 884 public Region getMagnifiedRegion() { 885 final IAccessibilityServiceConnection connection = 886 AccessibilityInteractionClient.getInstance().getConnection( 887 mService.mConnectionId); 888 if (connection != null) { 889 try { 890 return connection.getMagnifiedRegion(); 891 } catch (RemoteException re) { 892 Log.w(LOG_TAG, "Failed to obtain magnified region", re); 893 } 894 } 895 return Region.obtain(); 896 } 897 898 /** 899 * Resets magnification scale and center to their default (e.g. no 900 * magnification) values. 901 * <p> 902 * <strong>Note:</strong> If the service is not yet connected (e.g. 903 * {@link AccessibilityService#onServiceConnected()} has not yet been 904 * called) or the service has been disconnected, this method will have 905 * no effect and return {@code false}. 906 * 907 * @param animate {@code true} to animate from the current scale and 908 * center or {@code false} to reset the scale and center 909 * immediately 910 * @return {@code true} on success, {@code false} on failure 911 */ 912 public boolean reset(boolean animate) { 913 final IAccessibilityServiceConnection connection = 914 AccessibilityInteractionClient.getInstance().getConnection( 915 mService.mConnectionId); 916 if (connection != null) { 917 try { 918 return connection.resetMagnification(animate); 919 } catch (RemoteException re) { 920 Log.w(LOG_TAG, "Failed to reset", re); 921 } 922 } 923 return false; 924 } 925 926 /** 927 * Sets the magnification scale. 928 * <p> 929 * <strong>Note:</strong> If the service is not yet connected (e.g. 930 * {@link AccessibilityService#onServiceConnected()} has not yet been 931 * called) or the service has been disconnected, this method will have 932 * no effect and return {@code false}. 933 * 934 * @param scale the magnification scale to set, must be >= 1 and <= 5 935 * @param animate {@code true} to animate from the current scale or 936 * {@code false} to set the scale immediately 937 * @return {@code true} on success, {@code false} on failure 938 */ 939 public boolean setScale(float scale, boolean animate) { 940 final IAccessibilityServiceConnection connection = 941 AccessibilityInteractionClient.getInstance().getConnection( 942 mService.mConnectionId); 943 if (connection != null) { 944 try { 945 return connection.setMagnificationScaleAndCenter( 946 scale, Float.NaN, Float.NaN, animate); 947 } catch (RemoteException re) { 948 Log.w(LOG_TAG, "Failed to set scale", re); 949 } 950 } 951 return false; 952 } 953 954 /** 955 * Sets the center of the magnified viewport. 956 * <p> 957 * <strong>Note:</strong> If the service is not yet connected (e.g. 958 * {@link AccessibilityService#onServiceConnected()} has not yet been 959 * called) or the service has been disconnected, this method will have 960 * no effect and return {@code false}. 961 * 962 * @param centerX the unscaled screen-relative X coordinate on which to 963 * center the viewport 964 * @param centerY the unscaled screen-relative Y coordinate on which to 965 * center the viewport 966 * @param animate {@code true} to animate from the current viewport 967 * center or {@code false} to set the center immediately 968 * @return {@code true} on success, {@code false} on failure 969 */ 970 public boolean setCenter(float centerX, float centerY, boolean animate) { 971 final IAccessibilityServiceConnection connection = 972 AccessibilityInteractionClient.getInstance().getConnection( 973 mService.mConnectionId); 974 if (connection != null) { 975 try { 976 return connection.setMagnificationScaleAndCenter( 977 Float.NaN, centerX, centerY, animate); 978 } catch (RemoteException re) { 979 Log.w(LOG_TAG, "Failed to set center", re); 980 } 981 } 982 return false; 983 } 984 985 /** 986 * Listener for changes in the state of magnification. 987 */ 988 public interface OnMagnificationChangedListener { 989 /** 990 * Called when the magnified region, scale, or center changes. 991 * 992 * @param controller the magnification controller 993 * @param region the new magnified region, may be empty if 994 * magnification is not enabled (e.g. scale is 1) 995 * @param scale the new scale 996 * @param centerX the new X coordinate around which magnification is focused 997 * @param centerY the new Y coordinate around which magnification is focused 998 */ 999 void onMagnificationChanged(@NonNull MagnificationController controller, 1000 @NonNull Region region, float scale, float centerX, float centerY); 1001 } 1002 } 1003 1004 /** 1005 * Performs a global action. Such an action can be performed 1006 * at any moment regardless of the current application or user 1007 * location in that application. For example going back, going 1008 * home, opening recents, etc. 1009 * 1010 * @param action The action to perform. 1011 * @return Whether the action was successfully performed. 1012 * 1013 * @see #GLOBAL_ACTION_BACK 1014 * @see #GLOBAL_ACTION_HOME 1015 * @see #GLOBAL_ACTION_NOTIFICATIONS 1016 * @see #GLOBAL_ACTION_RECENTS 1017 */ 1018 public final boolean performGlobalAction(int action) { 1019 IAccessibilityServiceConnection connection = 1020 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); 1021 if (connection != null) { 1022 try { 1023 return connection.performGlobalAction(action); 1024 } catch (RemoteException re) { 1025 Log.w(LOG_TAG, "Error while calling performGlobalAction", re); 1026 } 1027 } 1028 return false; 1029 } 1030 1031 /** 1032 * Find the view that has the specified focus type. The search is performed 1033 * across all windows. 1034 * <p> 1035 * <strong>Note:</strong> In order to access the windows your service has 1036 * to declare the capability to retrieve window content by setting the 1037 * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent} 1038 * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}. 1039 * Also the service has to opt-in to retrieve the interactive windows by 1040 * setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} 1041 * flag.Otherwise, the search will be performed only in the active window. 1042 * </p> 1043 * 1044 * @param focus The focus to find. One of {@link AccessibilityNodeInfo#FOCUS_INPUT} or 1045 * {@link AccessibilityNodeInfo#FOCUS_ACCESSIBILITY}. 1046 * @return The node info of the focused view or null. 1047 * 1048 * @see AccessibilityNodeInfo#FOCUS_INPUT 1049 * @see AccessibilityNodeInfo#FOCUS_ACCESSIBILITY 1050 */ 1051 public AccessibilityNodeInfo findFocus(int focus) { 1052 return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId, 1053 AccessibilityNodeInfo.ANY_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID, focus); 1054 } 1055 1056 /** 1057 * Gets the an {@link AccessibilityServiceInfo} describing this 1058 * {@link AccessibilityService}. This method is useful if one wants 1059 * to change some of the dynamically configurable properties at 1060 * runtime. 1061 * 1062 * @return The accessibility service info. 1063 * 1064 * @see AccessibilityServiceInfo 1065 */ 1066 public final AccessibilityServiceInfo getServiceInfo() { 1067 IAccessibilityServiceConnection connection = 1068 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); 1069 if (connection != null) { 1070 try { 1071 return connection.getServiceInfo(); 1072 } catch (RemoteException re) { 1073 Log.w(LOG_TAG, "Error while getting AccessibilityServiceInfo", re); 1074 } 1075 } 1076 return null; 1077 } 1078 1079 /** 1080 * Sets the {@link AccessibilityServiceInfo} that describes this service. 1081 * <p> 1082 * Note: You can call this method any time but the info will be picked up after 1083 * the system has bound to this service and when this method is called thereafter. 1084 * 1085 * @param info The info. 1086 */ 1087 public final void setServiceInfo(AccessibilityServiceInfo info) { 1088 mInfo = info; 1089 sendServiceInfo(); 1090 } 1091 1092 /** 1093 * Sets the {@link AccessibilityServiceInfo} for this service if the latter is 1094 * properly set and there is an {@link IAccessibilityServiceConnection} to the 1095 * AccessibilityManagerService. 1096 */ 1097 private void sendServiceInfo() { 1098 IAccessibilityServiceConnection connection = 1099 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); 1100 if (mInfo != null && connection != null) { 1101 try { 1102 connection.setServiceInfo(mInfo); 1103 mInfo = null; 1104 AccessibilityInteractionClient.getInstance().clearCache(); 1105 } catch (RemoteException re) { 1106 Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re); 1107 } 1108 } 1109 } 1110 1111 @Override 1112 public Object getSystemService(@ServiceName @NonNull String name) { 1113 if (getBaseContext() == null) { 1114 throw new IllegalStateException( 1115 "System services not available to Activities before onCreate()"); 1116 } 1117 1118 // Guarantee that we always return the same window manager instance. 1119 if (WINDOW_SERVICE.equals(name)) { 1120 if (mWindowManager == null) { 1121 mWindowManager = (WindowManager) getBaseContext().getSystemService(name); 1122 } 1123 return mWindowManager; 1124 } 1125 return super.getSystemService(name); 1126 } 1127 1128 /** 1129 * Implement to return the implementation of the internal accessibility 1130 * service interface. 1131 */ 1132 @Override 1133 public final IBinder onBind(Intent intent) { 1134 return new IAccessibilityServiceClientWrapper(this, getMainLooper(), new Callbacks() { 1135 @Override 1136 public void onServiceConnected() { 1137 AccessibilityService.this.dispatchServiceConnected(); 1138 } 1139 1140 @Override 1141 public void onInterrupt() { 1142 AccessibilityService.this.onInterrupt(); 1143 } 1144 1145 @Override 1146 public void onAccessibilityEvent(AccessibilityEvent event) { 1147 AccessibilityService.this.onAccessibilityEvent(event); 1148 } 1149 1150 @Override 1151 public void init(int connectionId, IBinder windowToken) { 1152 mConnectionId = connectionId; 1153 mWindowToken = windowToken; 1154 1155 // The client may have already obtained the window manager, so 1156 // update the default token on whatever manager we gave them. 1157 final WindowManagerImpl wm = (WindowManagerImpl) getSystemService(WINDOW_SERVICE); 1158 wm.setDefaultToken(windowToken); 1159 } 1160 1161 @Override 1162 public boolean onGesture(int gestureId) { 1163 return AccessibilityService.this.onGesture(gestureId); 1164 } 1165 1166 @Override 1167 public boolean onKeyEvent(KeyEvent event) { 1168 return AccessibilityService.this.onKeyEvent(event); 1169 } 1170 1171 @Override 1172 public void onMagnificationChanged(@NonNull Region region, 1173 float scale, float centerX, float centerY) { 1174 AccessibilityService.this.onMagnificationChanged(region, scale, centerX, centerY); 1175 } 1176 1177 @Override 1178 public void onPerformGestureResult(int sequence, boolean completedSuccessfully) { 1179 AccessibilityService.this.onPerformGestureResult(sequence, completedSuccessfully); 1180 } 1181 }); 1182 } 1183 1184 /** 1185 * Implements the internal {@link IAccessibilityServiceClient} interface to convert 1186 * incoming calls to it back to calls on an {@link AccessibilityService}. 1187 * 1188 * @hide 1189 */ 1190 public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub 1191 implements HandlerCaller.Callback { 1192 private static final int DO_INIT = 1; 1193 private static final int DO_ON_INTERRUPT = 2; 1194 private static final int DO_ON_ACCESSIBILITY_EVENT = 3; 1195 private static final int DO_ON_GESTURE = 4; 1196 private static final int DO_CLEAR_ACCESSIBILITY_CACHE = 5; 1197 private static final int DO_ON_KEY_EVENT = 6; 1198 private static final int DO_ON_MAGNIFICATION_CHANGED = 7; 1199 private static final int DO_GESTURE_COMPLETE = 8; 1200 1201 private final HandlerCaller mCaller; 1202 1203 private final Callbacks mCallback; 1204 1205 private int mConnectionId; 1206 1207 public IAccessibilityServiceClientWrapper(Context context, Looper looper, 1208 Callbacks callback) { 1209 mCallback = callback; 1210 mCaller = new HandlerCaller(context, looper, this, true /*asyncHandler*/); 1211 } 1212 1213 public void init(IAccessibilityServiceConnection connection, int connectionId, 1214 IBinder windowToken) { 1215 Message message = mCaller.obtainMessageIOO(DO_INIT, connectionId, 1216 connection, windowToken); 1217 mCaller.sendMessage(message); 1218 } 1219 1220 public void onInterrupt() { 1221 Message message = mCaller.obtainMessage(DO_ON_INTERRUPT); 1222 mCaller.sendMessage(message); 1223 } 1224 1225 public void onAccessibilityEvent(AccessibilityEvent event) { 1226 Message message = mCaller.obtainMessageO(DO_ON_ACCESSIBILITY_EVENT, event); 1227 mCaller.sendMessage(message); 1228 } 1229 1230 public void onGesture(int gestureId) { 1231 Message message = mCaller.obtainMessageI(DO_ON_GESTURE, gestureId); 1232 mCaller.sendMessage(message); 1233 } 1234 1235 public void clearAccessibilityCache() { 1236 Message message = mCaller.obtainMessage(DO_CLEAR_ACCESSIBILITY_CACHE); 1237 mCaller.sendMessage(message); 1238 } 1239 1240 @Override 1241 public void onKeyEvent(KeyEvent event, int sequence) { 1242 Message message = mCaller.obtainMessageIO(DO_ON_KEY_EVENT, sequence, event); 1243 mCaller.sendMessage(message); 1244 } 1245 1246 public void onMagnificationChanged(@NonNull Region region, 1247 float scale, float centerX, float centerY) { 1248 final SomeArgs args = SomeArgs.obtain(); 1249 args.arg1 = region; 1250 args.arg2 = scale; 1251 args.arg3 = centerX; 1252 args.arg4 = centerY; 1253 1254 final Message message = mCaller.obtainMessageO(DO_ON_MAGNIFICATION_CHANGED, args); 1255 mCaller.sendMessage(message); 1256 } 1257 1258 public void onPerformGestureResult(int sequence, boolean successfully) { 1259 Message message = mCaller.obtainMessageII(DO_GESTURE_COMPLETE, sequence, 1260 successfully ? 1 : 0); 1261 mCaller.sendMessage(message); 1262 } 1263 1264 @Override 1265 public void executeMessage(Message message) { 1266 switch (message.what) { 1267 case DO_ON_ACCESSIBILITY_EVENT: { 1268 AccessibilityEvent event = (AccessibilityEvent) message.obj; 1269 if (event != null) { 1270 AccessibilityInteractionClient.getInstance().onAccessibilityEvent(event); 1271 mCallback.onAccessibilityEvent(event); 1272 // Make sure the event is recycled. 1273 try { 1274 event.recycle(); 1275 } catch (IllegalStateException ise) { 1276 /* ignore - best effort */ 1277 } 1278 } 1279 } return; 1280 1281 case DO_ON_INTERRUPT: { 1282 mCallback.onInterrupt(); 1283 } return; 1284 1285 case DO_INIT: { 1286 mConnectionId = message.arg1; 1287 SomeArgs args = (SomeArgs) message.obj; 1288 IAccessibilityServiceConnection connection = 1289 (IAccessibilityServiceConnection) args.arg1; 1290 IBinder windowToken = (IBinder) args.arg2; 1291 args.recycle(); 1292 if (connection != null) { 1293 AccessibilityInteractionClient.getInstance().addConnection(mConnectionId, 1294 connection); 1295 mCallback.init(mConnectionId, windowToken); 1296 mCallback.onServiceConnected(); 1297 } else { 1298 AccessibilityInteractionClient.getInstance().removeConnection( 1299 mConnectionId); 1300 mConnectionId = AccessibilityInteractionClient.NO_ID; 1301 AccessibilityInteractionClient.getInstance().clearCache(); 1302 mCallback.init(AccessibilityInteractionClient.NO_ID, null); 1303 } 1304 } return; 1305 1306 case DO_ON_GESTURE: { 1307 final int gestureId = message.arg1; 1308 mCallback.onGesture(gestureId); 1309 } return; 1310 1311 case DO_CLEAR_ACCESSIBILITY_CACHE: { 1312 AccessibilityInteractionClient.getInstance().clearCache(); 1313 } return; 1314 1315 case DO_ON_KEY_EVENT: { 1316 KeyEvent event = (KeyEvent) message.obj; 1317 try { 1318 IAccessibilityServiceConnection connection = AccessibilityInteractionClient 1319 .getInstance().getConnection(mConnectionId); 1320 if (connection != null) { 1321 final boolean result = mCallback.onKeyEvent(event); 1322 final int sequence = message.arg1; 1323 try { 1324 connection.setOnKeyEventResult(result, sequence); 1325 } catch (RemoteException re) { 1326 /* ignore */ 1327 } 1328 } 1329 } finally { 1330 // Make sure the event is recycled. 1331 try { 1332 event.recycle(); 1333 } catch (IllegalStateException ise) { 1334 /* ignore - best effort */ 1335 } 1336 } 1337 } return; 1338 1339 case DO_ON_MAGNIFICATION_CHANGED: { 1340 final SomeArgs args = (SomeArgs) message.obj; 1341 final Region region = (Region) args.arg1; 1342 final float scale = (float) args.arg2; 1343 final float centerX = (float) args.arg3; 1344 final float centerY = (float) args.arg4; 1345 mCallback.onMagnificationChanged(region, scale, centerX, centerY); 1346 } return; 1347 1348 case DO_GESTURE_COMPLETE: { 1349 final boolean successfully = message.arg2 == 1; 1350 mCallback.onPerformGestureResult(message.arg1, successfully); 1351 } return; 1352 1353 default : 1354 Log.w(LOG_TAG, "Unknown message type " + message.what); 1355 } 1356 } 1357 } 1358 1359 /** 1360 * Class used to report status of dispatched gestures 1361 */ 1362 public static abstract class GestureResultCallback { 1363 /** Called when the gesture has completed successfully 1364 * 1365 * @param gestureDescription The description of the gesture that completed. 1366 */ 1367 public void onCompleted(GestureDescription gestureDescription) { 1368 } 1369 1370 /** Called when the gesture was cancelled 1371 * 1372 * @param gestureDescription The description of the gesture that was cancelled. 1373 */ 1374 public void onCancelled(GestureDescription gestureDescription) { 1375 } 1376 } 1377 1378 /* Object to keep track of gesture result callbacks */ 1379 private static class GestureResultCallbackInfo { 1380 GestureDescription gestureDescription; 1381 GestureResultCallback callback; 1382 Handler handler; 1383 1384 GestureResultCallbackInfo(GestureDescription gestureDescription, 1385 GestureResultCallback callback, Handler handler) { 1386 this.gestureDescription = gestureDescription; 1387 this.callback = callback; 1388 this.handler = handler; 1389 } 1390 } 1391} 1392