AccessibilityManagerService.java revision d07d60b9bc0fd2e88f58d6e68dbfadad1bdd31cf
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 android.Manifest; 20import android.accessibilityservice.AccessibilityService; 21import android.accessibilityservice.AccessibilityServiceInfo; 22import android.accessibilityservice.IAccessibilityServiceConnection; 23import android.accessibilityservice.IEventListener; 24import android.app.PendingIntent; 25import android.content.BroadcastReceiver; 26import android.content.ComponentName; 27import android.content.ContentResolver; 28import android.content.Context; 29import android.content.Intent; 30import android.content.IntentFilter; 31import android.content.ServiceConnection; 32import android.content.pm.PackageManager; 33import android.content.pm.ResolveInfo; 34import android.database.ContentObserver; 35import android.graphics.Rect; 36import android.net.Uri; 37import android.os.Binder; 38import android.os.Handler; 39import android.os.IBinder; 40import android.os.Message; 41import android.os.RemoteException; 42import android.os.ServiceManager; 43import android.provider.Settings; 44import android.text.TextUtils; 45import android.text.TextUtils.SimpleStringSplitter; 46import android.util.Slog; 47import android.util.SparseArray; 48import android.view.IWindow; 49import android.view.View; 50import android.view.accessibility.AccessibilityEvent; 51import android.view.accessibility.AccessibilityManager; 52import android.view.accessibility.AccessibilityNodeInfo; 53import android.view.accessibility.IAccessibilityInteractionConnection; 54import android.view.accessibility.IAccessibilityInteractionConnectionCallback; 55import android.view.accessibility.IAccessibilityManager; 56import android.view.accessibility.IAccessibilityManagerClient; 57 58import com.android.internal.content.PackageMonitor; 59import com.android.internal.os.HandlerCaller; 60import com.android.internal.os.HandlerCaller.SomeArgs; 61import com.android.server.wm.WindowManagerService; 62 63import org.xmlpull.v1.XmlPullParserException; 64 65import java.io.IOException; 66import java.util.ArrayList; 67import java.util.Arrays; 68import java.util.HashMap; 69import java.util.HashSet; 70import java.util.Iterator; 71import java.util.List; 72import java.util.Map; 73import java.util.Set; 74 75/** 76 * This class is instantiated by the system as a system level service and can be 77 * accessed only by the system. The task of this service is to be a centralized 78 * event dispatch for {@link AccessibilityEvent}s generated across all processes 79 * on the device. Events are dispatched to {@link AccessibilityService}s. 80 * 81 * @hide 82 */ 83public class AccessibilityManagerService extends IAccessibilityManager.Stub 84 implements HandlerCaller.Callback { 85 86 private static final boolean DEBUG = false; 87 88 private static final String LOG_TAG = "AccessibilityManagerService"; 89 90 private static final String FUNCTION_REGISTER_EVENT_LISTENER = 91 "registerEventListener"; 92 93 private static int sIdCounter = 0; 94 95 private static final int OWN_PROCESS_ID = android.os.Process.myPid(); 96 97 private static final int DO_SET_SERVICE_INFO = 10; 98 99 private static int sNextWindowId; 100 101 final HandlerCaller mCaller; 102 103 final Context mContext; 104 105 final Object mLock = new Object(); 106 107 final List<Service> mServices = new ArrayList<Service>(); 108 109 final List<IAccessibilityManagerClient> mClients = 110 new ArrayList<IAccessibilityManagerClient>(); 111 112 final Map<ComponentName, Service> mComponentNameToServiceMap = new HashMap<ComponentName, Service>(); 113 114 private final List<AccessibilityServiceInfo> mInstalledServices = new ArrayList<AccessibilityServiceInfo>(); 115 116 private final Set<ComponentName> mEnabledServices = new HashSet<ComponentName>(); 117 118 private final SparseArray<IAccessibilityInteractionConnection> mWindowIdToInteractionConnectionMap = 119 new SparseArray<IAccessibilityInteractionConnection>(); 120 121 private final SparseArray<IBinder> mWindowIdToWindowTokenMap = new SparseArray<IBinder>(); 122 123 private final SimpleStringSplitter mStringColonSplitter = new SimpleStringSplitter(':'); 124 125 private PackageManager mPackageManager; 126 127 private int mHandledFeedbackTypes = 0; 128 129 private boolean mIsAccessibilityEnabled; 130 131 private AccessibilityInputFilter mInputFilter; 132 133 private boolean mHasInputFilter; 134 135 private final List<AccessibilityServiceInfo> mEnabledServicesForFeedbackTempList = new ArrayList<AccessibilityServiceInfo>(); 136 137 private boolean mIsTouchExplorationEnabled; 138 139 private final WindowManagerService mWindowManagerService; 140 141 private final SecurityPolicy mSecurityPolicy; 142 143 /** 144 * Handler for delayed event dispatch. 145 */ 146 private Handler mHandler = new Handler() { 147 148 @Override 149 public void handleMessage(Message message) { 150 Service service = (Service) message.obj; 151 int eventType = message.arg1; 152 153 synchronized (mLock) { 154 notifyEventListenerLocked(service, eventType); 155 } 156 } 157 }; 158 159 /** 160 * Creates a new instance. 161 * 162 * @param context A {@link Context} instance. 163 */ 164 public AccessibilityManagerService(Context context) { 165 mContext = context; 166 mPackageManager = mContext.getPackageManager(); 167 mCaller = new HandlerCaller(context, this); 168 mWindowManagerService = (WindowManagerService) ServiceManager.getService( 169 Context.WINDOW_SERVICE); 170 mSecurityPolicy = new SecurityPolicy(); 171 172 registerPackageChangeAndBootCompletedBroadcastReceiver(); 173 registerSettingsContentObservers(); 174 } 175 176 /** 177 * Registers a {@link BroadcastReceiver} for the events of 178 * adding/changing/removing/restarting a package and boot completion. 179 */ 180 private void registerPackageChangeAndBootCompletedBroadcastReceiver() { 181 Context context = mContext; 182 183 PackageMonitor monitor = new PackageMonitor() { 184 @Override 185 public void onSomePackagesChanged() { 186 synchronized (mLock) { 187 populateAccessibilityServiceListLocked(); 188 manageServicesLocked(); 189 } 190 } 191 192 @Override 193 public void onPackageRemoved(String packageName, int uid) { 194 synchronized (mLock) { 195 Iterator<ComponentName> it = mEnabledServices.iterator(); 196 while (it.hasNext()) { 197 ComponentName comp = it.next(); 198 String compPkg = comp.getPackageName(); 199 if (compPkg.equals(packageName)) { 200 it.remove(); 201 updateEnabledAccessibilitySerivcesSettingLocked(mEnabledServices); 202 return; 203 } 204 } 205 } 206 } 207 208 @Override 209 public boolean onHandleForceStop(Intent intent, String[] packages, 210 int uid, boolean doit) { 211 synchronized (mLock) { 212 boolean changed = false; 213 Iterator<ComponentName> it = mEnabledServices.iterator(); 214 while (it.hasNext()) { 215 ComponentName comp = it.next(); 216 String compPkg = comp.getPackageName(); 217 for (String pkg : packages) { 218 if (compPkg.equals(pkg)) { 219 if (!doit) { 220 return true; 221 } 222 it.remove(); 223 changed = true; 224 } 225 } 226 } 227 if (changed) { 228 updateEnabledAccessibilitySerivcesSettingLocked(mEnabledServices); 229 } 230 return false; 231 } 232 } 233 234 @Override 235 public void onReceive(Context context, Intent intent) { 236 if (intent.getAction() == Intent.ACTION_BOOT_COMPLETED) { 237 synchronized (mLock) { 238 populateAccessibilityServiceListLocked(); 239 // get accessibility enabled setting on boot 240 mIsAccessibilityEnabled = Settings.Secure.getInt( 241 mContext.getContentResolver(), 242 Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1; 243 244 manageServicesLocked(); 245 246 // get touch exploration enabled setting on boot 247 mIsTouchExplorationEnabled = Settings.Secure.getInt( 248 mContext.getContentResolver(), 249 Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0) == 1; 250 updateInputFilterLocked(); 251 252 sendStateToClientsLocked(); 253 } 254 255 return; 256 } 257 258 super.onReceive(context, intent); 259 } 260 261 private void updateEnabledAccessibilitySerivcesSettingLocked( 262 Set<ComponentName> enabledServices) { 263 Iterator<ComponentName> it = enabledServices.iterator(); 264 StringBuilder str = new StringBuilder(); 265 while (it.hasNext()) { 266 if (str.length() > 0) { 267 str.append(':'); 268 } 269 str.append(it.next().flattenToShortString()); 270 } 271 Settings.Secure.putString(mContext.getContentResolver(), 272 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, 273 str.toString()); 274 } 275 }; 276 277 // package changes 278 monitor.register(context, true); 279 280 // boot completed 281 IntentFilter bootFiler = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); 282 mContext.registerReceiver(monitor, bootFiler); 283 } 284 285 /** 286 * {@link ContentObserver}s for {@link Settings.Secure#ACCESSIBILITY_ENABLED} 287 * and {@link Settings.Secure#ENABLED_ACCESSIBILITY_SERVICES} settings. 288 */ 289 private void registerSettingsContentObservers() { 290 ContentResolver contentResolver = mContext.getContentResolver(); 291 292 Uri accessibilityEnabledUri = Settings.Secure.getUriFor( 293 Settings.Secure.ACCESSIBILITY_ENABLED); 294 contentResolver.registerContentObserver(accessibilityEnabledUri, false, 295 new ContentObserver(new Handler()) { 296 @Override 297 public void onChange(boolean selfChange) { 298 super.onChange(selfChange); 299 300 synchronized (mLock) { 301 mIsAccessibilityEnabled = Settings.Secure.getInt( 302 mContext.getContentResolver(), 303 Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1; 304 if (mIsAccessibilityEnabled) { 305 manageServicesLocked(); 306 } else { 307 unbindAllServicesLocked(); 308 } 309 updateInputFilterLocked(); 310 sendStateToClientsLocked(); 311 } 312 } 313 }); 314 315 Uri touchExplorationRequestedUri = Settings.Secure.getUriFor( 316 Settings.Secure.TOUCH_EXPLORATION_ENABLED); 317 contentResolver.registerContentObserver(touchExplorationRequestedUri, false, 318 new ContentObserver(new Handler()) { 319 @Override 320 public void onChange(boolean selfChange) { 321 super.onChange(selfChange); 322 323 synchronized (mLock) { 324 mIsTouchExplorationEnabled = Settings.Secure.getInt( 325 mContext.getContentResolver(), 326 Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0) == 1; 327 updateInputFilterLocked(); 328 sendStateToClientsLocked(); 329 } 330 } 331 }); 332 333 Uri accessibilityServicesUri = 334 Settings.Secure.getUriFor(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); 335 contentResolver.registerContentObserver(accessibilityServicesUri, false, 336 new ContentObserver(new Handler()) { 337 @Override 338 public void onChange(boolean selfChange) { 339 super.onChange(selfChange); 340 341 synchronized (mLock) { 342 manageServicesLocked(); 343 } 344 } 345 }); 346 } 347 348 public int addClient(IAccessibilityManagerClient client) throws RemoteException { 349 synchronized (mLock) { 350 final IAccessibilityManagerClient addedClient = client; 351 mClients.add(addedClient); 352 // Clients are registered all the time until their process is 353 // killed, therefore we do not have a corresponding unlinkToDeath. 354 client.asBinder().linkToDeath(new DeathRecipient() { 355 public void binderDied() { 356 synchronized (mLock) { 357 mClients.remove(addedClient); 358 } 359 } 360 }, 0); 361 return getState(); 362 } 363 } 364 365 public boolean sendAccessibilityEvent(AccessibilityEvent event) { 366 synchronized (mLock) { 367 if (mSecurityPolicy.canDispatchAccessibilityEvent(event)) { 368 mSecurityPolicy.updateRetrievalAllowingWindowAndEventSourceLocked(event); 369 notifyAccessibilityServicesDelayedLocked(event, false); 370 notifyAccessibilityServicesDelayedLocked(event, true); 371 } 372 } 373 event.recycle(); 374 mHandledFeedbackTypes = 0; 375 return (OWN_PROCESS_ID != Binder.getCallingPid()); 376 } 377 378 public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList() { 379 synchronized (mLock) { 380 return mInstalledServices; 381 } 382 } 383 384 public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType) { 385 List<AccessibilityServiceInfo> result = mEnabledServicesForFeedbackTempList; 386 result.clear(); 387 List<Service> services = mServices; 388 synchronized (mLock) { 389 while (feedbackType != 0) { 390 final int feedbackTypeBit = (1 << Integer.numberOfTrailingZeros(feedbackType)); 391 feedbackType &= ~feedbackTypeBit; 392 final int serviceCount = services.size(); 393 for (int i = 0; i < serviceCount; i++) { 394 Service service = services.get(i); 395 if ((service.mFeedbackType & feedbackTypeBit) != 0) { 396 result.add(service.mAccessibilityServiceInfo); 397 } 398 } 399 } 400 } 401 return result; 402 } 403 404 public void interrupt() { 405 synchronized (mLock) { 406 for (int i = 0, count = mServices.size(); i < count; i++) { 407 Service service = mServices.get(i); 408 try { 409 service.mServiceInterface.onInterrupt(); 410 } catch (RemoteException re) { 411 Slog.e(LOG_TAG, "Error during sending interrupt request to " 412 + service.mService, re); 413 } 414 } 415 } 416 } 417 418 public void executeMessage(Message message) { 419 switch (message.what) { 420 case DO_SET_SERVICE_INFO: { 421 SomeArgs arguments = ((SomeArgs) message.obj); 422 423 AccessibilityServiceInfo info = (AccessibilityServiceInfo) arguments.arg1; 424 Service service = (Service) arguments.arg2; 425 426 synchronized (mLock) { 427 // If the XML manifest had data to configure the service its info 428 // should be already set. In such a case update only the dynamically 429 // configurable properties. 430 AccessibilityServiceInfo oldInfo = service.mAccessibilityServiceInfo; 431 if (oldInfo != null) { 432 oldInfo.updateDynamicallyConfigurableProperties(info); 433 service.setDynamicallyConfigurableProperties(oldInfo); 434 } else { 435 service.setDynamicallyConfigurableProperties(info); 436 } 437 } 438 } return; 439 default: 440 Slog.w(LOG_TAG, "Unknown message type: " + message.what); 441 } 442 } 443 444 public int addAccessibilityInteractionConnection(IWindow windowToken, 445 IAccessibilityInteractionConnection connection) throws RemoteException { 446 synchronized (mLock) { 447 final IWindow addedWindowToken = windowToken; 448 final int windowId = sNextWindowId++; 449 connection.asBinder().linkToDeath(new DeathRecipient() { 450 public void binderDied() { 451 synchronized (mLock) { 452 removeAccessibilityInteractionConnection(addedWindowToken); 453 } 454 } 455 }, 0); 456 mWindowIdToWindowTokenMap.put(windowId, addedWindowToken.asBinder()); 457 mWindowIdToInteractionConnectionMap.put(windowId, connection); 458 if (DEBUG) { 459 Slog.i(LOG_TAG, "Adding interaction connection to windowId: " + windowId); 460 } 461 return windowId; 462 } 463 } 464 465 public void removeAccessibilityInteractionConnection(IWindow windowToken) { 466 synchronized (mLock) { 467 final int count = mWindowIdToWindowTokenMap.size(); 468 for (int i = 0; i < count; i++) { 469 if (mWindowIdToWindowTokenMap.valueAt(i) == windowToken.asBinder()) { 470 final int windowId = mWindowIdToWindowTokenMap.keyAt(i); 471 mWindowIdToWindowTokenMap.remove(windowId); 472 mWindowIdToInteractionConnectionMap.remove(windowId); 473 if (DEBUG) { 474 Slog.i(LOG_TAG, "Removing interaction connection to windowId: " + windowId); 475 } 476 return; 477 } 478 } 479 } 480 } 481 482 public IAccessibilityServiceConnection registerEventListener(IEventListener listener) { 483 mSecurityPolicy.enforceCallingPermission(Manifest.permission.RETRIEVE_WINDOW_CONTENT, 484 FUNCTION_REGISTER_EVENT_LISTENER); 485 ComponentName componentName = new ComponentName("foo.bar", 486 "AutomationAccessibilityService"); 487 synchronized (mLock) { 488 Service oldService = mComponentNameToServiceMap.get(componentName); 489 if (oldService != null) { 490 tryRemoveServiceLocked(oldService); 491 } 492 // This API is intended for testing so enable accessibility to make 493 // sure clients can start poking with the window content. 494 Settings.Secure.putInt(mContext.getContentResolver(), 495 Settings.Secure.ACCESSIBILITY_ENABLED, 1); 496 // Also disable all accessibility services to avoid interference 497 // with the tests. 498 Settings.Secure.putString(mContext.getContentResolver(), 499 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, ""); 500 } 501 AccessibilityServiceInfo accessibilityServiceInfo = new AccessibilityServiceInfo(); 502 accessibilityServiceInfo.eventTypes = AccessibilityEvent.TYPES_ALL_MASK; 503 accessibilityServiceInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC; 504 Service service = new Service(componentName, accessibilityServiceInfo, true); 505 service.onServiceConnected(componentName, listener.asBinder()); 506 return service; 507 } 508 509 /** 510 * Populates the cached list of installed {@link AccessibilityService}s. 511 */ 512 private void populateAccessibilityServiceListLocked() { 513 mInstalledServices.clear(); 514 515 List<ResolveInfo> installedServices = mPackageManager.queryIntentServices( 516 new Intent(AccessibilityService.SERVICE_INTERFACE), 517 PackageManager.GET_SERVICES | PackageManager.GET_META_DATA); 518 519 for (int i = 0, count = installedServices.size(); i < count; i++) { 520 ResolveInfo resolveInfo = installedServices.get(i); 521 AccessibilityServiceInfo accessibilityServiceInfo; 522 try { 523 accessibilityServiceInfo = new AccessibilityServiceInfo(resolveInfo, mContext); 524 mInstalledServices.add(accessibilityServiceInfo); 525 } catch (XmlPullParserException xppe) { 526 Slog.e(LOG_TAG, "Error while initializing AccessibilityServiceInfo", xppe); 527 } catch (IOException ioe) { 528 Slog.e(LOG_TAG, "Error while initializing AccessibilityServiceInfo", ioe); 529 } 530 } 531 } 532 533 /** 534 * Performs {@link AccessibilityService}s delayed notification. The delay is configurable 535 * and denotes the period after the last event before notifying the service. 536 * 537 * @param event The event. 538 * @param isDefault True to notify default listeners, not default services. 539 */ 540 private void notifyAccessibilityServicesDelayedLocked(AccessibilityEvent event, 541 boolean isDefault) { 542 try { 543 for (int i = 0, count = mServices.size(); i < count; i++) { 544 Service service = mServices.get(i); 545 546 if (service.mIsDefault == isDefault) { 547 if (canDispathEventLocked(service, event, mHandledFeedbackTypes)) { 548 mHandledFeedbackTypes |= service.mFeedbackType; 549 notifyAccessibilityServiceDelayedLocked(service, event); 550 } 551 } 552 } 553 } catch (IndexOutOfBoundsException oobe) { 554 // An out of bounds exception can happen if services are going away 555 // as the for loop is running. If that happens, just bail because 556 // there are no more services to notify. 557 return; 558 } 559 } 560 561 /** 562 * Performs an {@link AccessibilityService} delayed notification. The delay is configurable 563 * and denotes the period after the last event before notifying the service. 564 * 565 * @param service The service. 566 * @param event The event. 567 */ 568 private void notifyAccessibilityServiceDelayedLocked(Service service, 569 AccessibilityEvent event) { 570 synchronized (mLock) { 571 final int eventType = event.getEventType(); 572 // Make a copy since during dispatch it is possible the event to 573 // be modified to remove its source if the receiving service does 574 // not have permission to access the window content. 575 AccessibilityEvent newEvent = AccessibilityEvent.obtain(event); 576 AccessibilityEvent oldEvent = service.mPendingEvents.get(eventType); 577 service.mPendingEvents.put(eventType, newEvent); 578 579 final int what = eventType | (service.mId << 16); 580 if (oldEvent != null) { 581 mHandler.removeMessages(what); 582 oldEvent.recycle(); 583 } 584 585 Message message = mHandler.obtainMessage(what, service); 586 message.arg1 = eventType; 587 mHandler.sendMessageDelayed(message, service.mNotificationTimeout); 588 } 589 } 590 591 /** 592 * Notifies a service for a scheduled event given the event type. 593 * 594 * @param service The service. 595 * @param eventType The type of the event to dispatch. 596 */ 597 private void notifyEventListenerLocked(Service service, int eventType) { 598 IEventListener listener = service.mServiceInterface; 599 AccessibilityEvent event = service.mPendingEvents.get(eventType); 600 601 // Check for null here because there is a concurrent scenario in which this 602 // happens: 1) A binder thread calls notifyAccessibilityServiceDelayedLocked 603 // which posts a message for dispatching an event. 2) The message is pulled 604 // from the queue by the handler on the service thread and the latter is 605 // just about to acquire the lock and call this method. 3) Now another binder 606 // thread acquires the lock calling notifyAccessibilityServiceDelayedLocked 607 // so the service thread waits for the lock; 4) The binder thread replaces 608 // the event with a more recent one (assume the same event type) and posts a 609 // dispatch request releasing the lock. 5) Now the main thread is unblocked and 610 // dispatches the event which is removed from the pending ones. 6) And ... now 611 // the service thread handles the last message posted by the last binder call 612 // but the event is already dispatched and hence looking it up in the pending 613 // ones yields null. This check is much simpler that keeping count for each 614 // event type of each service to catch such a scenario since only one message 615 // is processed at a time. 616 if (event == null) { 617 return; 618 } 619 620 service.mPendingEvents.remove(eventType); 621 try { 622 if (mSecurityPolicy.canRetrieveWindowContent(service)) { 623 event.setConnection(service); 624 } else { 625 event.setSource(null); 626 } 627 event.setSealed(true); 628 listener.onAccessibilityEvent(event); 629 event.recycle(); 630 if (DEBUG) { 631 Slog.i(LOG_TAG, "Event " + event + " sent to " + listener); 632 } 633 } catch (RemoteException re) { 634 Slog.e(LOG_TAG, "Error during sending " + event + " to " + service.mService, re); 635 } 636 } 637 638 /** 639 * Adds a service. 640 * 641 * @param service The service to add. 642 */ 643 private void tryAddServiceLocked(Service service) { 644 try { 645 if (mServices.contains(service) || !service.isConfigured()) { 646 return; 647 } 648 service.linkToOwnDeath(); 649 mServices.add(service); 650 mComponentNameToServiceMap.put(service.mComponentName, service); 651 updateInputFilterLocked(); 652 } catch (RemoteException e) { 653 /* do nothing */ 654 } 655 } 656 657 /** 658 * Removes a service. 659 * 660 * @param service The service. 661 * @return True if the service was removed, false otherwise. 662 */ 663 private boolean tryRemoveServiceLocked(Service service) { 664 final boolean removed = mServices.remove(service); 665 if (!removed) { 666 return false; 667 } 668 mComponentNameToServiceMap.remove(service.mComponentName); 669 mHandler.removeMessages(service.mId); 670 service.unlinkToOwnDeath(); 671 updateInputFilterLocked(); 672 return removed; 673 } 674 675 /** 676 * Determines if given event can be dispatched to a service based on the package of the 677 * event source and already notified services for that event type. Specifically, a 678 * service is notified if it is interested in events from the package and no other service 679 * providing the same feedback type has been notified. Exception are services the 680 * provide generic feedback (feedback type left as a safety net for unforeseen feedback 681 * types) which are always notified. 682 * 683 * @param service The potential receiver. 684 * @param event The event. 685 * @param handledFeedbackTypes The feedback types for which services have been notified. 686 * @return True if the listener should be notified, false otherwise. 687 */ 688 private boolean canDispathEventLocked(Service service, AccessibilityEvent event, 689 int handledFeedbackTypes) { 690 691 if (!service.isConfigured()) { 692 return false; 693 } 694 695 int eventType = event.getEventType(); 696 if ((service.mEventTypes & eventType) != eventType) { 697 return false; 698 } 699 700 Set<String> packageNames = service.mPackageNames; 701 CharSequence packageName = event.getPackageName(); 702 703 if (packageNames.isEmpty() || packageNames.contains(packageName)) { 704 int feedbackType = service.mFeedbackType; 705 if ((handledFeedbackTypes & feedbackType) != feedbackType 706 || feedbackType == AccessibilityServiceInfo.FEEDBACK_GENERIC) { 707 return true; 708 } 709 } 710 711 return false; 712 } 713 714 /** 715 * Manages services by starting enabled ones and stopping disabled ones. 716 */ 717 private void manageServicesLocked() { 718 populateEnabledServicesLocked(mEnabledServices); 719 updateServicesStateLocked(mInstalledServices, mEnabledServices); 720 disableAccessibilityIfNoEnabledServices(mEnabledServices); 721 } 722 723 /** 724 * Unbinds all bound services. 725 */ 726 private void unbindAllServicesLocked() { 727 List<Service> services = mServices; 728 729 for (int i = 0, count = services.size(); i < count; i++) { 730 Service service = services.get(i); 731 if (service.unbind()) { 732 i--; 733 count--; 734 } 735 } 736 } 737 738 /** 739 * Populates a list with the {@link ComponentName}s of all enabled 740 * {@link AccessibilityService}s. 741 * 742 * @param enabledServices The list. 743 */ 744 private void populateEnabledServicesLocked(Set<ComponentName> enabledServices) { 745 enabledServices.clear(); 746 747 String servicesValue = Settings.Secure.getString(mContext.getContentResolver(), 748 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); 749 750 if (servicesValue != null) { 751 TextUtils.SimpleStringSplitter splitter = mStringColonSplitter; 752 splitter.setString(servicesValue); 753 while (splitter.hasNext()) { 754 String str = splitter.next(); 755 if (str == null || str.length() <= 0) { 756 continue; 757 } 758 ComponentName enabledService = ComponentName.unflattenFromString(str); 759 if (enabledService != null) { 760 enabledServices.add(enabledService); 761 } 762 } 763 } 764 } 765 766 /** 767 * Updates the state of each service by starting (or keeping running) enabled ones and 768 * stopping the rest. 769 * 770 * @param installedServices All installed {@link AccessibilityService}s. 771 * @param enabledServices The {@link ComponentName}s of the enabled services. 772 */ 773 private void updateServicesStateLocked(List<AccessibilityServiceInfo> installedServices, 774 Set<ComponentName> enabledServices) { 775 776 Map<ComponentName, Service> componentNameToServiceMap = mComponentNameToServiceMap; 777 boolean isEnabled = mIsAccessibilityEnabled; 778 779 for (int i = 0, count = installedServices.size(); i < count; i++) { 780 AccessibilityServiceInfo installedService = installedServices.get(i); 781 ComponentName componentName = ComponentName.unflattenFromString( 782 installedService.getId()); 783 Service service = componentNameToServiceMap.get(componentName); 784 785 if (isEnabled) { 786 if (enabledServices.contains(componentName)) { 787 if (service == null) { 788 service = new Service(componentName, installedService, false); 789 } 790 service.bind(); 791 } else { 792 if (service != null) { 793 service.unbind(); 794 } 795 } 796 } else { 797 if (service != null) { 798 service.unbind(); 799 } 800 } 801 } 802 } 803 804 /** 805 * Disables accessibility if there are no enabled accessibility services which 806 * to consume the generated accessibility events. 807 * 808 * @param enabledServices The set of enabled services. 809 */ 810 private void disableAccessibilityIfNoEnabledServices(Set<ComponentName> enabledServices) { 811 if (enabledServices.isEmpty()) { 812 Settings.Secure.putInt(mContext.getContentResolver(), 813 Settings.Secure.ACCESSIBILITY_ENABLED, 0); 814 } 815 } 816 817 /** 818 * Sends the state to the clients. 819 */ 820 private void sendStateToClientsLocked() { 821 final int state = getState(); 822 for (int i = 0, count = mClients.size(); i < count; i++) { 823 try { 824 mClients.get(i).setState(state); 825 } catch (RemoteException re) { 826 mClients.remove(i); 827 count--; 828 i--; 829 } 830 } 831 } 832 833 /** 834 * Gets the current state as a set of flags. 835 * 836 * @return The state. 837 */ 838 private int getState() { 839 int state = 0; 840 if (mIsAccessibilityEnabled) { 841 state |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED; 842 } 843 // Touch exploration relies on enabled accessibility. 844 if (mIsAccessibilityEnabled && mIsTouchExplorationEnabled) { 845 state |= AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED; 846 } 847 return state; 848 } 849 850 /** 851 * Updates the touch exploration state. 852 */ 853 private void updateInputFilterLocked() { 854 if (mIsAccessibilityEnabled && mIsTouchExplorationEnabled) { 855 if (!mHasInputFilter) { 856 mHasInputFilter = true; 857 if (mInputFilter == null) { 858 mInputFilter = new AccessibilityInputFilter(mContext); 859 } 860 mWindowManagerService.setInputFilter(mInputFilter); 861 } 862 return; 863 } 864 if (mHasInputFilter) { 865 mHasInputFilter = false; 866 mWindowManagerService.setInputFilter(null); 867 } 868 } 869 870 /** 871 * This class represents an accessibility service. It stores all per service 872 * data required for the service management, provides API for starting/stopping the 873 * service and is responsible for adding/removing the service in the data structures 874 * for service management. The class also exposes configuration interface that is 875 * passed to the service it represents as soon it is bound. It also serves as the 876 * connection for the service. 877 */ 878 class Service extends IAccessibilityServiceConnection.Stub 879 implements ServiceConnection, DeathRecipient { 880 int mId = 0; 881 882 AccessibilityServiceInfo mAccessibilityServiceInfo; 883 884 IBinder mService; 885 886 IEventListener mServiceInterface; 887 888 int mEventTypes; 889 890 int mFeedbackType; 891 892 Set<String> mPackageNames = new HashSet<String>(); 893 894 boolean mIsDefault; 895 896 long mNotificationTimeout; 897 898 ComponentName mComponentName; 899 900 Intent mIntent; 901 902 boolean mCanRetrieveScreenContent; 903 904 boolean mIsAutomation; 905 906 final Rect mTempBounds = new Rect(); 907 908 // the events pending events to be dispatched to this service 909 final SparseArray<AccessibilityEvent> mPendingEvents = 910 new SparseArray<AccessibilityEvent>(); 911 912 public Service(ComponentName componentName, 913 AccessibilityServiceInfo accessibilityServiceInfo, boolean isAutomation) { 914 mId = sIdCounter++; 915 mComponentName = componentName; 916 mAccessibilityServiceInfo = accessibilityServiceInfo; 917 mIsAutomation = isAutomation; 918 if (!isAutomation) { 919 mCanRetrieveScreenContent = accessibilityServiceInfo.getCanRetrieveWindowContent(); 920 mIntent = new Intent().setComponent(mComponentName); 921 mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL, 922 com.android.internal.R.string.accessibility_binding_label); 923 mIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity( 924 mContext, 0, new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS), 0)); 925 } else { 926 mCanRetrieveScreenContent = true; 927 } 928 setDynamicallyConfigurableProperties(accessibilityServiceInfo); 929 } 930 931 public void setDynamicallyConfigurableProperties(AccessibilityServiceInfo info) { 932 mEventTypes = info.eventTypes; 933 mFeedbackType = info.feedbackType; 934 String[] packageNames = info.packageNames; 935 if (packageNames != null) { 936 mPackageNames.addAll(Arrays.asList(packageNames)); 937 } 938 mNotificationTimeout = info.notificationTimeout; 939 mIsDefault = (info.flags & AccessibilityServiceInfo.DEFAULT) != 0; 940 941 synchronized (mLock) { 942 tryAddServiceLocked(this); 943 } 944 } 945 946 /** 947 * Binds to the accessibility service. 948 * 949 * @return True if binding is successful. 950 */ 951 public boolean bind() { 952 if (!mIsAutomation && mService == null) { 953 return mContext.bindService(mIntent, this, Context.BIND_AUTO_CREATE); 954 } 955 return false; 956 } 957 958 /** 959 * Unbinds form the accessibility service and removes it from the data 960 * structures for service management. 961 * 962 * @return True if unbinding is successful. 963 */ 964 public boolean unbind() { 965 if (mService != null) { 966 synchronized (mLock) { 967 tryRemoveServiceLocked(this); 968 } 969 if (!mIsAutomation) { 970 mContext.unbindService(this); 971 } 972 mService = null; 973 return true; 974 } 975 return false; 976 } 977 978 /** 979 * Returns if the service is configured i.e. at least event types of interest 980 * and feedback type must be set. 981 * 982 * @return True if the service is configured, false otherwise. 983 */ 984 public boolean isConfigured() { 985 return (mEventTypes != 0 && mFeedbackType != 0 && mService != null); 986 } 987 988 public void setServiceInfo(AccessibilityServiceInfo info) { 989 mCaller.obtainMessageOO(DO_SET_SERVICE_INFO, info, this).sendToTarget(); 990 } 991 992 public void onServiceConnected(ComponentName componentName, IBinder service) { 993 mService = service; 994 mServiceInterface = IEventListener.Stub.asInterface(service); 995 try { 996 mServiceInterface.setConnection(this); 997 synchronized (mLock) { 998 tryAddServiceLocked(this); 999 } 1000 } catch (RemoteException re) { 1001 Slog.w(LOG_TAG, "Error while setting Controller for service: " + service, re); 1002 } 1003 } 1004 1005 public float findAccessibilityNodeInfoByViewIdInActiveWindow(int viewId, 1006 int interactionId, IAccessibilityInteractionConnectionCallback callback, 1007 long interrogatingTid) 1008 throws RemoteException { 1009 IAccessibilityInteractionConnection connection = null; 1010 synchronized (mLock) { 1011 mSecurityPolicy.enforceCanRetrieveWindowContent(this); 1012 final boolean permissionGranted = mSecurityPolicy.canRetrieveWindowContent(this); 1013 if (!permissionGranted) { 1014 return 0; 1015 } else { 1016 connection = getConnectionToRetrievalAllowingWindowLocked(); 1017 if (connection == null) { 1018 if (DEBUG) { 1019 Slog.e(LOG_TAG, "No interaction connection to a retrieve " 1020 + "allowing window."); 1021 } 1022 return 0; 1023 } 1024 } 1025 } 1026 final int interrogatingPid = Binder.getCallingPid(); 1027 final long identityToken = Binder.clearCallingIdentity(); 1028 try { 1029 connection.findAccessibilityNodeInfoByViewId(viewId, interactionId, callback, 1030 interrogatingPid, interrogatingTid); 1031 } catch (RemoteException re) { 1032 if (DEBUG) { 1033 Slog.e(LOG_TAG, "Error finding node."); 1034 } 1035 } finally { 1036 Binder.restoreCallingIdentity(identityToken); 1037 } 1038 return getCompatibilityScale(mSecurityPolicy.getRetrievalAllowingWindowLocked()); 1039 } 1040 1041 public float findAccessibilityNodeInfosByViewTextInActiveWindow( 1042 String text, int interactionId, 1043 IAccessibilityInteractionConnectionCallback callback, long threadId) 1044 throws RemoteException { 1045 return findAccessibilityNodeInfosByViewText(text, 1046 mSecurityPolicy.mRetrievalAlowingWindowId, View.NO_ID, interactionId, callback, 1047 threadId); 1048 } 1049 1050 public float findAccessibilityNodeInfosByViewText(String text, 1051 int accessibilityWindowId, int accessibilityViewId, int interactionId, 1052 IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) 1053 throws RemoteException { 1054 IAccessibilityInteractionConnection connection = null; 1055 synchronized (mLock) { 1056 mSecurityPolicy.enforceCanRetrieveWindowContent(this); 1057 final boolean permissionGranted = 1058 mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, accessibilityWindowId); 1059 if (!permissionGranted) { 1060 return 0; 1061 } else { 1062 connection = getConnectionToRetrievalAllowingWindowLocked(); 1063 if (connection == null) { 1064 if (DEBUG) { 1065 Slog.e(LOG_TAG, "No interaction connection to focused window."); 1066 } 1067 return 0; 1068 } 1069 } 1070 } 1071 final int interrogatingPid = Binder.getCallingPid(); 1072 final long identityToken = Binder.clearCallingIdentity(); 1073 try { 1074 connection.findAccessibilityNodeInfosByViewText(text, accessibilityViewId, 1075 interactionId, callback, interrogatingPid, interrogatingTid); 1076 } catch (RemoteException re) { 1077 if (DEBUG) { 1078 Slog.e(LOG_TAG, "Error finding node."); 1079 } 1080 } finally { 1081 Binder.restoreCallingIdentity(identityToken); 1082 } 1083 return getCompatibilityScale(accessibilityWindowId); 1084 } 1085 1086 public float findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId, 1087 int accessibilityViewId, int interactionId, 1088 IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) 1089 throws RemoteException { 1090 IAccessibilityInteractionConnection connection = null; 1091 synchronized (mLock) { 1092 mSecurityPolicy.enforceCanRetrieveWindowContent(this); 1093 final boolean permissionGranted = 1094 mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, accessibilityWindowId); 1095 if (!permissionGranted) { 1096 return 0; 1097 } else { 1098 connection = mWindowIdToInteractionConnectionMap.get(accessibilityWindowId); 1099 if (connection == null) { 1100 if (DEBUG) { 1101 Slog.e(LOG_TAG, "No interaction connection to window: " 1102 + accessibilityWindowId); 1103 } 1104 return 0; 1105 } 1106 } 1107 } 1108 final int interrogatingPid = Binder.getCallingPid(); 1109 final long identityToken = Binder.clearCallingIdentity(); 1110 try { 1111 connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityViewId, 1112 interactionId, callback, interrogatingPid, interrogatingTid); 1113 } catch (RemoteException re) { 1114 if (DEBUG) { 1115 Slog.e(LOG_TAG, "Error requesting node with accessibilityViewId: " 1116 + accessibilityViewId); 1117 } 1118 } finally { 1119 Binder.restoreCallingIdentity(identityToken); 1120 } 1121 return getCompatibilityScale(accessibilityWindowId); 1122 } 1123 1124 public boolean performAccessibilityAction(int accessibilityWindowId, 1125 int accessibilityViewId, int action, int interactionId, 1126 IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) { 1127 IAccessibilityInteractionConnection connection = null; 1128 synchronized (mLock) { 1129 final boolean permissionGranted = mSecurityPolicy.canPerformActionLocked(this, 1130 accessibilityWindowId, action); 1131 if (!permissionGranted) { 1132 return false; 1133 } else { 1134 connection = mWindowIdToInteractionConnectionMap.get(accessibilityWindowId); 1135 if (connection == null) { 1136 if (DEBUG) { 1137 Slog.e(LOG_TAG, "No interaction connection to window: " 1138 + accessibilityWindowId); 1139 } 1140 return false; 1141 } 1142 } 1143 } 1144 final int interrogatingPid = Binder.getCallingPid(); 1145 final long identityToken = Binder.clearCallingIdentity(); 1146 try { 1147 connection.performAccessibilityAction(accessibilityViewId, action, interactionId, 1148 callback, interrogatingPid, interrogatingTid); 1149 } catch (RemoteException re) { 1150 if (DEBUG) { 1151 Slog.e(LOG_TAG, "Error requesting node with accessibilityViewId: " 1152 + accessibilityViewId); 1153 } 1154 } finally { 1155 Binder.restoreCallingIdentity(identityToken); 1156 } 1157 return true; 1158 } 1159 1160 public void onServiceDisconnected(ComponentName componentName) { 1161 /* do nothing - #binderDied takes care */ 1162 } 1163 1164 public void linkToOwnDeath() throws RemoteException { 1165 mService.linkToDeath(this, 0); 1166 } 1167 1168 public void unlinkToOwnDeath() { 1169 mService.unlinkToDeath(this, 0); 1170 } 1171 1172 public void binderDied() { 1173 synchronized (mLock) { 1174 tryRemoveServiceLocked(this); 1175 } 1176 } 1177 1178 private IAccessibilityInteractionConnection getConnectionToRetrievalAllowingWindowLocked() { 1179 final int windowId = mSecurityPolicy.getRetrievalAllowingWindowLocked(); 1180 if (DEBUG) { 1181 Slog.i(LOG_TAG, "Trying to get interaction connection to windowId: " + windowId); 1182 } 1183 return mWindowIdToInteractionConnectionMap.get(windowId); 1184 } 1185 1186 private float getCompatibilityScale(int windowId) { 1187 IBinder windowToken = mWindowIdToWindowTokenMap.get(windowId); 1188 return mWindowManagerService.getWindowCompatibilityScale(windowToken); 1189 } 1190 } 1191 1192 final class SecurityPolicy { 1193 private static final int VALID_ACTIONS = AccessibilityNodeInfo.ACTION_FOCUS 1194 | AccessibilityNodeInfo.ACTION_CLEAR_FOCUS | AccessibilityNodeInfo.ACTION_SELECT 1195 | AccessibilityNodeInfo.ACTION_CLEAR_SELECTION; 1196 1197 private static final int RETRIEVAL_ALLOWING_EVENT_TYPES = 1198 AccessibilityEvent.TYPE_VIEW_CLICKED | AccessibilityEvent.TYPE_VIEW_FOCUSED 1199 | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT 1200 | AccessibilityEvent.TYPE_VIEW_LONG_CLICKED | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED 1201 | AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED | AccessibilityEvent.TYPE_VIEW_SELECTED 1202 | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED 1203 | AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED 1204 | AccessibilityEvent.TYPE_VIEW_SCROLLED; 1205 1206 private static final int RETRIEVAL_ALLOWING_WINDOW_CHANGE_EVENT_TYPES = 1207 AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER 1208 | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT; 1209 1210 private int mRetrievalAlowingWindowId; 1211 1212 private boolean canDispatchAccessibilityEvent(AccessibilityEvent event) { 1213 // Send window changed event only for the retrieval allowing window. 1214 return (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED 1215 || event.getWindowId() == mRetrievalAlowingWindowId); 1216 } 1217 1218 public void updateRetrievalAllowingWindowAndEventSourceLocked(AccessibilityEvent event) { 1219 final int windowId = event.getWindowId(); 1220 final int eventType = event.getEventType(); 1221 if ((eventType & RETRIEVAL_ALLOWING_WINDOW_CHANGE_EVENT_TYPES) != 0) { 1222 mRetrievalAlowingWindowId = windowId; 1223 } 1224 if ((eventType & RETRIEVAL_ALLOWING_EVENT_TYPES) == 0) { 1225 event.setSource(null); 1226 } 1227 } 1228 1229 public int getRetrievalAllowingWindowLocked() { 1230 return mRetrievalAlowingWindowId; 1231 } 1232 1233 public boolean canGetAccessibilityNodeInfoLocked(Service service, int windowId) { 1234 return canRetrieveWindowContent(service) && isRetrievalAllowingWindow(windowId); 1235 } 1236 1237 public boolean canPerformActionLocked(Service service, int windowId, int action) { 1238 return canRetrieveWindowContent(service) 1239 && isRetrievalAllowingWindow(windowId) 1240 && isActionPermitted(action); 1241 } 1242 1243 public boolean canRetrieveWindowContent(Service service) { 1244 return service.mCanRetrieveScreenContent; 1245 } 1246 1247 public void enforceCanRetrieveWindowContent(Service service) throws RemoteException { 1248 // This happens due to incorrect registration so make it apparent. 1249 if (!canRetrieveWindowContent(service)) { 1250 Slog.e(LOG_TAG, "Accessibility serivce " + service.mComponentName + " does not " + 1251 "declare android:canRetrieveWindowContent."); 1252 throw new RemoteException(); 1253 } 1254 } 1255 1256 private boolean isRetrievalAllowingWindow(int windowId) { 1257 return (mRetrievalAlowingWindowId == windowId); 1258 } 1259 1260 private boolean isActionPermitted(int action) { 1261 return (VALID_ACTIONS & action) != 0; 1262 } 1263 1264 private void enforceCallingPermission(String permission, String function) { 1265 if (OWN_PROCESS_ID == Binder.getCallingPid()) { 1266 return; 1267 } 1268 final int permissionStatus = mContext.checkCallingPermission(permission); 1269 if (permissionStatus != PackageManager.PERMISSION_GRANTED) { 1270 throw new SecurityException("You do not have " + permission 1271 + " required to call " + function); 1272 } 1273 } 1274 } 1275} 1276