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