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