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