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