AccessibilityManagerService.java revision 9bf21873c900e7bf82d864172ab40527a48dae3e
1/* 2 ** Copyright 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 com.android.server.accessibility; 18 19import static android.accessibilityservice.AccessibilityServiceInfo.DEFAULT; 20import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS; 21 22import android.Manifest; 23import android.accessibilityservice.AccessibilityService; 24import android.accessibilityservice.AccessibilityServiceInfo; 25import android.accessibilityservice.IAccessibilityServiceClient; 26import android.accessibilityservice.IAccessibilityServiceConnection; 27import android.app.AlertDialog; 28import android.app.PendingIntent; 29import android.app.StatusBarManager; 30import android.content.BroadcastReceiver; 31import android.content.ComponentName; 32import android.content.ContentResolver; 33import android.content.Context; 34import android.content.DialogInterface; 35import android.content.DialogInterface.OnClickListener; 36import android.content.Intent; 37import android.content.IntentFilter; 38import android.content.ServiceConnection; 39import android.content.pm.PackageManager; 40import android.content.pm.ResolveInfo; 41import android.content.pm.ServiceInfo; 42import android.database.ContentObserver; 43import android.graphics.Rect; 44import android.hardware.input.InputManager; 45import android.net.Uri; 46import android.os.Binder; 47import android.os.Build; 48import android.os.Bundle; 49import android.os.Handler; 50import android.os.IBinder; 51import android.os.Message; 52import android.os.RemoteException; 53import android.os.ServiceManager; 54import android.os.SystemClock; 55import android.provider.Settings; 56import android.text.TextUtils; 57import android.text.TextUtils.SimpleStringSplitter; 58import android.util.Slog; 59import android.util.SparseArray; 60import android.view.IWindow; 61import android.view.InputDevice; 62import android.view.KeyCharacterMap; 63import android.view.KeyEvent; 64import android.view.WindowManager; 65import android.view.accessibility.AccessibilityEvent; 66import android.view.accessibility.AccessibilityInteractionClient; 67import android.view.accessibility.AccessibilityManager; 68import android.view.accessibility.AccessibilityNodeInfo; 69import android.view.accessibility.IAccessibilityInteractionConnection; 70import android.view.accessibility.IAccessibilityInteractionConnectionCallback; 71import android.view.accessibility.IAccessibilityManager; 72import android.view.accessibility.IAccessibilityManagerClient; 73 74import com.android.internal.R; 75import com.android.internal.content.PackageMonitor; 76import com.android.server.wm.WindowManagerService; 77 78import org.xmlpull.v1.XmlPullParserException; 79 80import java.io.IOException; 81import java.util.ArrayList; 82import java.util.Arrays; 83import java.util.HashMap; 84import java.util.HashSet; 85import java.util.Iterator; 86import java.util.List; 87import java.util.Map; 88import java.util.Set; 89 90/** 91 * This class is instantiated by the system as a system level service and can be 92 * accessed only by the system. The task of this service is to be a centralized 93 * event dispatch for {@link AccessibilityEvent}s generated across all processes 94 * on the device. Events are dispatched to {@link AccessibilityService}s. 95 * 96 * @hide 97 */ 98public class AccessibilityManagerService extends IAccessibilityManager.Stub { 99 100 private static final boolean DEBUG = false; 101 102 private static final String LOG_TAG = "AccessibilityManagerService"; 103 104 private static final String FUNCTION_REGISTER_UI_TEST_AUTOMATION_SERVICE = 105 "registerUiTestAutomationService"; 106 107 private static final int OWN_PROCESS_ID = android.os.Process.myPid(); 108 109 private static final int MSG_SHOW_ENABLE_TOUCH_EXPLORATION_DIALOG = 1; 110 111 private static int sIdCounter = 0; 112 113 private static int sNextWindowId; 114 115 final Context mContext; 116 117 final Object mLock = new Object(); 118 119 final List<Service> mServices = new ArrayList<Service>(); 120 121 final List<IAccessibilityManagerClient> mClients = 122 new ArrayList<IAccessibilityManagerClient>(); 123 124 final Map<ComponentName, Service> mComponentNameToServiceMap = new HashMap<ComponentName, Service>(); 125 126 private final List<AccessibilityServiceInfo> mInstalledServices = new ArrayList<AccessibilityServiceInfo>(); 127 128 private final Set<ComponentName> mEnabledServices = new HashSet<ComponentName>(); 129 130 private final SparseArray<AccessibilityConnectionWrapper> mWindowIdToInteractionConnectionWrapperMap = 131 new SparseArray<AccessibilityConnectionWrapper>(); 132 133 private final SparseArray<IBinder> mWindowIdToWindowTokenMap = new SparseArray<IBinder>(); 134 135 private final SimpleStringSplitter mStringColonSplitter = new SimpleStringSplitter(':'); 136 137 private final Rect mTempRect = new Rect(); 138 139 private PackageManager mPackageManager; 140 141 private int mHandledFeedbackTypes = 0; 142 143 private boolean mIsAccessibilityEnabled; 144 145 private AccessibilityInputFilter mInputFilter; 146 147 private boolean mHasInputFilter; 148 149 private final List<AccessibilityServiceInfo> mEnabledServicesForFeedbackTempList = new ArrayList<AccessibilityServiceInfo>(); 150 151 private boolean mIsTouchExplorationEnabled; 152 153 private final WindowManagerService mWindowManagerService; 154 155 private final SecurityPolicy mSecurityPolicy; 156 157 private final MainHanler mMainHandler; 158 159 private Service mUiAutomationService; 160 161 private Service mQueryBridge; 162 163 private boolean mTouchExplorationGestureEnded; 164 165 private boolean mTouchExplorationGestureStarted; 166 167 /** 168 * Creates a new instance. 169 * 170 * @param context A {@link Context} instance. 171 */ 172 public AccessibilityManagerService(Context context) { 173 mContext = context; 174 mPackageManager = mContext.getPackageManager(); 175 mWindowManagerService = (WindowManagerService) ServiceManager.getService( 176 Context.WINDOW_SERVICE); 177 mSecurityPolicy = new SecurityPolicy(); 178 mMainHandler = new MainHanler(); 179 registerPackageChangeAndBootCompletedBroadcastReceiver(); 180 registerSettingsContentObservers(); 181 } 182 183 /** 184 * Registers a {@link BroadcastReceiver} for the events of 185 * adding/changing/removing/restarting a package and boot completion. 186 */ 187 private void registerPackageChangeAndBootCompletedBroadcastReceiver() { 188 Context context = mContext; 189 190 PackageMonitor monitor = new PackageMonitor() { 191 @Override 192 public void onSomePackagesChanged() { 193 synchronized (mLock) { 194 populateAccessibilityServiceListLocked(); 195 manageServicesLocked(); 196 } 197 } 198 199 @Override 200 public void onPackageRemoved(String packageName, int uid) { 201 synchronized (mLock) { 202 Iterator<ComponentName> it = mEnabledServices.iterator(); 203 while (it.hasNext()) { 204 ComponentName comp = it.next(); 205 String compPkg = comp.getPackageName(); 206 if (compPkg.equals(packageName)) { 207 it.remove(); 208 updateEnabledAccessibilitySerivcesSettingLocked(mEnabledServices); 209 return; 210 } 211 } 212 } 213 } 214 215 @Override 216 public boolean onHandleForceStop(Intent intent, String[] packages, 217 int uid, boolean doit) { 218 synchronized (mLock) { 219 boolean changed = false; 220 Iterator<ComponentName> it = mEnabledServices.iterator(); 221 while (it.hasNext()) { 222 ComponentName comp = it.next(); 223 String compPkg = comp.getPackageName(); 224 for (String pkg : packages) { 225 if (compPkg.equals(pkg)) { 226 if (!doit) { 227 return true; 228 } 229 it.remove(); 230 changed = true; 231 } 232 } 233 } 234 if (changed) { 235 updateEnabledAccessibilitySerivcesSettingLocked(mEnabledServices); 236 } 237 return false; 238 } 239 } 240 241 @Override 242 public void onReceive(Context context, Intent intent) { 243 if (intent.getAction() == Intent.ACTION_BOOT_COMPLETED) { 244 synchronized (mLock) { 245 populateAccessibilityServiceListLocked(); 246 handleAccessibilityEnabledSettingChangedLocked(); 247 handleTouchExplorationEnabledSettingChangedLocked(); 248 updateInputFilterLocked(); 249 sendStateToClientsLocked(); 250 } 251 252 return; 253 } 254 255 super.onReceive(context, intent); 256 } 257 258 private void updateEnabledAccessibilitySerivcesSettingLocked( 259 Set<ComponentName> enabledServices) { 260 Iterator<ComponentName> it = enabledServices.iterator(); 261 StringBuilder str = new StringBuilder(); 262 while (it.hasNext()) { 263 if (str.length() > 0) { 264 str.append(':'); 265 } 266 str.append(it.next().flattenToShortString()); 267 } 268 Settings.Secure.putString(mContext.getContentResolver(), 269 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, 270 str.toString()); 271 } 272 }; 273 274 // package changes 275 monitor.register(context, null, true); 276 277 // boot completed 278 IntentFilter bootFiler = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); 279 mContext.registerReceiver(monitor, bootFiler, null, monitor.getRegisteredHandler()); 280 } 281 282 /** 283 * {@link ContentObserver}s for {@link Settings.Secure#ACCESSIBILITY_ENABLED} 284 * and {@link Settings.Secure#ENABLED_ACCESSIBILITY_SERVICES} settings. 285 */ 286 private void registerSettingsContentObservers() { 287 ContentResolver contentResolver = mContext.getContentResolver(); 288 289 Uri accessibilityEnabledUri = Settings.Secure.getUriFor( 290 Settings.Secure.ACCESSIBILITY_ENABLED); 291 contentResolver.registerContentObserver(accessibilityEnabledUri, false, 292 new ContentObserver(new Handler()) { 293 @Override 294 public void onChange(boolean selfChange) { 295 super.onChange(selfChange); 296 synchronized (mLock) { 297 handleAccessibilityEnabledSettingChangedLocked(); 298 updateInputFilterLocked(); 299 sendStateToClientsLocked(); 300 } 301 } 302 }); 303 304 Uri touchExplorationRequestedUri = Settings.Secure.getUriFor( 305 Settings.Secure.TOUCH_EXPLORATION_ENABLED); 306 contentResolver.registerContentObserver(touchExplorationRequestedUri, false, 307 new ContentObserver(new Handler()) { 308 @Override 309 public void onChange(boolean selfChange) { 310 super.onChange(selfChange); 311 synchronized (mLock) { 312 handleTouchExplorationEnabledSettingChangedLocked(); 313 updateInputFilterLocked(); 314 sendStateToClientsLocked(); 315 } 316 } 317 }); 318 319 Uri accessibilityServicesUri = 320 Settings.Secure.getUriFor(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); 321 contentResolver.registerContentObserver(accessibilityServicesUri, false, 322 new ContentObserver(new Handler()) { 323 @Override 324 public void onChange(boolean selfChange) { 325 super.onChange(selfChange); 326 synchronized (mLock) { 327 manageServicesLocked(); 328 } 329 } 330 }); 331 } 332 333 public int addClient(IAccessibilityManagerClient client) throws RemoteException { 334 synchronized (mLock) { 335 final IAccessibilityManagerClient addedClient = client; 336 mClients.add(addedClient); 337 // Clients are registered all the time until their process is 338 // killed, therefore we do not have a corresponding unlinkToDeath. 339 client.asBinder().linkToDeath(new DeathRecipient() { 340 public void binderDied() { 341 synchronized (mLock) { 342 addedClient.asBinder().unlinkToDeath(this, 0); 343 mClients.remove(addedClient); 344 } 345 } 346 }, 0); 347 return getState(); 348 } 349 } 350 351 public boolean sendAccessibilityEvent(AccessibilityEvent event) { 352 // The event for gesture start should be strictly before the 353 // first hover enter event for the gesture. 354 if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_HOVER_ENTER 355 && mTouchExplorationGestureStarted) { 356 mTouchExplorationGestureStarted = false; 357 AccessibilityEvent gestureStartEvent = AccessibilityEvent.obtain( 358 AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START); 359 sendAccessibilityEvent(gestureStartEvent); 360 } 361 362 synchronized (mLock) { 363 if (mSecurityPolicy.canDispatchAccessibilityEvent(event)) { 364 mSecurityPolicy.updateActiveWindowAndEventSourceLocked(event); 365 notifyAccessibilityServicesDelayedLocked(event, false); 366 notifyAccessibilityServicesDelayedLocked(event, true); 367 } 368 } 369 370 // The event for gesture end should be strictly after the 371 // last hover exit event for the gesture. 372 if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT 373 && mTouchExplorationGestureEnded) { 374 mTouchExplorationGestureEnded = false; 375 AccessibilityEvent gestureEndEvent = AccessibilityEvent.obtain( 376 AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END); 377 sendAccessibilityEvent(gestureEndEvent); 378 } 379 380 event.recycle(); 381 mHandledFeedbackTypes = 0; 382 383 return (OWN_PROCESS_ID != Binder.getCallingPid()); 384 } 385 386 public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList() { 387 synchronized (mLock) { 388 return mInstalledServices; 389 } 390 } 391 392 public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType) { 393 List<AccessibilityServiceInfo> result = mEnabledServicesForFeedbackTempList; 394 result.clear(); 395 List<Service> services = mServices; 396 synchronized (mLock) { 397 while (feedbackType != 0) { 398 final int feedbackTypeBit = (1 << Integer.numberOfTrailingZeros(feedbackType)); 399 feedbackType &= ~feedbackTypeBit; 400 final int serviceCount = services.size(); 401 for (int i = 0; i < serviceCount; i++) { 402 Service service = services.get(i); 403 if ((service.mFeedbackType & feedbackTypeBit) != 0) { 404 result.add(service.mAccessibilityServiceInfo); 405 } 406 } 407 } 408 } 409 return result; 410 } 411 412 public void interrupt() { 413 synchronized (mLock) { 414 for (int i = 0, count = mServices.size(); i < count; i++) { 415 Service service = mServices.get(i); 416 try { 417 service.mServiceInterface.onInterrupt(); 418 } catch (RemoteException re) { 419 Slog.e(LOG_TAG, "Error during sending interrupt request to " 420 + service.mService, re); 421 } 422 } 423 } 424 } 425 426 public int addAccessibilityInteractionConnection(IWindow windowToken, 427 IAccessibilityInteractionConnection connection) throws RemoteException { 428 synchronized (mLock) { 429 final IWindow addedWindowToken = windowToken; 430 final int windowId = sNextWindowId++; 431 AccessibilityConnectionWrapper wrapper = new AccessibilityConnectionWrapper(windowId, 432 connection); 433 wrapper.linkToDeath(); 434 mWindowIdToWindowTokenMap.put(windowId, addedWindowToken.asBinder()); 435 mWindowIdToInteractionConnectionWrapperMap.put(windowId, wrapper); 436 if (DEBUG) { 437 Slog.i(LOG_TAG, "Adding interaction connection to windowId: " + windowId); 438 } 439 return windowId; 440 } 441 } 442 443 public void removeAccessibilityInteractionConnection(IWindow windowToken) { 444 synchronized (mLock) { 445 final int count = mWindowIdToWindowTokenMap.size(); 446 for (int i = 0; i < count; i++) { 447 if (mWindowIdToWindowTokenMap.valueAt(i) == windowToken.asBinder()) { 448 final int windowId = mWindowIdToWindowTokenMap.keyAt(i); 449 AccessibilityConnectionWrapper wrapper = 450 mWindowIdToInteractionConnectionWrapperMap.get(windowId); 451 wrapper.unlinkToDeath(); 452 removeAccessibilityInteractionConnectionLocked(windowId); 453 return; 454 } 455 } 456 } 457 } 458 459 public void registerUiTestAutomationService(IAccessibilityServiceClient serviceClient, 460 AccessibilityServiceInfo accessibilityServiceInfo) { 461 mSecurityPolicy.enforceCallingPermission(Manifest.permission.RETRIEVE_WINDOW_CONTENT, 462 FUNCTION_REGISTER_UI_TEST_AUTOMATION_SERVICE); 463 ComponentName componentName = new ComponentName("foo.bar", 464 "AutomationAccessibilityService"); 465 synchronized (mLock) { 466 // If an automation services is connected to the system all services are stopped 467 // so the automation one is the only one running. Settings are not changed so when 468 // the automation service goes away the state is restored from the settings. 469 470 // Disable all services. 471 final int runningServiceCount = mServices.size(); 472 for (int i = 0; i < runningServiceCount; i++) { 473 Service runningService = mServices.get(i); 474 runningService.unbind(); 475 } 476 // If necessary enable accessibility and announce that. 477 if (!mIsAccessibilityEnabled) { 478 mIsAccessibilityEnabled = true; 479 sendStateToClientsLocked(); 480 } 481 } 482 // Hook the automation service up. 483 mUiAutomationService = new Service(componentName, accessibilityServiceInfo, true); 484 mUiAutomationService.onServiceConnected(componentName, serviceClient.asBinder()); 485 } 486 487 public void unregisterUiTestAutomationService(IAccessibilityServiceClient serviceClient) { 488 synchronized (mLock) { 489 // Automation service is not bound, so pretend it died to perform clean up. 490 if (mUiAutomationService != null 491 && mUiAutomationService.mServiceInterface == serviceClient) { 492 mUiAutomationService.binderDied(); 493 } 494 } 495 } 496 497 boolean onGesture(int gestureId) { 498 synchronized (mLock) { 499 boolean handled = notifyGestureLocked(gestureId, false); 500 if (!handled) { 501 handled = notifyGestureLocked(gestureId, true); 502 } 503 return handled; 504 } 505 } 506 507 /** 508 * Gets the bounds of the accessibility focus if the provided, 509 * point coordinates are within the currently active window 510 * and accessibility focus is found within the latter. 511 * 512 * @param x X coordinate. 513 * @param y Y coordinate 514 * @param outBounds The output to which to write the focus bounds. 515 * @return The accessibility focus rectangle if the point is in the 516 * window and the window has accessibility focus. 517 */ 518 boolean getAccessibilityFocusBounds(int x, int y, Rect outBounds) { 519 // Instead of keeping track of accessibility focus events per 520 // window to be able to find the focus in the active window, 521 // we take a stateless approach and look it up. This is fine 522 // since we do this only when the user clicks/long presses. 523 Service service = getQueryBridge(); 524 final int connectionId = service.mId; 525 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 526 client.addConnection(connectionId, service); 527 try { 528 AccessibilityNodeInfo root = AccessibilityInteractionClient.getInstance() 529 .getRootInActiveWindow(connectionId); 530 if (root == null) { 531 return false; 532 } 533 Rect bounds = mTempRect; 534 root.getBoundsInScreen(bounds); 535 if (!bounds.contains(x, y)) { 536 return false; 537 } 538 AccessibilityNodeInfo focus = root.findFocus( 539 AccessibilityNodeInfo.FOCUS_ACCESSIBILITY); 540 if (focus == null) { 541 return false; 542 } 543 focus.getBoundsInScreen(outBounds); 544 return true; 545 } finally { 546 client.removeConnection(connectionId); 547 } 548 } 549 550 private Service getQueryBridge() { 551 if (mQueryBridge == null) { 552 AccessibilityServiceInfo info = new AccessibilityServiceInfo(); 553 mQueryBridge = new Service(null, info, true); 554 } 555 return mQueryBridge; 556 } 557 558 public void touchExplorationGestureEnded() { 559 mTouchExplorationGestureEnded = true; 560 } 561 562 public void touchExplorationGestureStarted() { 563 mTouchExplorationGestureStarted = true; 564 } 565 566 private boolean notifyGestureLocked(int gestureId, boolean isDefault) { 567 // TODO: Now we are giving the gestures to the last enabled 568 // service that can handle them which is the last one 569 // in our list since we write the last enabled as the 570 // last record in the enabled services setting. Ideally, 571 // the user should make the call which service handles 572 // gestures. However, only one service should handle 573 // gestures to avoid user frustration when different 574 // behavior is observed from different combinations of 575 // enabled accessibility services. 576 for (int i = mServices.size() - 1; i >= 0; i--) { 577 Service service = mServices.get(i); 578 if (service.mReqeustTouchExplorationMode && service.mIsDefault == isDefault) { 579 service.notifyGesture(gestureId); 580 return true; 581 } 582 } 583 return false; 584 } 585 586 /** 587 * Removes an AccessibilityInteractionConnection. 588 * 589 * @param windowId The id of the window to which the connection is targeted. 590 */ 591 private void removeAccessibilityInteractionConnectionLocked(int windowId) { 592 mWindowIdToWindowTokenMap.remove(windowId); 593 mWindowIdToInteractionConnectionWrapperMap.remove(windowId); 594 if (DEBUG) { 595 Slog.i(LOG_TAG, "Removing interaction connection to windowId: " + windowId); 596 } 597 } 598 599 /** 600 * Populates the cached list of installed {@link AccessibilityService}s. 601 */ 602 private void populateAccessibilityServiceListLocked() { 603 mInstalledServices.clear(); 604 605 List<ResolveInfo> installedServices = mPackageManager.queryIntentServices( 606 new Intent(AccessibilityService.SERVICE_INTERFACE), 607 PackageManager.GET_SERVICES | PackageManager.GET_META_DATA); 608 609 for (int i = 0, count = installedServices.size(); i < count; i++) { 610 ResolveInfo resolveInfo = installedServices.get(i); 611 ServiceInfo serviceInfo = resolveInfo.serviceInfo; 612 // For now we are enforcing this if the target version is JellyBean or 613 // higher and in a later release we will enforce this for everyone. 614 if (serviceInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN 615 && !android.Manifest.permission.BIND_ACCESSIBILITY_SERVICE.equals( 616 serviceInfo.permission)) { 617 Slog.w(LOG_TAG, "Skipping accessibilty service " + new ComponentName( 618 serviceInfo.packageName, serviceInfo.name).flattenToShortString() 619 + ": it does not require the permission " 620 + android.Manifest.permission.BIND_ACCESSIBILITY_SERVICE); 621 continue; 622 } 623 AccessibilityServiceInfo accessibilityServiceInfo; 624 try { 625 accessibilityServiceInfo = new AccessibilityServiceInfo(resolveInfo, mContext); 626 mInstalledServices.add(accessibilityServiceInfo); 627 } catch (XmlPullParserException xppe) { 628 Slog.e(LOG_TAG, "Error while initializing AccessibilityServiceInfo", xppe); 629 } catch (IOException ioe) { 630 Slog.e(LOG_TAG, "Error while initializing AccessibilityServiceInfo", ioe); 631 } 632 } 633 } 634 635 /** 636 * Performs {@link AccessibilityService}s delayed notification. The delay is configurable 637 * and denotes the period after the last event before notifying the service. 638 * 639 * @param event The event. 640 * @param isDefault True to notify default listeners, not default services. 641 */ 642 private void notifyAccessibilityServicesDelayedLocked(AccessibilityEvent event, 643 boolean isDefault) { 644 try { 645 for (int i = 0, count = mServices.size(); i < count; i++) { 646 Service service = mServices.get(i); 647 648 if (service.mIsDefault == isDefault) { 649 if (canDispathEventLocked(service, event, mHandledFeedbackTypes)) { 650 mHandledFeedbackTypes |= service.mFeedbackType; 651 service.notifyAccessibilityEvent(event); 652 } 653 } 654 } 655 } catch (IndexOutOfBoundsException oobe) { 656 // An out of bounds exception can happen if services are going away 657 // as the for loop is running. If that happens, just bail because 658 // there are no more services to notify. 659 return; 660 } 661 } 662 663 /** 664 * Adds a service. 665 * 666 * @param service The service to add. 667 */ 668 private void tryAddServiceLocked(Service service) { 669 try { 670 if (mServices.contains(service) || !service.isConfigured()) { 671 return; 672 } 673 service.linkToOwnDeath(); 674 mServices.add(service); 675 mComponentNameToServiceMap.put(service.mComponentName, service); 676 updateInputFilterLocked(); 677 tryEnableTouchExploration(service); 678 } catch (RemoteException e) { 679 /* do nothing */ 680 } 681 } 682 683 /** 684 * Removes a service. 685 * 686 * @param service The service. 687 * @return True if the service was removed, false otherwise. 688 */ 689 private boolean tryRemoveServiceLocked(Service service) { 690 final boolean removed = mServices.remove(service); 691 if (!removed) { 692 return false; 693 } 694 mComponentNameToServiceMap.remove(service.mComponentName); 695 service.unlinkToOwnDeath(); 696 service.dispose(); 697 updateInputFilterLocked(); 698 tryDisableTouchExploration(service); 699 return removed; 700 } 701 702 /** 703 * Determines if given event can be dispatched to a service based on the package of the 704 * event source and already notified services for that event type. Specifically, a 705 * service is notified if it is interested in events from the package and no other service 706 * providing the same feedback type has been notified. Exception are services the 707 * provide generic feedback (feedback type left as a safety net for unforeseen feedback 708 * types) which are always notified. 709 * 710 * @param service The potential receiver. 711 * @param event The event. 712 * @param handledFeedbackTypes The feedback types for which services have been notified. 713 * @return True if the listener should be notified, false otherwise. 714 */ 715 private boolean canDispathEventLocked(Service service, AccessibilityEvent event, 716 int handledFeedbackTypes) { 717 718 if (!service.isConfigured()) { 719 return false; 720 } 721 722 if (!event.isImportantForAccessibility() 723 && !service.mIncludeNotImportantViews) { 724 return false; 725 } 726 727 int eventType = event.getEventType(); 728 if ((service.mEventTypes & eventType) != eventType) { 729 return false; 730 } 731 732 Set<String> packageNames = service.mPackageNames; 733 CharSequence packageName = event.getPackageName(); 734 735 if (packageNames.isEmpty() || packageNames.contains(packageName)) { 736 int feedbackType = service.mFeedbackType; 737 if ((handledFeedbackTypes & feedbackType) != feedbackType 738 || feedbackType == AccessibilityServiceInfo.FEEDBACK_GENERIC) { 739 return true; 740 } 741 } 742 743 return false; 744 } 745 746 /** 747 * Manages services by starting enabled ones and stopping disabled ones. 748 */ 749 private void manageServicesLocked() { 750 // While the UI automation service is running it takes over. 751 if (mUiAutomationService != null) { 752 return; 753 } 754 populateEnabledServicesLocked(mEnabledServices); 755 final int enabledInstalledServicesCount = updateServicesStateLocked(mInstalledServices, 756 mEnabledServices); 757 // No enabled installed services => disable accessibility to avoid 758 // sending accessibility events with no recipient across processes. 759 if (mIsAccessibilityEnabled && enabledInstalledServicesCount == 0) { 760 Settings.Secure.putInt(mContext.getContentResolver(), 761 Settings.Secure.ACCESSIBILITY_ENABLED, 0); 762 } 763 } 764 765 /** 766 * Unbinds all bound services. 767 */ 768 private void unbindAllServicesLocked() { 769 List<Service> services = mServices; 770 771 for (int i = 0, count = services.size(); i < count; i++) { 772 Service service = services.get(i); 773 if (service.unbind()) { 774 i--; 775 count--; 776 } 777 } 778 } 779 780 /** 781 * Populates a list with the {@link ComponentName}s of all enabled 782 * {@link AccessibilityService}s. 783 * 784 * @param enabledServices The list. 785 */ 786 private void populateEnabledServicesLocked(Set<ComponentName> enabledServices) { 787 enabledServices.clear(); 788 789 String servicesValue = Settings.Secure.getString(mContext.getContentResolver(), 790 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); 791 792 if (servicesValue != null) { 793 TextUtils.SimpleStringSplitter splitter = mStringColonSplitter; 794 splitter.setString(servicesValue); 795 while (splitter.hasNext()) { 796 String str = splitter.next(); 797 if (str == null || str.length() <= 0) { 798 continue; 799 } 800 ComponentName enabledService = ComponentName.unflattenFromString(str); 801 if (enabledService != null) { 802 enabledServices.add(enabledService); 803 } 804 } 805 } 806 } 807 808 /** 809 * Updates the state of each service by starting (or keeping running) enabled ones and 810 * stopping the rest. 811 * 812 * @param installedServices All installed {@link AccessibilityService}s. 813 * @param enabledServices The {@link ComponentName}s of the enabled services. 814 * @return The number of enabled installed services. 815 */ 816 private int updateServicesStateLocked(List<AccessibilityServiceInfo> installedServices, 817 Set<ComponentName> enabledServices) { 818 819 Map<ComponentName, Service> componentNameToServiceMap = mComponentNameToServiceMap; 820 boolean isEnabled = mIsAccessibilityEnabled; 821 822 int enabledInstalledServices = 0; 823 for (int i = 0, count = installedServices.size(); i < count; i++) { 824 AccessibilityServiceInfo installedService = installedServices.get(i); 825 ComponentName componentName = ComponentName.unflattenFromString( 826 installedService.getId()); 827 Service service = componentNameToServiceMap.get(componentName); 828 829 if (isEnabled) { 830 if (enabledServices.contains(componentName)) { 831 if (service == null) { 832 service = new Service(componentName, installedService, false); 833 } 834 service.bind(); 835 enabledInstalledServices++; 836 } else { 837 if (service != null) { 838 service.unbind(); 839 } 840 } 841 } else { 842 if (service != null) { 843 service.unbind(); 844 } 845 } 846 } 847 848 return enabledInstalledServices; 849 } 850 851 /** 852 * Sends the state to the clients. 853 */ 854 private void sendStateToClientsLocked() { 855 final int state = getState(); 856 for (int i = 0, count = mClients.size(); i < count; i++) { 857 try { 858 mClients.get(i).setState(state); 859 } catch (RemoteException re) { 860 mClients.remove(i); 861 count--; 862 i--; 863 } 864 } 865 } 866 867 /** 868 * Gets the current state as a set of flags. 869 * 870 * @return The state. 871 */ 872 private int getState() { 873 int state = 0; 874 if (mIsAccessibilityEnabled) { 875 state |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED; 876 } 877 // Touch exploration relies on enabled accessibility. 878 if (mIsAccessibilityEnabled && mIsTouchExplorationEnabled) { 879 state |= AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED; 880 } 881 return state; 882 } 883 884 /** 885 * Updates the touch exploration state. 886 */ 887 private void updateInputFilterLocked() { 888 if (mIsAccessibilityEnabled && mIsTouchExplorationEnabled) { 889 if (!mHasInputFilter) { 890 mHasInputFilter = true; 891 if (mInputFilter == null) { 892 mInputFilter = new AccessibilityInputFilter(mContext, this); 893 } 894 mWindowManagerService.setInputFilter(mInputFilter); 895 } 896 return; 897 } 898 if (mHasInputFilter) { 899 mHasInputFilter = false; 900 mWindowManagerService.setInputFilter(null); 901 } 902 } 903 904 /** 905 * Updated the state based on the accessibility enabled setting. 906 */ 907 private void handleAccessibilityEnabledSettingChangedLocked() { 908 mIsAccessibilityEnabled = Settings.Secure.getInt( 909 mContext.getContentResolver(), 910 Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1; 911 if (mIsAccessibilityEnabled) { 912 manageServicesLocked(); 913 } else { 914 unbindAllServicesLocked(); 915 } 916 } 917 918 /** 919 * Updates the state based on the touch exploration enabled setting. 920 */ 921 private void handleTouchExplorationEnabledSettingChangedLocked() { 922 mIsTouchExplorationEnabled = Settings.Secure.getInt( 923 mContext.getContentResolver(), 924 Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0) == 1; 925 } 926 927 private void tryEnableTouchExploration(final Service service) { 928 if (!mIsTouchExplorationEnabled && service.mRequestTouchExplorationMode) { 929 mMainHandler.obtainMessage(MSG_SHOW_ENABLE_TOUCH_EXPLORATION_DIALOG, 930 service).sendToTarget(); 931 } 932 } 933 934 private void tryDisableTouchExploration(Service service) { 935 if (mIsTouchExplorationEnabled && service.mReqeustTouchExplorationMode) { 936 synchronized (mLock) { 937 final int serviceCount = mServices.size(); 938 for (int i = 0; i < serviceCount; i++) { 939 Service other = mServices.get(i); 940 if (other != service && other.mRequestTouchExplorationMode) { 941 return; 942 } 943 } 944 Settings.Secure.putInt(mContext.getContentResolver(), 945 Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0); 946 } 947 } 948 } 949 950 private class AccessibilityConnectionWrapper implements DeathRecipient { 951 private final int mWindowId; 952 private final IAccessibilityInteractionConnection mConnection; 953 954 public AccessibilityConnectionWrapper(int windowId, 955 IAccessibilityInteractionConnection connection) { 956 mWindowId = windowId; 957 mConnection = connection; 958 } 959 960 public void linkToDeath() throws RemoteException { 961 mConnection.asBinder().linkToDeath(this, 0); 962 } 963 964 public void unlinkToDeath() { 965 mConnection.asBinder().unlinkToDeath(this, 0); 966 } 967 968 @Override 969 public void binderDied() { 970 unlinkToDeath(); 971 synchronized (mLock) { 972 removeAccessibilityInteractionConnectionLocked(mWindowId); 973 } 974 } 975 } 976 977 private class MainHanler extends Handler { 978 @Override 979 public void handleMessage(Message msg) { 980 final int type = msg.what; 981 switch (type) { 982 case MSG_SHOW_ENABLE_TOUCH_EXPLORATION_DIALOG: { 983 Service service = (Service) msg.obj; 984 String label = service.mResolveInfo.loadLabel( 985 mContext.getPackageManager()).toString(); 986 final AlertDialog dialog = new AlertDialog.Builder(mContext) 987 .setIcon(android.R.drawable.ic_dialog_alert) 988 .setPositiveButton(android.R.string.ok, new OnClickListener() { 989 @Override 990 public void onClick(DialogInterface dialog, int which) { 991 Settings.Secure.putInt(mContext.getContentResolver(), 992 Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1); 993 } 994 }) 995 .setNegativeButton(android.R.string.cancel, new OnClickListener() { 996 @Override 997 public void onClick(DialogInterface dialog, int which) { 998 dialog.dismiss(); 999 } 1000 }) 1001 .setTitle(R.string.enable_explore_by_touch_warning_title) 1002 .setMessage(mContext.getString( 1003 R.string.enable_explore_by_touch_warning_message, label)) 1004 .create(); 1005 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG); 1006 dialog.setCanceledOnTouchOutside(true); 1007 dialog.show(); 1008 } 1009 } 1010 } 1011 } 1012 1013 /** 1014 * This class represents an accessibility service. It stores all per service 1015 * data required for the service management, provides API for starting/stopping the 1016 * service and is responsible for adding/removing the service in the data structures 1017 * for service management. The class also exposes configuration interface that is 1018 * passed to the service it represents as soon it is bound. It also serves as the 1019 * connection for the service. 1020 */ 1021 class Service extends IAccessibilityServiceConnection.Stub 1022 implements ServiceConnection, DeathRecipient { 1023 1024 // We pick the MSB to avoid collision since accessibility event types are 1025 // used as message types allowing us to remove messages per event type. 1026 private static final int MSG_ON_GESTURE = 0x80000000; 1027 1028 int mId = 0; 1029 1030 AccessibilityServiceInfo mAccessibilityServiceInfo; 1031 1032 IBinder mService; 1033 1034 IAccessibilityServiceClient mServiceInterface; 1035 1036 int mEventTypes; 1037 1038 int mFeedbackType; 1039 1040 Set<String> mPackageNames = new HashSet<String>(); 1041 1042 boolean mIsDefault; 1043 1044 boolean mRequestTouchExplorationMode; 1045 1046 boolean mIncludeNotImportantViews; 1047 1048 long mNotificationTimeout; 1049 1050 ComponentName mComponentName; 1051 1052 Intent mIntent; 1053 1054 boolean mCanRetrieveScreenContent; 1055 1056 boolean mReqeustTouchExplorationMode; 1057 1058 boolean mIsAutomation; 1059 1060 final Rect mTempBounds = new Rect(); 1061 1062 final ResolveInfo mResolveInfo; 1063 1064 // the events pending events to be dispatched to this service 1065 final SparseArray<AccessibilityEvent> mPendingEvents = 1066 new SparseArray<AccessibilityEvent>(); 1067 1068 /** 1069 * Handler for delayed event dispatch. 1070 */ 1071 public Handler mHandler = new Handler() { 1072 @Override 1073 public void handleMessage(Message message) { 1074 final int type = message.what; 1075 switch (type) { 1076 case MSG_ON_GESTURE: { 1077 final int gestureId = message.arg1; 1078 notifyGestureInternal(gestureId); 1079 } break; 1080 default: { 1081 final int eventType = type; 1082 notifyAccessibilityEventInternal(eventType); 1083 } break; 1084 } 1085 } 1086 }; 1087 1088 public Service(ComponentName componentName, 1089 AccessibilityServiceInfo accessibilityServiceInfo, boolean isAutomation) { 1090 mResolveInfo = accessibilityServiceInfo.getResolveInfo(); 1091 mId = sIdCounter++; 1092 mComponentName = componentName; 1093 mAccessibilityServiceInfo = accessibilityServiceInfo; 1094 mIsAutomation = isAutomation; 1095 if (!isAutomation) { 1096 mCanRetrieveScreenContent = accessibilityServiceInfo.getCanRetrieveWindowContent(); 1097 mReqeustTouchExplorationMode = 1098 (accessibilityServiceInfo.flags 1099 & AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0; 1100 mIntent = new Intent().setComponent(mComponentName); 1101 mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL, 1102 com.android.internal.R.string.accessibility_binding_label); 1103 mIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity( 1104 mContext, 0, new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS), 0)); 1105 } else { 1106 mCanRetrieveScreenContent = true; 1107 } 1108 setDynamicallyConfigurableProperties(accessibilityServiceInfo); 1109 } 1110 1111 public void setDynamicallyConfigurableProperties(AccessibilityServiceInfo info) { 1112 mEventTypes = info.eventTypes; 1113 mFeedbackType = info.feedbackType; 1114 String[] packageNames = info.packageNames; 1115 if (packageNames != null) { 1116 mPackageNames.addAll(Arrays.asList(packageNames)); 1117 } 1118 mNotificationTimeout = info.notificationTimeout; 1119 mIsDefault = (info.flags & DEFAULT) != 0; 1120 1121 if (mIsAutomation || info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion 1122 >= Build.VERSION_CODES.JELLY_BEAN) { 1123 mIncludeNotImportantViews = 1124 (info.flags & FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) != 0; 1125 } 1126 1127 mRequestTouchExplorationMode = (info.flags 1128 & AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0; 1129 1130 synchronized (mLock) { 1131 tryAddServiceLocked(this); 1132 } 1133 } 1134 1135 /** 1136 * Binds to the accessibility service. 1137 * 1138 * @return True if binding is successful. 1139 */ 1140 public boolean bind() { 1141 if (!mIsAutomation && mService == null) { 1142 return mContext.bindService(mIntent, this, Context.BIND_AUTO_CREATE); 1143 } 1144 return false; 1145 } 1146 1147 /** 1148 * Unbinds form the accessibility service and removes it from the data 1149 * structures for service management. 1150 * 1151 * @return True if unbinding is successful. 1152 */ 1153 public boolean unbind() { 1154 if (mService != null) { 1155 synchronized (mLock) { 1156 tryRemoveServiceLocked(this); 1157 } 1158 if (!mIsAutomation) { 1159 mContext.unbindService(this); 1160 } 1161 return true; 1162 } 1163 return false; 1164 } 1165 1166 /** 1167 * Returns if the service is configured i.e. at least event types of interest 1168 * and feedback type must be set. 1169 * 1170 * @return True if the service is configured, false otherwise. 1171 */ 1172 public boolean isConfigured() { 1173 return (mEventTypes != 0 && mFeedbackType != 0 && mService != null); 1174 } 1175 1176 @Override 1177 public AccessibilityServiceInfo getServiceInfo() { 1178 synchronized (mLock) { 1179 return mAccessibilityServiceInfo; 1180 } 1181 } 1182 1183 @Override 1184 public void setServiceInfo(AccessibilityServiceInfo info) { 1185 synchronized (mLock) { 1186 // If the XML manifest had data to configure the service its info 1187 // should be already set. In such a case update only the dynamically 1188 // configurable properties. 1189 AccessibilityServiceInfo oldInfo = mAccessibilityServiceInfo; 1190 if (oldInfo != null) { 1191 oldInfo.updateDynamicallyConfigurableProperties(info); 1192 setDynamicallyConfigurableProperties(oldInfo); 1193 } else { 1194 setDynamicallyConfigurableProperties(info); 1195 } 1196 } 1197 } 1198 1199 @Override 1200 public void onServiceConnected(ComponentName componentName, IBinder service) { 1201 mService = service; 1202 mServiceInterface = IAccessibilityServiceClient.Stub.asInterface(service); 1203 try { 1204 mServiceInterface.setConnection(this, mId); 1205 synchronized (mLock) { 1206 tryAddServiceLocked(this); 1207 } 1208 } catch (RemoteException re) { 1209 Slog.w(LOG_TAG, "Error while setting Controller for service: " + service, re); 1210 } 1211 } 1212 1213 @Override 1214 public float findAccessibilityNodeInfoByViewId(int accessibilityWindowId, 1215 long accessibilityNodeId, int viewId, int interactionId, 1216 IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) 1217 throws RemoteException { 1218 final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId); 1219 IAccessibilityInteractionConnection connection = null; 1220 synchronized (mLock) { 1221 mSecurityPolicy.enforceCanRetrieveWindowContent(this); 1222 final boolean permissionGranted = mSecurityPolicy.canRetrieveWindowContent(this); 1223 if (!permissionGranted) { 1224 return 0; 1225 } else { 1226 connection = getConnectionLocked(resolvedWindowId); 1227 if (connection == null) { 1228 return 0; 1229 } 1230 } 1231 } 1232 final int flags = (mIncludeNotImportantViews) ? 1233 AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0; 1234 final int interrogatingPid = Binder.getCallingPid(); 1235 final long identityToken = Binder.clearCallingIdentity(); 1236 try { 1237 connection.findAccessibilityNodeInfoByViewId(accessibilityNodeId, viewId, 1238 interactionId, callback, flags, interrogatingPid, interrogatingTid); 1239 } catch (RemoteException re) { 1240 if (DEBUG) { 1241 Slog.e(LOG_TAG, "Error findAccessibilityNodeInfoByViewId()."); 1242 } 1243 } finally { 1244 Binder.restoreCallingIdentity(identityToken); 1245 } 1246 return getCompatibilityScale(resolvedWindowId); 1247 } 1248 1249 @Override 1250 public float findAccessibilityNodeInfosByText(int accessibilityWindowId, 1251 long accessibilityNodeId, String text, int interactionId, 1252 IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) 1253 throws RemoteException { 1254 final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId); 1255 IAccessibilityInteractionConnection connection = null; 1256 synchronized (mLock) { 1257 mSecurityPolicy.enforceCanRetrieveWindowContent(this); 1258 final boolean permissionGranted = 1259 mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId); 1260 if (!permissionGranted) { 1261 return 0; 1262 } else { 1263 connection = getConnectionLocked(resolvedWindowId); 1264 if (connection == null) { 1265 return 0; 1266 } 1267 } 1268 } 1269 final int flags = (mIncludeNotImportantViews) ? 1270 AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0; 1271 final int interrogatingPid = Binder.getCallingPid(); 1272 final long identityToken = Binder.clearCallingIdentity(); 1273 try { 1274 connection.findAccessibilityNodeInfosByText(accessibilityNodeId, text, 1275 interactionId, callback, flags, interrogatingPid, interrogatingTid); 1276 } catch (RemoteException re) { 1277 if (DEBUG) { 1278 Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfosByText()"); 1279 } 1280 } finally { 1281 Binder.restoreCallingIdentity(identityToken); 1282 } 1283 return getCompatibilityScale(resolvedWindowId); 1284 } 1285 1286 @Override 1287 public float findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId, 1288 long accessibilityNodeId, int interactionId, 1289 IAccessibilityInteractionConnectionCallback callback, int flags, 1290 long interrogatingTid) throws RemoteException { 1291 final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId); 1292 IAccessibilityInteractionConnection connection = null; 1293 synchronized (mLock) { 1294 mSecurityPolicy.enforceCanRetrieveWindowContent(this); 1295 final boolean permissionGranted = 1296 mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId); 1297 if (!permissionGranted) { 1298 return 0; 1299 } else { 1300 connection = getConnectionLocked(resolvedWindowId); 1301 if (connection == null) { 1302 return 0; 1303 } 1304 } 1305 } 1306 final int allFlags = flags | ((mIncludeNotImportantViews) ? 1307 AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0); 1308 final int interrogatingPid = Binder.getCallingPid(); 1309 final long identityToken = Binder.clearCallingIdentity(); 1310 try { 1311 connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId, 1312 interactionId, callback, allFlags, interrogatingPid, interrogatingTid); 1313 } catch (RemoteException re) { 1314 if (DEBUG) { 1315 Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfoByAccessibilityId()"); 1316 } 1317 } finally { 1318 Binder.restoreCallingIdentity(identityToken); 1319 } 1320 return getCompatibilityScale(resolvedWindowId); 1321 } 1322 1323 @Override 1324 public float findFocus(int accessibilityWindowId, long accessibilityNodeId, 1325 int focusType, int interactionId, 1326 IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) 1327 throws RemoteException { 1328 final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId); 1329 IAccessibilityInteractionConnection connection = null; 1330 synchronized (mLock) { 1331 mSecurityPolicy.enforceCanRetrieveWindowContent(this); 1332 final boolean permissionGranted = 1333 mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId); 1334 if (!permissionGranted) { 1335 return 0; 1336 } else { 1337 connection = getConnectionLocked(resolvedWindowId); 1338 if (connection == null) { 1339 return 0; 1340 } 1341 } 1342 } 1343 final int flags = (mIncludeNotImportantViews) ? 1344 AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0; 1345 final int interrogatingPid = Binder.getCallingPid(); 1346 final long identityToken = Binder.clearCallingIdentity(); 1347 try { 1348 connection.findFocus(accessibilityNodeId, interactionId, focusType, callback, 1349 flags, interrogatingPid, interrogatingTid); 1350 } catch (RemoteException re) { 1351 if (DEBUG) { 1352 Slog.e(LOG_TAG, "Error calling findAccessibilityFocus()"); 1353 } 1354 } finally { 1355 Binder.restoreCallingIdentity(identityToken); 1356 } 1357 return getCompatibilityScale(resolvedWindowId); 1358 } 1359 1360 @Override 1361 public float focusSearch(int accessibilityWindowId, long accessibilityNodeId, 1362 int direction, int interactionId, 1363 IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) 1364 throws RemoteException { 1365 final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId); 1366 IAccessibilityInteractionConnection connection = null; 1367 synchronized (mLock) { 1368 mSecurityPolicy.enforceCanRetrieveWindowContent(this); 1369 final boolean permissionGranted = 1370 mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId); 1371 if (!permissionGranted) { 1372 return 0; 1373 } else { 1374 connection = getConnectionLocked(resolvedWindowId); 1375 if (connection == null) { 1376 return 0; 1377 } 1378 } 1379 } 1380 final int flags = (mIncludeNotImportantViews) ? 1381 AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0; 1382 final int interrogatingPid = Binder.getCallingPid(); 1383 final long identityToken = Binder.clearCallingIdentity(); 1384 try { 1385 connection.focusSearch(accessibilityNodeId, interactionId, direction, callback, 1386 flags, interrogatingPid, interrogatingTid); 1387 } catch (RemoteException re) { 1388 if (DEBUG) { 1389 Slog.e(LOG_TAG, "Error calling accessibilityFocusSearch()"); 1390 } 1391 } finally { 1392 Binder.restoreCallingIdentity(identityToken); 1393 } 1394 return getCompatibilityScale(resolvedWindowId); 1395 } 1396 1397 @Override 1398 public boolean performAccessibilityAction(int accessibilityWindowId, 1399 long accessibilityNodeId, int action, Bundle arguments, int interactionId, 1400 IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) { 1401 final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId); 1402 IAccessibilityInteractionConnection connection = null; 1403 synchronized (mLock) { 1404 final boolean permissionGranted = mSecurityPolicy.canPerformActionLocked(this, 1405 resolvedWindowId, action, arguments); 1406 if (!permissionGranted) { 1407 return false; 1408 } else { 1409 connection = getConnectionLocked(resolvedWindowId); 1410 if (connection == null) { 1411 return false; 1412 } 1413 } 1414 } 1415 final int flags = (mIncludeNotImportantViews) ? 1416 AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0; 1417 final int interrogatingPid = Binder.getCallingPid(); 1418 final long identityToken = Binder.clearCallingIdentity(); 1419 try { 1420 connection.performAccessibilityAction(accessibilityNodeId, action, arguments, 1421 interactionId, callback, flags, interrogatingPid, interrogatingTid); 1422 } catch (RemoteException re) { 1423 if (DEBUG) { 1424 Slog.e(LOG_TAG, "Error calling performAccessibilityAction()"); 1425 } 1426 } finally { 1427 Binder.restoreCallingIdentity(identityToken); 1428 } 1429 return true; 1430 } 1431 1432 public boolean performGlobalAction(int action) { 1433 switch (action) { 1434 case AccessibilityService.GLOBAL_ACTION_BACK: { 1435 sendDownAndUpKeyEvents(KeyEvent.KEYCODE_BACK); 1436 } return true; 1437 case AccessibilityService.GLOBAL_ACTION_HOME: { 1438 sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HOME); 1439 } return true; 1440 case AccessibilityService.GLOBAL_ACTION_RECENTS: { 1441 sendDownAndUpKeyEvents(KeyEvent.KEYCODE_APP_SWITCH); 1442 } return true; 1443 case AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS: { 1444 expandStatusBar(); 1445 } return true; 1446 } 1447 return false; 1448 } 1449 1450 public void onServiceDisconnected(ComponentName componentName) { 1451 /* do nothing - #binderDied takes care */ 1452 } 1453 1454 public void linkToOwnDeath() throws RemoteException { 1455 mService.linkToDeath(this, 0); 1456 } 1457 1458 public void unlinkToOwnDeath() { 1459 mService.unlinkToDeath(this, 0); 1460 } 1461 1462 public void dispose() { 1463 try { 1464 // Clear the proxy in the other process so this 1465 // IAccessibilityServiceConnection can be garbage collected. 1466 mServiceInterface.setConnection(null, mId); 1467 } catch (RemoteException re) { 1468 /* ignore */ 1469 } 1470 mService = null; 1471 mServiceInterface = null; 1472 } 1473 1474 public void binderDied() { 1475 synchronized (mLock) { 1476 // The death recipient is unregistered in tryRemoveServiceLocked 1477 tryRemoveServiceLocked(this); 1478 // We no longer have an automation service, so restore 1479 // the state based on values in the settings database. 1480 if (mIsAutomation) { 1481 mUiAutomationService = null; 1482 handleAccessibilityEnabledSettingChangedLocked(); 1483 handleTouchExplorationEnabledSettingChangedLocked(); 1484 updateInputFilterLocked(); 1485 sendStateToClientsLocked(); 1486 } 1487 } 1488 } 1489 1490 /** 1491 * Performs a notification for an {@link AccessibilityEvent}. 1492 * 1493 * @param event The event. 1494 */ 1495 public void notifyAccessibilityEvent(AccessibilityEvent event) { 1496 synchronized (mLock) { 1497 final int eventType = event.getEventType(); 1498 // Make a copy since during dispatch it is possible the event to 1499 // be modified to remove its source if the receiving service does 1500 // not have permission to access the window content. 1501 AccessibilityEvent newEvent = AccessibilityEvent.obtain(event); 1502 AccessibilityEvent oldEvent = mPendingEvents.get(eventType); 1503 mPendingEvents.put(eventType, newEvent); 1504 1505 final int what = eventType; 1506 if (oldEvent != null) { 1507 mHandler.removeMessages(what); 1508 oldEvent.recycle(); 1509 } 1510 1511 Message message = mHandler.obtainMessage(what); 1512 mHandler.sendMessageDelayed(message, mNotificationTimeout); 1513 } 1514 } 1515 1516 /** 1517 * Notifies an accessibility service client for a scheduled event given the event type. 1518 * 1519 * @param eventType The type of the event to dispatch. 1520 */ 1521 private void notifyAccessibilityEventInternal(int eventType) { 1522 IAccessibilityServiceClient listener; 1523 AccessibilityEvent event; 1524 1525 synchronized (mLock) { 1526 listener = mServiceInterface; 1527 1528 // If the service died/was disabled while the message for dispatching 1529 // the accessibility event was propagating the listener may be null. 1530 if (listener == null) { 1531 return; 1532 } 1533 1534 event = mPendingEvents.get(eventType); 1535 1536 // Check for null here because there is a concurrent scenario in which this 1537 // happens: 1) A binder thread calls notifyAccessibilityServiceDelayedLocked 1538 // which posts a message for dispatching an event. 2) The message is pulled 1539 // from the queue by the handler on the service thread and the latter is 1540 // just about to acquire the lock and call this method. 3) Now another binder 1541 // thread acquires the lock calling notifyAccessibilityServiceDelayedLocked 1542 // so the service thread waits for the lock; 4) The binder thread replaces 1543 // the event with a more recent one (assume the same event type) and posts a 1544 // dispatch request releasing the lock. 5) Now the main thread is unblocked and 1545 // dispatches the event which is removed from the pending ones. 6) And ... now 1546 // the service thread handles the last message posted by the last binder call 1547 // but the event is already dispatched and hence looking it up in the pending 1548 // ones yields null. This check is much simpler that keeping count for each 1549 // event type of each service to catch such a scenario since only one message 1550 // is processed at a time. 1551 if (event == null) { 1552 return; 1553 } 1554 1555 mPendingEvents.remove(eventType); 1556 if (mSecurityPolicy.canRetrieveWindowContent(this)) { 1557 event.setConnectionId(mId); 1558 } else { 1559 event.setSource(null); 1560 } 1561 event.setSealed(true); 1562 } 1563 1564 try { 1565 listener.onAccessibilityEvent(event); 1566 if (DEBUG) { 1567 Slog.i(LOG_TAG, "Event " + event + " sent to " + listener); 1568 } 1569 } catch (RemoteException re) { 1570 Slog.e(LOG_TAG, "Error during sending " + event + " to " + listener, re); 1571 } finally { 1572 event.recycle(); 1573 } 1574 } 1575 1576 public void notifyGesture(int gestureId) { 1577 mHandler.obtainMessage(MSG_ON_GESTURE, gestureId, 0).sendToTarget(); 1578 } 1579 1580 private void notifyGestureInternal(int gestureId) { 1581 IAccessibilityServiceClient listener = mServiceInterface; 1582 if (listener != null) { 1583 try { 1584 listener.onGesture(gestureId); 1585 } catch (RemoteException re) { 1586 Slog.e(LOG_TAG, "Error during sending gesture " + gestureId 1587 + " to " + mService, re); 1588 } 1589 } 1590 } 1591 1592 private void sendDownAndUpKeyEvents(int keyCode) { 1593 final long token = Binder.clearCallingIdentity(); 1594 1595 // Inject down. 1596 final long downTime = SystemClock.uptimeMillis(); 1597 KeyEvent down = KeyEvent.obtain(downTime, downTime, KeyEvent.ACTION_DOWN, keyCode, 0, 0, 1598 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FROM_SYSTEM, 1599 InputDevice.SOURCE_KEYBOARD, null); 1600 InputManager.getInstance().injectInputEvent(down, 1601 InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); 1602 down.recycle(); 1603 1604 // Inject up. 1605 final long upTime = SystemClock.uptimeMillis(); 1606 KeyEvent up = KeyEvent.obtain(downTime, upTime, KeyEvent.ACTION_UP, keyCode, 0, 0, 1607 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FROM_SYSTEM, 1608 InputDevice.SOURCE_KEYBOARD, null); 1609 InputManager.getInstance().injectInputEvent(up, 1610 InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); 1611 up.recycle(); 1612 1613 Binder.restoreCallingIdentity(token); 1614 } 1615 1616 private void expandStatusBar() { 1617 final long token = Binder.clearCallingIdentity(); 1618 1619 StatusBarManager statusBarManager = (StatusBarManager) mContext.getSystemService( 1620 android.app.Service.STATUS_BAR_SERVICE); 1621 statusBarManager.expand(); 1622 1623 Binder.restoreCallingIdentity(token); 1624 } 1625 1626 private IAccessibilityInteractionConnection getConnectionLocked(int windowId) { 1627 if (DEBUG) { 1628 Slog.i(LOG_TAG, "Trying to get interaction connection to windowId: " + windowId); 1629 } 1630 AccessibilityConnectionWrapper wrapper = mWindowIdToInteractionConnectionWrapperMap.get( 1631 windowId); 1632 if (wrapper != null && wrapper.mConnection != null) { 1633 return wrapper.mConnection; 1634 } 1635 if (DEBUG) { 1636 Slog.e(LOG_TAG, "No interaction connection to window: " + windowId); 1637 } 1638 return null; 1639 } 1640 1641 private int resolveAccessibilityWindowId(int accessibilityWindowId) { 1642 if (accessibilityWindowId == AccessibilityNodeInfo.ACTIVE_WINDOW_ID) { 1643 return mSecurityPolicy.mActiveWindowId; 1644 } 1645 return accessibilityWindowId; 1646 } 1647 1648 private float getCompatibilityScale(int windowId) { 1649 IBinder windowToken = mWindowIdToWindowTokenMap.get(windowId); 1650 return mWindowManagerService.getWindowCompatibilityScale(windowToken); 1651 } 1652 } 1653 1654 final class SecurityPolicy { 1655 private static final int VALID_ACTIONS = 1656 AccessibilityNodeInfo.ACTION_CLICK 1657 | AccessibilityNodeInfo.ACTION_LONG_CLICK 1658 | AccessibilityNodeInfo.ACTION_FOCUS 1659 | AccessibilityNodeInfo.ACTION_CLEAR_FOCUS 1660 | AccessibilityNodeInfo.ACTION_SELECT 1661 | AccessibilityNodeInfo.ACTION_CLEAR_SELECTION 1662 | AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS 1663 | AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS 1664 | AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY 1665 | AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY 1666 | AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT 1667 | AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT 1668 | AccessibilityNodeInfo.ACTION_SCROLL_FORWARD 1669 | AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD; 1670 1671 private static final int RETRIEVAL_ALLOWING_EVENT_TYPES = 1672 AccessibilityEvent.TYPE_VIEW_CLICKED 1673 | AccessibilityEvent.TYPE_VIEW_FOCUSED 1674 | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER 1675 | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT 1676 | AccessibilityEvent.TYPE_VIEW_LONG_CLICKED 1677 | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED 1678 | AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED 1679 | AccessibilityEvent.TYPE_VIEW_SELECTED 1680 | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED 1681 | AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED 1682 | AccessibilityEvent.TYPE_VIEW_SCROLLED 1683 | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED 1684 | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED; 1685 1686 private int mActiveWindowId; 1687 1688 private boolean canDispatchAccessibilityEvent(AccessibilityEvent event) { 1689 // Send window changed event only for the retrieval allowing window. 1690 return (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED 1691 || event.getWindowId() == mActiveWindowId); 1692 } 1693 1694 public void updateActiveWindowAndEventSourceLocked(AccessibilityEvent event) { 1695 // The active window is either the window that has input focus or 1696 // the window that the user is currently touching. If the user is 1697 // touching a window that does not have input focus as soon as the 1698 // the user stops touching that window the focused window becomes 1699 // the active one. 1700 final int windowId = event.getWindowId(); 1701 final int eventType = event.getEventType(); 1702 switch (eventType) { 1703 case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: { 1704 if (getFocusedWindowId() == windowId) { 1705 mActiveWindowId = windowId; 1706 } 1707 } break; 1708 case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER: 1709 case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT: { 1710 mActiveWindowId = windowId; 1711 } break; 1712 case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END: { 1713 mActiveWindowId = getFocusedWindowId(); 1714 } break; 1715 } 1716 if ((eventType & RETRIEVAL_ALLOWING_EVENT_TYPES) == 0) { 1717 event.setSource(null); 1718 } 1719 } 1720 1721 public int getRetrievalAllowingWindowLocked() { 1722 return mActiveWindowId; 1723 } 1724 1725 public boolean canGetAccessibilityNodeInfoLocked(Service service, int windowId) { 1726 return canRetrieveWindowContent(service) && isRetrievalAllowingWindow(windowId); 1727 } 1728 1729 public boolean canPerformActionLocked(Service service, int windowId, int action, 1730 Bundle arguments) { 1731 return canRetrieveWindowContent(service) 1732 && isRetrievalAllowingWindow(windowId) 1733 && isActionPermitted(action); 1734 } 1735 1736 public boolean canRetrieveWindowContent(Service service) { 1737 return service.mCanRetrieveScreenContent; 1738 } 1739 1740 public void enforceCanRetrieveWindowContent(Service service) throws RemoteException { 1741 // This happens due to incorrect registration so make it apparent. 1742 if (!canRetrieveWindowContent(service)) { 1743 Slog.e(LOG_TAG, "Accessibility serivce " + service.mComponentName + " does not " + 1744 "declare android:canRetrieveWindowContent."); 1745 throw new RemoteException(); 1746 } 1747 } 1748 1749 private boolean isRetrievalAllowingWindow(int windowId) { 1750 return (mActiveWindowId == windowId); 1751 } 1752 1753 private boolean isActionPermitted(int action) { 1754 return (VALID_ACTIONS & action) != 0; 1755 } 1756 1757 private void enforceCallingPermission(String permission, String function) { 1758 if (OWN_PROCESS_ID == Binder.getCallingPid()) { 1759 return; 1760 } 1761 final int permissionStatus = mContext.checkCallingPermission(permission); 1762 if (permissionStatus != PackageManager.PERMISSION_GRANTED) { 1763 throw new SecurityException("You do not have " + permission 1764 + " required to call " + function); 1765 } 1766 } 1767 1768 private int getFocusedWindowId() { 1769 // We call this only on window focus change or after touch 1770 // exploration gesture end and the shown windows are not that 1771 // many, so the linear look up is just fine. 1772 IBinder token = mWindowManagerService.getFocusedWindowClientToken(); 1773 if (token != null) { 1774 SparseArray<IBinder> windows = mWindowIdToWindowTokenMap; 1775 final int windowCount = windows.size(); 1776 for (int i = 0; i < windowCount; i++) { 1777 if (windows.valueAt(i) == token) { 1778 return windows.keyAt(i); 1779 } 1780 } 1781 } 1782 return -1; 1783 } 1784 } 1785} 1786