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