AccessibilityService.java revision e34560b21989eea54a139a0586d156ba573cc2ea
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.app.Service; 20import android.content.Context; 21import android.content.Intent; 22import android.os.IBinder; 23import android.os.Looper; 24import android.os.Message; 25import android.os.RemoteException; 26import android.os.SystemClock; 27import android.util.Log; 28import android.view.KeyEvent; 29import android.view.accessibility.AccessibilityEvent; 30import android.view.accessibility.AccessibilityInteractionClient; 31import android.view.accessibility.AccessibilityNodeInfo; 32import android.view.accessibility.AccessibilityWindowInfo; 33 34import com.android.internal.os.HandlerCaller; 35 36import java.util.List; 37 38/** 39 * An accessibility service runs in the background and receives callbacks by the system 40 * when {@link AccessibilityEvent}s are fired. Such events denote some state transition 41 * in the user interface, for example, the focus has changed, a button has been clicked, 42 * etc. Such a service can optionally request the capability for querying the content 43 * of the active window. Development of an accessibility service requires extending this 44 * class and implementing its abstract methods. 45 * 46 * <div class="special reference"> 47 * <h3>Developer Guides</h3> 48 * <p>For more information about creating AccessibilityServices, read the 49 * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a> 50 * developer guide.</p> 51 * </div> 52 * 53 * <h3>Lifecycle</h3> 54 * <p> 55 * The lifecycle of an accessibility service is managed exclusively by the system and 56 * follows the established service life cycle. Additionally, starting or stopping an 57 * accessibility service is triggered exclusively by an explicit user action through 58 * enabling or disabling it in the device settings. After the system binds to a service it 59 * calls {@link AccessibilityService#onServiceConnected()}. This method can be 60 * overriden by clients that want to perform post binding setup. 61 * </p> 62 * <h3>Declaration</h3> 63 * <p> 64 * An accessibility is declared as any other service in an AndroidManifest.xml but it 65 * must also specify that it handles the "android.accessibilityservice.AccessibilityService" 66 * {@link android.content.Intent}. Failure to declare this intent will cause the system to 67 * ignore the accessibility service. Additionally an accessibility service must request the 68 * {@link android.Manifest.permission#BIND_ACCESSIBILITY_SERVICE} permission to ensure 69 * that only the system 70 * can bind to it. Failure to declare this intent will cause the system to ignore the 71 * accessibility service. Following is an example declaration: 72 * </p> 73 * <pre> <service android:name=".MyAccessibilityService" 74 * android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"> 75 * <intent-filter> 76 * <action android:name="android.accessibilityservice.AccessibilityService" /> 77 * </intent-filter> 78 * . . . 79 * </service></pre> 80 * <h3>Configuration</h3> 81 * <p> 82 * An accessibility service can be configured to receive specific types of accessibility events, 83 * listen only to specific packages, get events from each type only once in a given time frame, 84 * retrieve window content, specify a settings activity, etc. 85 * </p> 86 * <p> 87 * There are two approaches for configuring an accessibility service: 88 * </p> 89 * <ul> 90 * <li> 91 * Providing a {@link #SERVICE_META_DATA meta-data} entry in the manifest when declaring 92 * the service. A service declaration with a meta-data tag is presented below: 93 * <pre> <service android:name=".MyAccessibilityService"> 94 * <intent-filter> 95 * <action android:name="android.accessibilityservice.AccessibilityService" /> 96 * </intent-filter> 97 * <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibilityservice" /> 98 * </service></pre> 99 * <p class="note"> 100 * <strong>Note:</strong> This approach enables setting all properties. 101 * </p> 102 * <p> 103 * For more details refer to {@link #SERVICE_META_DATA} and 104 * <code><{@link android.R.styleable#AccessibilityService accessibility-service}></code>. 105 * </p> 106 * </li> 107 * <li> 108 * Calling {@link AccessibilityService#setServiceInfo(AccessibilityServiceInfo)}. Note 109 * that this method can be called any time to dynamically change the service configuration. 110 * <p class="note"> 111 * <strong>Note:</strong> This approach enables setting only dynamically configurable properties: 112 * {@link AccessibilityServiceInfo#eventTypes}, 113 * {@link AccessibilityServiceInfo#feedbackType}, 114 * {@link AccessibilityServiceInfo#flags}, 115 * {@link AccessibilityServiceInfo#notificationTimeout}, 116 * {@link AccessibilityServiceInfo#packageNames} 117 * </p> 118 * <p> 119 * For more details refer to {@link AccessibilityServiceInfo}. 120 * </p> 121 * </li> 122 * </ul> 123 * <h3>Retrieving window content</h3> 124 * <p> 125 * A service can specify in its declaration that it can retrieve the active window 126 * content which is represented as a tree of {@link AccessibilityNodeInfo}. Note that 127 * declaring this capability requires that the service declares its configuration via 128 * an XML resource referenced by {@link #SERVICE_META_DATA}. 129 * </p> 130 * <p> 131 * For security purposes an accessibility service can retrieve only the content of the 132 * currently active window. The currently active window is defined as the window from 133 * which was fired the last event of the following types: 134 * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}, 135 * {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER}, 136 * {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}, 137 * In other words, the last window that was shown or the last window that the user has touched 138 * during touch exploration. 139 * </p> 140 * <p> 141 * The entry point for retrieving window content is through calling 142 * {@link AccessibilityEvent#getSource() AccessibilityEvent.getSource()} of the last received 143 * event of the above types or a previous event from the same window 144 * (see {@link AccessibilityEvent#getWindowId() AccessibilityEvent.getWindowId()}). Invoking 145 * this method will return an {@link AccessibilityNodeInfo} that can be used to traverse the 146 * window content which represented as a tree of such objects. 147 * </p> 148 * <p class="note"> 149 * <strong>Note</strong> An accessibility service may have requested to be notified for 150 * a subset of the event types, thus be unaware that the active window has changed. Therefore 151 * accessibility service that would like to retrieve window content should: 152 * <ul> 153 * <li> 154 * Register for all event types with no notification timeout and keep track for the active 155 * window by calling {@link AccessibilityEvent#getWindowId()} of the last received event and 156 * compare this with the {@link AccessibilityNodeInfo#getWindowId()} before calling retrieval 157 * methods on the latter. 158 * </li> 159 * <li> 160 * Prepare that a retrieval method on {@link AccessibilityNodeInfo} may fail since the 161 * active window has changed and the service did not get the accessibility event yet. Note 162 * that it is possible to have a retrieval method failing even adopting the strategy 163 * specified in the previous bullet because the accessibility event dispatch is asynchronous 164 * and crosses process boundaries. 165 * </li> 166 * </ul> 167 * </p> 168 * <h3>Notification strategy</h3> 169 * <p> 170 * For each feedback type only one accessibility service is notified. Services are notified 171 * in the order of registration. Hence, if two services are registered for the same 172 * feedback type in the same package the first one wins. It is possible however, to 173 * register a service as the default one for a given feedback type. In such a case this 174 * service is invoked if no other service was interested in the event. In other words, default 175 * services do not compete with other services and are notified last regardless of the 176 * registration order. This enables "generic" accessibility services that work reasonably 177 * well with most applications to coexist with "polished" ones that are targeted for 178 * specific applications. 179 * </p> 180 * <p class="note"> 181 * <strong>Note:</strong> The event notification timeout is useful to avoid propagating 182 * events to the client too frequently since this is accomplished via an expensive 183 * interprocess call. One can think of the timeout as a criteria to determine when 184 * event generation has settled down.</p> 185 * <h3>Event types</h3> 186 * <ul> 187 * <li>{@link AccessibilityEvent#TYPE_VIEW_CLICKED}</li> 188 * <li>{@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED}</li> 189 * <li>{@link AccessibilityEvent#TYPE_VIEW_FOCUSED}</li> 190 * <li>{@link AccessibilityEvent#TYPE_VIEW_SELECTED}</li> 191 * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED}</li> 192 * <li>{@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}</li> 193 * <li>{@link AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED}</li> 194 * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START}</li> 195 * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END}</li> 196 * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER}</li> 197 * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}</li> 198 * <li>{@link AccessibilityEvent#TYPE_VIEW_SCROLLED}</li> 199 * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED}</li> 200 * <li>{@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED}</li> 201 * <li>{@link AccessibilityEvent#TYPE_ANNOUNCEMENT}</li> 202 * <li>{@link AccessibilityEvent#TYPE_GESTURE_DETECTION_START}</li> 203 * <li>{@link AccessibilityEvent#TYPE_GESTURE_DETECTION_END}</li> 204 * <li>{@link AccessibilityEvent#TYPE_TOUCH_INTERACTION_START}</li> 205 * <li>{@link AccessibilityEvent#TYPE_TOUCH_INTERACTION_END}</li> 206 * <li>{@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUSED}</li> 207 * <li>{@link AccessibilityEvent#TYPE_WINDOWS_CHANGED}</li> 208 * <li>{@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED}</li> 209 * </ul> 210 * <h3>Feedback types</h3> 211 * <ul> 212 * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}</li> 213 * <li>{@link AccessibilityServiceInfo#FEEDBACK_HAPTIC}</li> 214 * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}</li> 215 * <li>{@link AccessibilityServiceInfo#FEEDBACK_VISUAL}</li> 216 * <li>{@link AccessibilityServiceInfo#FEEDBACK_GENERIC}</li> 217 * <li>{@link AccessibilityServiceInfo#FEEDBACK_BRAILLE}</li> 218 * </ul> 219 * @see AccessibilityEvent 220 * @see AccessibilityServiceInfo 221 * @see android.view.accessibility.AccessibilityManager 222 */ 223public abstract class AccessibilityService extends Service { 224 225 /** 226 * The user has performed a swipe up gesture on the touch screen. 227 */ 228 public static final int GESTURE_SWIPE_UP = 1; 229 230 /** 231 * The user has performed a swipe down gesture on the touch screen. 232 */ 233 public static final int GESTURE_SWIPE_DOWN = 2; 234 235 /** 236 * The user has performed a swipe left gesture on the touch screen. 237 */ 238 public static final int GESTURE_SWIPE_LEFT = 3; 239 240 /** 241 * The user has performed a swipe right gesture on the touch screen. 242 */ 243 public static final int GESTURE_SWIPE_RIGHT = 4; 244 245 /** 246 * The user has performed a swipe left and right gesture on the touch screen. 247 */ 248 public static final int GESTURE_SWIPE_LEFT_AND_RIGHT = 5; 249 250 /** 251 * The user has performed a swipe right and left gesture on the touch screen. 252 */ 253 public static final int GESTURE_SWIPE_RIGHT_AND_LEFT = 6; 254 255 /** 256 * The user has performed a swipe up and down gesture on the touch screen. 257 */ 258 public static final int GESTURE_SWIPE_UP_AND_DOWN = 7; 259 260 /** 261 * The user has performed a swipe down and up gesture on the touch screen. 262 */ 263 public static final int GESTURE_SWIPE_DOWN_AND_UP = 8; 264 265 /** 266 * The user has performed a left and up gesture on the touch screen. 267 */ 268 public static final int GESTURE_SWIPE_LEFT_AND_UP = 9; 269 270 /** 271 * The user has performed a left and down gesture on the touch screen. 272 */ 273 public static final int GESTURE_SWIPE_LEFT_AND_DOWN = 10; 274 275 /** 276 * The user has performed a right and up gesture on the touch screen. 277 */ 278 public static final int GESTURE_SWIPE_RIGHT_AND_UP = 11; 279 280 /** 281 * The user has performed a right and down gesture on the touch screen. 282 */ 283 public static final int GESTURE_SWIPE_RIGHT_AND_DOWN = 12; 284 285 /** 286 * The user has performed an up and left gesture on the touch screen. 287 */ 288 public static final int GESTURE_SWIPE_UP_AND_LEFT = 13; 289 290 /** 291 * The user has performed an up and right gesture on the touch screen. 292 */ 293 public static final int GESTURE_SWIPE_UP_AND_RIGHT = 14; 294 295 /** 296 * The user has performed an down and left gesture on the touch screen. 297 */ 298 public static final int GESTURE_SWIPE_DOWN_AND_LEFT = 15; 299 300 /** 301 * The user has performed an down and right gesture on the touch screen. 302 */ 303 public static final int GESTURE_SWIPE_DOWN_AND_RIGHT = 16; 304 305 /** 306 * The {@link Intent} that must be declared as handled by the service. 307 */ 308 public static final String SERVICE_INTERFACE = 309 "android.accessibilityservice.AccessibilityService"; 310 311 /** 312 * Name under which an AccessibilityService component publishes information 313 * about itself. This meta-data must reference an XML resource containing an 314 * <code><{@link android.R.styleable#AccessibilityService accessibility-service}></code> 315 * tag. This is a a sample XML file configuring an accessibility service: 316 * <pre> <accessibility-service 317 * android:accessibilityEventTypes="typeViewClicked|typeViewFocused" 318 * android:packageNames="foo.bar, foo.baz" 319 * android:accessibilityFeedbackType="feedbackSpoken" 320 * android:notificationTimeout="100" 321 * android:accessibilityFlags="flagDefault" 322 * android:settingsActivity="foo.bar.TestBackActivity" 323 * android:canRetrieveWindowContent="true" 324 * android:canRequestTouchExplorationMode="true" 325 * android:canRequestEnhancedWebAccessibility="true" 326 * . . . 327 * /></pre> 328 */ 329 public static final String SERVICE_META_DATA = "android.accessibilityservice"; 330 331 /** 332 * Action to go back. 333 */ 334 public static final int GLOBAL_ACTION_BACK = 1; 335 336 /** 337 * Action to go home. 338 */ 339 public static final int GLOBAL_ACTION_HOME = 2; 340 341 /** 342 * Action to open the recent apps. 343 */ 344 public static final int GLOBAL_ACTION_RECENTS = 3; 345 346 /** 347 * Action to open the notifications. 348 */ 349 public static final int GLOBAL_ACTION_NOTIFICATIONS = 4; 350 351 /** 352 * Action to open the quick settings. 353 */ 354 public static final int GLOBAL_ACTION_QUICK_SETTINGS = 5; 355 356 /** 357 * Action to open the power long-press dialog. 358 */ 359 public static final int GLOBAL_ACTION_POWER_DIALOG = 6; 360 361 private static final String LOG_TAG = "AccessibilityService"; 362 363 /** 364 * @hide 365 */ 366 public interface Callbacks { 367 public void onAccessibilityEvent(AccessibilityEvent event); 368 public void onInterrupt(); 369 public void onServiceConnected(); 370 public void onSetConnectionId(int connectionId); 371 public boolean onGesture(int gestureId); 372 public boolean onKeyEvent(KeyEvent event); 373 } 374 375 private int mConnectionId; 376 377 private AccessibilityServiceInfo mInfo; 378 379 /** 380 * Callback for {@link android.view.accessibility.AccessibilityEvent}s. 381 * 382 * @param event An event. 383 */ 384 public abstract void onAccessibilityEvent(AccessibilityEvent event); 385 386 /** 387 * Callback for interrupting the accessibility feedback. 388 */ 389 public abstract void onInterrupt(); 390 391 /** 392 * This method is a part of the {@link AccessibilityService} lifecycle and is 393 * called after the system has successfully bound to the service. If is 394 * convenient to use this method for setting the {@link AccessibilityServiceInfo}. 395 * 396 * @see AccessibilityServiceInfo 397 * @see #setServiceInfo(AccessibilityServiceInfo) 398 */ 399 protected void onServiceConnected() { 400 401 } 402 403 /** 404 * Called by the system when the user performs a specific gesture on the 405 * touch screen. 406 * 407 * <strong>Note:</strong> To receive gestures an accessibility service must 408 * request that the device is in touch exploration mode by setting the 409 * {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} 410 * flag. 411 * 412 * @param gestureId The unique id of the performed gesture. 413 * 414 * @return Whether the gesture was handled. 415 * 416 * @see #GESTURE_SWIPE_UP 417 * @see #GESTURE_SWIPE_UP_AND_LEFT 418 * @see #GESTURE_SWIPE_UP_AND_DOWN 419 * @see #GESTURE_SWIPE_UP_AND_RIGHT 420 * @see #GESTURE_SWIPE_DOWN 421 * @see #GESTURE_SWIPE_DOWN_AND_LEFT 422 * @see #GESTURE_SWIPE_DOWN_AND_UP 423 * @see #GESTURE_SWIPE_DOWN_AND_RIGHT 424 * @see #GESTURE_SWIPE_LEFT 425 * @see #GESTURE_SWIPE_LEFT_AND_UP 426 * @see #GESTURE_SWIPE_LEFT_AND_RIGHT 427 * @see #GESTURE_SWIPE_LEFT_AND_DOWN 428 * @see #GESTURE_SWIPE_RIGHT 429 * @see #GESTURE_SWIPE_RIGHT_AND_UP 430 * @see #GESTURE_SWIPE_RIGHT_AND_LEFT 431 * @see #GESTURE_SWIPE_RIGHT_AND_DOWN 432 */ 433 protected boolean onGesture(int gestureId) { 434 return false; 435 } 436 437 /** 438 * Callback that allows an accessibility service to observe the key events 439 * before they are passed to the rest of the system. This means that the events 440 * are first delivered here before they are passed to the device policy, the 441 * input method, or applications. 442 * <p> 443 * <strong>Note:</strong> It is important that key events are handled in such 444 * a way that the event stream that would be passed to the rest of the system 445 * is well-formed. For example, handling the down event but not the up event 446 * and vice versa would generate an inconsistent event stream. 447 * </p> 448 * <p> 449 * <strong>Note:</strong> The key events delivered in this method are copies 450 * and modifying them will have no effect on the events that will be passed 451 * to the system. This method is intended to perform purely filtering 452 * functionality. 453 * <p> 454 * 455 * @param event The event to be processed. 456 * @return If true then the event will be consumed and not delivered to 457 * applications, otherwise it will be delivered as usual. 458 */ 459 protected boolean onKeyEvent(KeyEvent event) { 460 return false; 461 } 462 463 /** 464 * Gets the windows on the screen. This method returns only the windows 465 * that a sighted user can interact with, as opposed to all windows. 466 * For example, if there is a modal dialog shown and the user cannot touch 467 * anything behind it, then only the modal window will be reported 468 * (assuming it is the top one). For convenience the returned windows 469 * are ordered in a descending layer order, which is the windows that 470 * are higher in the Z-order are reported first. Since the user can always 471 * interact with the window that has input focus by typing, the focused 472 * window is always returned (even if covered by a modal window). 473 * <p> 474 * <strong>Note:</strong> In order to access the windows your service has 475 * to declare the capability to retrieve window content by setting the 476 * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent} 477 * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}. 478 * Also the service has to opt-in to retrieve the interactive windows by 479 * setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} 480 * flag. 481 * </p> 482 * 483 * @return The windows if there are windows and the service is can retrieve 484 * them, otherwise an empty list. 485 */ 486 public List<AccessibilityWindowInfo> getWindows() { 487 return AccessibilityInteractionClient.getInstance().getWindows(mConnectionId); 488 } 489 490 /** 491 * Gets the root node in the currently active window if this service 492 * can retrieve window content. The active window is the one that the user 493 * is currently touching or the window with input focus, if the user is not 494 * touching any window. 495 * <p> 496 * <strong>Note:</strong> In order to access the root node your service has 497 * to declare the capability to retrieve window content by setting the 498 * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent} 499 * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}. 500 * </p> 501 * 502 * @return The root node if this service can retrieve window content. 503 */ 504 public AccessibilityNodeInfo getRootInActiveWindow() { 505 return AccessibilityInteractionClient.getInstance().getRootInActiveWindow(mConnectionId); 506 } 507 508 /** 509 * Performs a global action. Such an action can be performed 510 * at any moment regardless of the current application or user 511 * location in that application. For example going back, going 512 * home, opening recents, etc. 513 * 514 * @param action The action to perform. 515 * @return Whether the action was successfully performed. 516 * 517 * @see #GLOBAL_ACTION_BACK 518 * @see #GLOBAL_ACTION_HOME 519 * @see #GLOBAL_ACTION_NOTIFICATIONS 520 * @see #GLOBAL_ACTION_RECENTS 521 */ 522 public final boolean performGlobalAction(int action) { 523 IAccessibilityServiceConnection connection = 524 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); 525 if (connection != null) { 526 try { 527 return connection.performGlobalAction(action); 528 } catch (RemoteException re) { 529 Log.w(LOG_TAG, "Error while calling performGlobalAction", re); 530 } 531 } 532 return false; 533 } 534 535 /** 536 * Find the view that has the specified focus type. The search is performed 537 * across all windows. 538 * <p> 539 * <strong>Note:</strong> In order to access the windows your service has 540 * to declare the capability to retrieve window content by setting the 541 * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent} 542 * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}. 543 * Also the service has to opt-in to retrieve the interactive windows by 544 * setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} 545 * flag.Otherwise, the search will be performed only in the active window. 546 * </p> 547 * 548 * @param focus The focus to find. One of {@link AccessibilityNodeInfo#FOCUS_INPUT} or 549 * {@link AccessibilityNodeInfo#FOCUS_ACCESSIBILITY}. 550 * @return The node info of the focused view or null. 551 * 552 * @see AccessibilityNodeInfo#FOCUS_INPUT 553 * @see AccessibilityNodeInfo#FOCUS_ACCESSIBILITY 554 */ 555 public AccessibilityNodeInfo findFocus(int focus) { 556 return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId, 557 AccessibilityNodeInfo.ANY_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID, focus); 558 } 559 560 /** 561 * Gets the an {@link AccessibilityServiceInfo} describing this 562 * {@link AccessibilityService}. This method is useful if one wants 563 * to change some of the dynamically configurable properties at 564 * runtime. 565 * 566 * @return The accessibility service info. 567 * 568 * @see AccessibilityServiceInfo 569 */ 570 public final AccessibilityServiceInfo getServiceInfo() { 571 IAccessibilityServiceConnection connection = 572 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); 573 if (connection != null) { 574 try { 575 return connection.getServiceInfo(); 576 } catch (RemoteException re) { 577 Log.w(LOG_TAG, "Error while getting AccessibilityServiceInfo", re); 578 } 579 } 580 return null; 581 } 582 583 /** 584 * Sets the {@link AccessibilityServiceInfo} that describes this service. 585 * <p> 586 * Note: You can call this method any time but the info will be picked up after 587 * the system has bound to this service and when this method is called thereafter. 588 * 589 * @param info The info. 590 */ 591 public final void setServiceInfo(AccessibilityServiceInfo info) { 592 mInfo = info; 593 sendServiceInfo(); 594 } 595 596 /** 597 * Sets the {@link AccessibilityServiceInfo} for this service if the latter is 598 * properly set and there is an {@link IAccessibilityServiceConnection} to the 599 * AccessibilityManagerService. 600 */ 601 private void sendServiceInfo() { 602 IAccessibilityServiceConnection connection = 603 AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); 604 if (mInfo != null && connection != null) { 605 try { 606 connection.setServiceInfo(mInfo); 607 mInfo = null; 608 AccessibilityInteractionClient.getInstance().clearCache(); 609 } catch (RemoteException re) { 610 Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re); 611 } 612 } 613 } 614 615 /** 616 * Implement to return the implementation of the internal accessibility 617 * service interface. 618 */ 619 @Override 620 public final IBinder onBind(Intent intent) { 621 return new IAccessibilityServiceClientWrapper(this, getMainLooper(), new Callbacks() { 622 @Override 623 public void onServiceConnected() { 624 AccessibilityService.this.onServiceConnected(); 625 } 626 627 @Override 628 public void onInterrupt() { 629 AccessibilityService.this.onInterrupt(); 630 } 631 632 @Override 633 public void onAccessibilityEvent(AccessibilityEvent event) { 634 AccessibilityService.this.onAccessibilityEvent(event); 635 } 636 637 @Override 638 public void onSetConnectionId( int connectionId) { 639 mConnectionId = connectionId; 640 } 641 642 @Override 643 public boolean onGesture(int gestureId) { 644 return AccessibilityService.this.onGesture(gestureId); 645 } 646 647 @Override 648 public boolean onKeyEvent(KeyEvent event) { 649 return AccessibilityService.this.onKeyEvent(event); 650 } 651 }); 652 } 653 654 /** 655 * Implements the internal {@link IAccessibilityServiceClient} interface to convert 656 * incoming calls to it back to calls on an {@link AccessibilityService}. 657 * 658 * @hide 659 */ 660 public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub 661 implements HandlerCaller.Callback { 662 663 static final int NO_ID = -1; 664 665 private static final int DO_SET_SET_CONNECTION = 1; 666 private static final int DO_ON_INTERRUPT = 2; 667 private static final int DO_ON_ACCESSIBILITY_EVENT = 3; 668 private static final int DO_ON_GESTURE = 4; 669 private static final int DO_CLEAR_ACCESSIBILITY_CACHE = 5; 670 private static final int DO_ON_KEY_EVENT = 6; 671 private static final int DO_ON_WINDOWS_CHANGED = 7; 672 673 private final HandlerCaller mCaller; 674 675 private final Callbacks mCallback; 676 677 private int mConnectionId; 678 679 public IAccessibilityServiceClientWrapper(Context context, Looper looper, 680 Callbacks callback) { 681 mCallback = callback; 682 mCaller = new HandlerCaller(context, looper, this, true /*asyncHandler*/); 683 } 684 685 public void setConnection(IAccessibilityServiceConnection connection, int connectionId) { 686 Message message = mCaller.obtainMessageIO(DO_SET_SET_CONNECTION, connectionId, 687 connection); 688 mCaller.sendMessage(message); 689 } 690 691 public void onInterrupt() { 692 Message message = mCaller.obtainMessage(DO_ON_INTERRUPT); 693 mCaller.sendMessage(message); 694 } 695 696 public void onAccessibilityEvent(AccessibilityEvent event) { 697 Message message = mCaller.obtainMessageO(DO_ON_ACCESSIBILITY_EVENT, event); 698 mCaller.sendMessage(message); 699 } 700 701 public void onGesture(int gestureId) { 702 Message message = mCaller.obtainMessageI(DO_ON_GESTURE, gestureId); 703 mCaller.sendMessage(message); 704 } 705 706 public void clearAccessibilityCache() { 707 Message message = mCaller.obtainMessage(DO_CLEAR_ACCESSIBILITY_CACHE); 708 mCaller.sendMessage(message); 709 } 710 711 @Override 712 public void onKeyEvent(KeyEvent event, int sequence) { 713 Message message = mCaller.obtainMessageIO(DO_ON_KEY_EVENT, sequence, event); 714 mCaller.sendMessage(message); 715 } 716 717 @Override 718 public void onWindowsChanged(int[] windowIds) { 719 Message message = mCaller.obtainMessageO(DO_ON_WINDOWS_CHANGED, windowIds); 720 mCaller.sendMessage(message); 721 } 722 723 @Override 724 public void executeMessage(Message message) { 725 switch (message.what) { 726 case DO_ON_ACCESSIBILITY_EVENT: { 727 AccessibilityEvent event = (AccessibilityEvent) message.obj; 728 if (event != null) { 729 AccessibilityInteractionClient.getInstance().onAccessibilityEvent(event); 730 mCallback.onAccessibilityEvent(event); 731 // Make sure the event is recycled. 732 try { 733 event.recycle(); 734 } catch (IllegalStateException ise) { 735 /* ignore - best effort */ 736 } 737 } 738 } return; 739 740 case DO_ON_INTERRUPT: { 741 mCallback.onInterrupt(); 742 } return; 743 744 case DO_SET_SET_CONNECTION: { 745 mConnectionId = message.arg1; 746 IAccessibilityServiceConnection connection = 747 (IAccessibilityServiceConnection) message.obj; 748 if (connection != null) { 749 AccessibilityInteractionClient.getInstance().addConnection(mConnectionId, 750 connection); 751 mCallback.onSetConnectionId(mConnectionId); 752 mCallback.onServiceConnected(); 753 } else { 754 AccessibilityInteractionClient.getInstance().removeConnection( 755 mConnectionId); 756 AccessibilityInteractionClient.getInstance().clearCache(); 757 mCallback.onSetConnectionId(AccessibilityInteractionClient.NO_ID); 758 } 759 } return; 760 761 case DO_ON_GESTURE: { 762 final int gestureId = message.arg1; 763 mCallback.onGesture(gestureId); 764 } return; 765 766 case DO_CLEAR_ACCESSIBILITY_CACHE: { 767 AccessibilityInteractionClient.getInstance().clearCache(); 768 } return; 769 770 case DO_ON_KEY_EVENT: { 771 KeyEvent event = (KeyEvent) message.obj; 772 try { 773 IAccessibilityServiceConnection connection = AccessibilityInteractionClient 774 .getInstance().getConnection(mConnectionId); 775 if (connection != null) { 776 final boolean result = mCallback.onKeyEvent(event); 777 final int sequence = message.arg1; 778 try { 779 connection.setOnKeyEventResult(result, sequence); 780 } catch (RemoteException re) { 781 /* ignore */ 782 } 783 } 784 } finally { 785 // Make sure the event is recycled. 786 try { 787 event.recycle(); 788 } catch (IllegalStateException ise) { 789 /* ignore - best effort */ 790 } 791 } 792 } return; 793 794 case DO_ON_WINDOWS_CHANGED: { 795 final int[] windowIds = (int[]) message.obj; 796 797 // Update the cached windows first. 798 // NOTE: The cache will hold on to the windows so do not recycle. 799 if (windowIds != null) { 800 AccessibilityInteractionClient.getInstance().removeWindows(windowIds); 801 } 802 803 // Let the client know the windows changed. 804 AccessibilityEvent event = AccessibilityEvent.obtain( 805 AccessibilityEvent.TYPE_WINDOWS_CHANGED); 806 event.setEventTime(SystemClock.uptimeMillis()); 807 event.setSealed(true); 808 809 mCallback.onAccessibilityEvent(event); 810 811 // Make sure the event is recycled. 812 try { 813 event.recycle(); 814 } catch (IllegalStateException ise) { 815 /* ignore - best effort */ 816 } 817 } break; 818 819 default : 820 Log.w(LOG_TAG, "Unknown message type " + message.what); 821 } 822 } 823 } 824} 825