AccessibilityManagerService.java revision aa780c110922148a6a4ba06734bb2b0bb8c98f93
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, true); 287 288 // boot completed 289 IntentFilter bootFiler = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); 290 mContext.registerReceiver(monitor, bootFiler); 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 try { 1012 mGestureLock.wait(); 1013 } catch (InterruptedException ie) { 1014 /* ignore */ 1015 } 1016 } 1017 } 1018 1019 @Override 1020 public void run() { 1021 Looper.prepare(); 1022 synchronized (mGestureLock) { 1023 mHandlerCaller = new HandlerCaller(mContext, Looper.myLooper(), this); 1024 mGestureLock.notifyAll(); 1025 } 1026 Looper.loop(); 1027 } 1028 1029 @Override 1030 public void setGestureResult(int gestureId, boolean handled, int interactionId) { 1031 synchronized (mGestureLock) { 1032 if (interactionId > mInteractionId) { 1033 mGestureResult = handled; 1034 mInteractionId = interactionId; 1035 } 1036 mGestureLock.notifyAll(); 1037 } 1038 } 1039 1040 @Override 1041 public void executeMessage(Message message) { 1042 final int type = message.what; 1043 switch (type) { 1044 case MSG_HANDLE_GESTURE: { 1045 IAccessibilityServiceClient service = 1046 (IAccessibilityServiceClient) message.obj; 1047 final int gestureId = message.arg1; 1048 final int interactionId = message.arg1; 1049 1050 try { 1051 service.onGesture(gestureId, this, interactionId); 1052 } catch (RemoteException re) { 1053 Slog.e(LOG_TAG, "Error dispatching a gesture to a client.", re); 1054 return; 1055 } 1056 1057 long waitTimeMillis = 0; 1058 final long startTimeMillis = SystemClock.uptimeMillis(); 1059 synchronized (mGestureLock) { 1060 while (true) { 1061 try { 1062 // Did we get the expected callback? 1063 if (mInteractionId == interactionId) { 1064 break; 1065 } 1066 // Did we get an obsolete callback? 1067 if (mInteractionId > interactionId) { 1068 break; 1069 } 1070 // Did we time out? 1071 final long elapsedTimeMillis = 1072 SystemClock.uptimeMillis() - startTimeMillis; 1073 waitTimeMillis = TIMEOUT_INTERACTION_MILLIS - elapsedTimeMillis; 1074 if (waitTimeMillis <= 0) { 1075 break; 1076 } 1077 mGestureLock.wait(waitTimeMillis); 1078 } catch (InterruptedException ie) { 1079 /* ignore */ 1080 } 1081 } 1082 handleGestureIfNeededAndResetLocked(gestureId); 1083 } 1084 } break; 1085 case MSG_HANDLE_GESTURE_DEFAULT: { 1086 final int gestureId = message.arg1; 1087 handleGestureDefault(gestureId); 1088 } break; 1089 default: { 1090 throw new IllegalArgumentException("Unknown message type: " + type); 1091 } 1092 } 1093 } 1094 1095 private void handleGestureIfNeededAndResetLocked(int gestureId) { 1096 if (!mGestureResult) { 1097 handleGestureDefault(gestureId); 1098 } 1099 mGestureResult = false; 1100 mInteractionId = -1; 1101 } 1102 1103 public void scheduleHandleGesture(int gestureId, IAccessibilityServiceClient service) { 1104 final int interactionId = mInteractionCounter.incrementAndGet(); 1105 mHandlerCaller.obtainMessageIIO(MSG_HANDLE_GESTURE, gestureId, interactionId, 1106 service).sendToTarget(); 1107 } 1108 1109 public void scheduleHandleGestureDefault(int gestureId) { 1110 final int interactionId = mInteractionCounter.incrementAndGet(); 1111 mHandlerCaller.obtainMessageI(MSG_HANDLE_GESTURE_DEFAULT, gestureId).sendToTarget(); 1112 } 1113 1114 private void handleGestureDefault(int gestureId) { 1115 Service service = getDefaultGestureHandlingHelperService(); 1116 1117 // Global actions. 1118 switch (gestureId) { 1119 case AccessibilityService.GESTURE_SWIPE_DOWN_AND_LEFT: { 1120 service.performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK); 1121 } return; 1122 case AccessibilityService.GESTURE_SWIPE_DOWN_AND_RIGHT: { 1123 service.performGlobalAction(AccessibilityService.GLOBAL_ACTION_HOME); 1124 } return; 1125 case AccessibilityService.GESTURE_SWIPE_UP_AND_LEFT: { 1126 service.performGlobalAction(AccessibilityService.GLOBAL_ACTION_RECENTS); 1127 } return; 1128 case AccessibilityService.GESTURE_SWIPE_UP_AND_RIGHT: { 1129 service.performGlobalAction(AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS); 1130 } return; 1131 } 1132 1133 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 1134 1135 AccessibilityNodeInfo root = client.getRootInActiveWindow(service.mId); 1136 if (root == null) { 1137 return; 1138 } 1139 1140 AccessibilityNodeInfo current = root.findFocus( 1141 AccessibilityNodeInfo.FOCUS_ACCESSIBILITY); 1142 if (current == null) { 1143 current = root; 1144 } 1145 1146 // Local actions. 1147 AccessibilityNodeInfo next = null; 1148 switch (gestureId) { 1149 case AccessibilityService.GESTURE_SWIPE_UP: { 1150 next = current.focusSearch(View.ACCESSIBILITY_FOCUS_OUT); 1151 } break; 1152 case AccessibilityService.GESTURE_SWIPE_DOWN: { 1153 next = current.focusSearch(View.ACCESSIBILITY_FOCUS_IN); 1154 } break; 1155 case AccessibilityService.GESTURE_SWIPE_LEFT: { 1156 // TODO: Implement the RTL support. 1157// if (mLayoutDirection == View.LAYOUT_DIRECTION_LTR) { 1158 next = current.focusSearch(View.ACCESSIBILITY_FOCUS_BACKWARD); 1159// } else { // LAYOUT_DIRECTION_RTL 1160// next = current.focusSearch(View.ACCESSIBILITY_FOCUS_FORWARD); 1161// } 1162 } break; 1163 case AccessibilityService.GESTURE_SWIPE_RIGHT: { 1164 // TODO: Implement the RTL support. 1165// if (mLayoutDirection == View.LAYOUT_DIRECTION_LTR) { 1166 next = current.focusSearch(View.ACCESSIBILITY_FOCUS_FORWARD); 1167// } else { // LAYOUT_DIRECTION_RTL 1168// next = current.focusSearch(View.ACCESSIBILITY_FOCUS_BACKWARD); 1169// } 1170 } break; 1171 case AccessibilityService.GESTURE_SWIPE_UP_AND_DOWN: { 1172 next = current.focusSearch(View.ACCESSIBILITY_FOCUS_UP); 1173 } break; 1174 case AccessibilityService.GESTURE_SWIPE_DOWN_AND_UP: { 1175 next = current.focusSearch(View.ACCESSIBILITY_FOCUS_DOWN); 1176 } break; 1177 case AccessibilityService.GESTURE_SWIPE_LEFT_AND_RIGHT: { 1178 next = current.focusSearch(View.ACCESSIBILITY_FOCUS_LEFT); 1179 } break; 1180 case AccessibilityService.GESTURE_SWIPE_RIGHT_AND_LEFT: { 1181 next = current.focusSearch(View.ACCESSIBILITY_FOCUS_RIGHT); 1182 } break; 1183 } 1184 if (next != null && !next.equals(current)) { 1185 next.performAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS); 1186 } 1187 } 1188 } 1189 1190 /** 1191 * This class represents an accessibility service. It stores all per service 1192 * data required for the service management, provides API for starting/stopping the 1193 * service and is responsible for adding/removing the service in the data structures 1194 * for service management. The class also exposes configuration interface that is 1195 * passed to the service it represents as soon it is bound. It also serves as the 1196 * connection for the service. 1197 */ 1198 class Service extends IAccessibilityServiceConnection.Stub 1199 implements ServiceConnection, DeathRecipient { 1200 int mId = 0; 1201 1202 AccessibilityServiceInfo mAccessibilityServiceInfo; 1203 1204 IBinder mService; 1205 1206 IAccessibilityServiceClient mServiceInterface; 1207 1208 int mEventTypes; 1209 1210 int mFeedbackType; 1211 1212 Set<String> mPackageNames = new HashSet<String>(); 1213 1214 boolean mIsDefault; 1215 1216 boolean mIncludeNotImportantViews; 1217 1218 long mNotificationTimeout; 1219 1220 ComponentName mComponentName; 1221 1222 Intent mIntent; 1223 1224 boolean mCanRetrieveScreenContent; 1225 1226 boolean mCanHandleGestures; 1227 1228 boolean mIsAutomation; 1229 1230 final Rect mTempBounds = new Rect(); 1231 1232 // the events pending events to be dispatched to this service 1233 final SparseArray<AccessibilityEvent> mPendingEvents = 1234 new SparseArray<AccessibilityEvent>(); 1235 1236 public Service(ComponentName componentName, 1237 AccessibilityServiceInfo accessibilityServiceInfo, boolean isAutomation) { 1238 mId = sIdCounter++; 1239 mComponentName = componentName; 1240 mAccessibilityServiceInfo = accessibilityServiceInfo; 1241 mIsAutomation = isAutomation; 1242 if (!isAutomation) { 1243 mCanRetrieveScreenContent = accessibilityServiceInfo.getCanRetrieveWindowContent(); 1244 mCanHandleGestures = accessibilityServiceInfo.getCanHandleGestures(); 1245 mIntent = new Intent().setComponent(mComponentName); 1246 mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL, 1247 com.android.internal.R.string.accessibility_binding_label); 1248 mIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity( 1249 mContext, 0, new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS), 0)); 1250 } else { 1251 mCanRetrieveScreenContent = true; 1252 mIncludeNotImportantViews = true; 1253 mCanHandleGestures = true; 1254 } 1255 setDynamicallyConfigurableProperties(accessibilityServiceInfo); 1256 } 1257 1258 public void setDynamicallyConfigurableProperties(AccessibilityServiceInfo info) { 1259 mEventTypes = info.eventTypes; 1260 mFeedbackType = info.feedbackType; 1261 String[] packageNames = info.packageNames; 1262 if (packageNames != null) { 1263 mPackageNames.addAll(Arrays.asList(packageNames)); 1264 } 1265 mNotificationTimeout = info.notificationTimeout; 1266 mIsDefault = (info.flags & DEFAULT) != 0; 1267 1268 if (!mIsAutomation) { 1269 final int targetSdkVersion = 1270 info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion; 1271 // TODO: Uncomment this line and remove the line below when JellyBean 1272 // SDK version is finalized. 1273 // if (targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN) { 1274 if (targetSdkVersion > Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { 1275 mIncludeNotImportantViews = 1276 (info.flags & INCLUDE_NOT_IMPORTANT_VIEWS) != 0; 1277 } 1278 } 1279 1280 synchronized (mLock) { 1281 tryAddServiceLocked(this); 1282 } 1283 } 1284 1285 /** 1286 * Binds to the accessibility service. 1287 * 1288 * @return True if binding is successful. 1289 */ 1290 public boolean bind() { 1291 if (!mIsAutomation && mService == null) { 1292 return mContext.bindService(mIntent, this, Context.BIND_AUTO_CREATE); 1293 } 1294 return false; 1295 } 1296 1297 /** 1298 * Unbinds form the accessibility service and removes it from the data 1299 * structures for service management. 1300 * 1301 * @return True if unbinding is successful. 1302 */ 1303 public boolean unbind() { 1304 if (mService != null) { 1305 synchronized (mLock) { 1306 tryRemoveServiceLocked(this); 1307 } 1308 if (!mIsAutomation) { 1309 mContext.unbindService(this); 1310 } 1311 return true; 1312 } 1313 return false; 1314 } 1315 1316 /** 1317 * Returns if the service is configured i.e. at least event types of interest 1318 * and feedback type must be set. 1319 * 1320 * @return True if the service is configured, false otherwise. 1321 */ 1322 public boolean isConfigured() { 1323 return (mEventTypes != 0 && mFeedbackType != 0 && mService != null); 1324 } 1325 1326 @Override 1327 public AccessibilityServiceInfo getServiceInfo() { 1328 synchronized (mLock) { 1329 return mAccessibilityServiceInfo; 1330 } 1331 } 1332 1333 @Override 1334 public void setServiceInfo(AccessibilityServiceInfo info) { 1335 synchronized (mLock) { 1336 // If the XML manifest had data to configure the service its info 1337 // should be already set. In such a case update only the dynamically 1338 // configurable properties. 1339 AccessibilityServiceInfo oldInfo = mAccessibilityServiceInfo; 1340 if (oldInfo != null) { 1341 oldInfo.updateDynamicallyConfigurableProperties(info); 1342 setDynamicallyConfigurableProperties(oldInfo); 1343 } else { 1344 setDynamicallyConfigurableProperties(info); 1345 } 1346 } 1347 } 1348 1349 @Override 1350 public void onServiceConnected(ComponentName componentName, IBinder service) { 1351 mService = service; 1352 mServiceInterface = IAccessibilityServiceClient.Stub.asInterface(service); 1353 try { 1354 mServiceInterface.setConnection(this, mId); 1355 synchronized (mLock) { 1356 tryAddServiceLocked(this); 1357 } 1358 } catch (RemoteException re) { 1359 Slog.w(LOG_TAG, "Error while setting Controller for service: " + service, re); 1360 } 1361 } 1362 1363 @Override 1364 public float findAccessibilityNodeInfoByViewId(int accessibilityWindowId, 1365 long accessibilityNodeId, int viewId, int interactionId, 1366 IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) 1367 throws RemoteException { 1368 final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId); 1369 IAccessibilityInteractionConnection connection = null; 1370 synchronized (mLock) { 1371 mSecurityPolicy.enforceCanRetrieveWindowContent(this); 1372 final boolean permissionGranted = mSecurityPolicy.canRetrieveWindowContent(this); 1373 if (!permissionGranted) { 1374 return 0; 1375 } else { 1376 connection = getConnectionLocked(resolvedWindowId); 1377 if (connection == null) { 1378 return 0; 1379 } 1380 } 1381 } 1382 final int flags = (mIncludeNotImportantViews) ? 1383 AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0; 1384 final int interrogatingPid = Binder.getCallingPid(); 1385 final long identityToken = Binder.clearCallingIdentity(); 1386 try { 1387 connection.findAccessibilityNodeInfoByViewId(accessibilityNodeId, viewId, 1388 interactionId, callback, flags, interrogatingPid, interrogatingTid); 1389 } catch (RemoteException re) { 1390 if (DEBUG) { 1391 Slog.e(LOG_TAG, "Error findAccessibilityNodeInfoByViewId()."); 1392 } 1393 } finally { 1394 Binder.restoreCallingIdentity(identityToken); 1395 } 1396 return getCompatibilityScale(resolvedWindowId); 1397 } 1398 1399 @Override 1400 public float findAccessibilityNodeInfosByText(int accessibilityWindowId, 1401 long accessibilityNodeId, String text, int interactionId, 1402 IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) 1403 throws RemoteException { 1404 final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId); 1405 IAccessibilityInteractionConnection connection = null; 1406 synchronized (mLock) { 1407 mSecurityPolicy.enforceCanRetrieveWindowContent(this); 1408 final boolean permissionGranted = 1409 mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId); 1410 if (!permissionGranted) { 1411 return 0; 1412 } else { 1413 connection = getConnectionLocked(resolvedWindowId); 1414 if (connection == null) { 1415 return 0; 1416 } 1417 } 1418 } 1419 final int flags = (mIncludeNotImportantViews) ? 1420 AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0; 1421 final int interrogatingPid = Binder.getCallingPid(); 1422 final long identityToken = Binder.clearCallingIdentity(); 1423 try { 1424 connection.findAccessibilityNodeInfosByText(accessibilityNodeId, text, 1425 interactionId, callback, flags, interrogatingPid, interrogatingTid); 1426 } catch (RemoteException re) { 1427 if (DEBUG) { 1428 Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfosByText()"); 1429 } 1430 } finally { 1431 Binder.restoreCallingIdentity(identityToken); 1432 } 1433 return getCompatibilityScale(resolvedWindowId); 1434 } 1435 1436 @Override 1437 public float findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId, 1438 long accessibilityNodeId, int interactionId, 1439 IAccessibilityInteractionConnectionCallback callback, int flags, 1440 long interrogatingTid) throws RemoteException { 1441 final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId); 1442 IAccessibilityInteractionConnection connection = null; 1443 synchronized (mLock) { 1444 mSecurityPolicy.enforceCanRetrieveWindowContent(this); 1445 final boolean permissionGranted = 1446 mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId); 1447 if (!permissionGranted) { 1448 return 0; 1449 } else { 1450 connection = getConnectionLocked(resolvedWindowId); 1451 if (connection == null) { 1452 return 0; 1453 } 1454 } 1455 } 1456 final int allFlags = flags | ((mIncludeNotImportantViews) ? 1457 AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0); 1458 final int interrogatingPid = Binder.getCallingPid(); 1459 final long identityToken = Binder.clearCallingIdentity(); 1460 try { 1461 connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId, 1462 interactionId, callback, allFlags, interrogatingPid, interrogatingTid); 1463 } catch (RemoteException re) { 1464 if (DEBUG) { 1465 Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfoByAccessibilityId()"); 1466 } 1467 } finally { 1468 Binder.restoreCallingIdentity(identityToken); 1469 } 1470 return getCompatibilityScale(resolvedWindowId); 1471 } 1472 1473 @Override 1474 public float findFocus(int accessibilityWindowId, long accessibilityNodeId, 1475 int focusType, int interactionId, 1476 IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) 1477 throws RemoteException { 1478 final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId); 1479 IAccessibilityInteractionConnection connection = null; 1480 synchronized (mLock) { 1481 mSecurityPolicy.enforceCanRetrieveWindowContent(this); 1482 final boolean permissionGranted = 1483 mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId); 1484 if (!permissionGranted) { 1485 return 0; 1486 } else { 1487 connection = getConnectionLocked(resolvedWindowId); 1488 if (connection == null) { 1489 return 0; 1490 } 1491 } 1492 } 1493 final int flags = (mIncludeNotImportantViews) ? 1494 AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0; 1495 final int interrogatingPid = Binder.getCallingPid(); 1496 final long identityToken = Binder.clearCallingIdentity(); 1497 try { 1498 connection.findFocus(accessibilityNodeId, interactionId, focusType, callback, 1499 flags, interrogatingPid, interrogatingTid); 1500 } catch (RemoteException re) { 1501 if (DEBUG) { 1502 Slog.e(LOG_TAG, "Error calling findAccessibilityFocus()"); 1503 } 1504 } finally { 1505 Binder.restoreCallingIdentity(identityToken); 1506 } 1507 return getCompatibilityScale(resolvedWindowId); 1508 } 1509 1510 @Override 1511 public float focusSearch(int accessibilityWindowId, long accessibilityNodeId, 1512 int direction, int interactionId, 1513 IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) 1514 throws RemoteException { 1515 final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId); 1516 IAccessibilityInteractionConnection connection = null; 1517 synchronized (mLock) { 1518 mSecurityPolicy.enforceCanRetrieveWindowContent(this); 1519 final boolean permissionGranted = 1520 mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId); 1521 if (!permissionGranted) { 1522 return 0; 1523 } else { 1524 connection = getConnectionLocked(resolvedWindowId); 1525 if (connection == null) { 1526 return 0; 1527 } 1528 } 1529 } 1530 final int flags = (mIncludeNotImportantViews) ? 1531 AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0; 1532 final int interrogatingPid = Binder.getCallingPid(); 1533 final long identityToken = Binder.clearCallingIdentity(); 1534 try { 1535 connection.focusSearch(accessibilityNodeId, interactionId, direction, callback, 1536 flags, interrogatingPid, interrogatingTid); 1537 } catch (RemoteException re) { 1538 if (DEBUG) { 1539 Slog.e(LOG_TAG, "Error calling accessibilityFocusSearch()"); 1540 } 1541 } finally { 1542 Binder.restoreCallingIdentity(identityToken); 1543 } 1544 return getCompatibilityScale(resolvedWindowId); 1545 } 1546 1547 @Override 1548 public boolean performAccessibilityAction(int accessibilityWindowId, 1549 long accessibilityNodeId, int action, Bundle arguments, int interactionId, 1550 IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) { 1551 final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId); 1552 IAccessibilityInteractionConnection connection = null; 1553 synchronized (mLock) { 1554 final boolean permissionGranted = mSecurityPolicy.canPerformActionLocked(this, 1555 resolvedWindowId, action); 1556 if (!permissionGranted) { 1557 return false; 1558 } else { 1559 connection = getConnectionLocked(resolvedWindowId); 1560 if (connection == null) { 1561 return false; 1562 } 1563 } 1564 } 1565 final long identityToken = Binder.clearCallingIdentity(); 1566 try { 1567 final int flags = (mIncludeNotImportantViews) ? 1568 AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0; 1569 final int interrogatingPid = Binder.getCallingPid(); 1570 connection.performAccessibilityAction(accessibilityNodeId, action, arguments, 1571 interactionId, callback, flags, interrogatingPid, interrogatingTid); 1572 } catch (RemoteException re) { 1573 if (DEBUG) { 1574 Slog.e(LOG_TAG, "Error calling performAccessibilityAction()"); 1575 } 1576 } finally { 1577 Binder.restoreCallingIdentity(identityToken); 1578 } 1579 return true; 1580 } 1581 1582 public boolean performGlobalAction(int action) { 1583 switch (action) { 1584 case AccessibilityService.GLOBAL_ACTION_BACK: { 1585 sendDownAndUpKeyEvents(KeyEvent.KEYCODE_BACK); 1586 } return true; 1587 case AccessibilityService.GLOBAL_ACTION_HOME: { 1588 sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HOME); 1589 } return true; 1590 case AccessibilityService.GLOBAL_ACTION_RECENTS: { 1591 sendDownAndUpKeyEvents(KeyEvent.KEYCODE_APP_SWITCH); 1592 } return true; 1593 case AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS: { 1594 // TODO: Implement when 6346026 is fixed. 1595 } return true; 1596 } 1597 return false; 1598 } 1599 1600 public void onServiceDisconnected(ComponentName componentName) { 1601 /* do nothing - #binderDied takes care */ 1602 } 1603 1604 public void linkToOwnDeath() throws RemoteException { 1605 mService.linkToDeath(this, 0); 1606 } 1607 1608 public void unlinkToOwnDeath() { 1609 mService.unlinkToDeath(this, 0); 1610 } 1611 1612 public void dispose() { 1613 try { 1614 // Clear the proxy in the other process so this 1615 // IAccessibilityServiceConnection can be garbage collected. 1616 mServiceInterface.setConnection(null, mId); 1617 } catch (RemoteException re) { 1618 /* ignore */ 1619 } 1620 mService = null; 1621 mServiceInterface = null; 1622 } 1623 1624 public void binderDied() { 1625 synchronized (mLock) { 1626 // The death recipient is unregistered in tryRemoveServiceLocked 1627 tryRemoveServiceLocked(this); 1628 // We no longer have an automation service, so restore 1629 // the state based on values in the settings database. 1630 if (mIsAutomation) { 1631 mUiAutomationService = null; 1632 handleAccessibilityEnabledSettingChangedLocked(); 1633 handleTouchExplorationEnabledSettingChangedLocked(); 1634 updateInputFilterLocked(); 1635 sendStateToClientsLocked(); 1636 } 1637 } 1638 } 1639 1640 private void sendDownAndUpKeyEvents(int keyCode) { 1641 final long token = Binder.clearCallingIdentity(); 1642 1643 // Inject down. 1644 final long downTime = SystemClock.uptimeMillis(); 1645 KeyEvent down = KeyEvent.obtain(downTime, downTime, KeyEvent.ACTION_DOWN, keyCode, 0, 0, 1646 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FROM_SYSTEM, 1647 InputDevice.SOURCE_KEYBOARD, null); 1648 InputManager.getInstance().injectInputEvent(down, 1649 InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); 1650 down.recycle(); 1651 1652 // Inject up. 1653 final long upTime = SystemClock.uptimeMillis(); 1654 KeyEvent up = KeyEvent.obtain(downTime, upTime, KeyEvent.ACTION_UP, keyCode, 0, 0, 1655 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FROM_SYSTEM, 1656 InputDevice.SOURCE_KEYBOARD, null); 1657 InputManager.getInstance().injectInputEvent(up, 1658 InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); 1659 up.recycle(); 1660 1661 Binder.restoreCallingIdentity(token); 1662 } 1663 1664 private IAccessibilityInteractionConnection getConnectionLocked(int windowId) { 1665 if (DEBUG) { 1666 Slog.i(LOG_TAG, "Trying to get interaction connection to windowId: " + windowId); 1667 } 1668 AccessibilityConnectionWrapper wrapper = mWindowIdToInteractionConnectionWrapperMap.get( 1669 windowId); 1670 if (wrapper != null && wrapper.mConnection != null) { 1671 return wrapper.mConnection; 1672 } 1673 if (DEBUG) { 1674 Slog.e(LOG_TAG, "No interaction connection to window: " + windowId); 1675 } 1676 return null; 1677 } 1678 1679 private int resolveAccessibilityWindowId(int accessibilityWindowId) { 1680 if (accessibilityWindowId == AccessibilityNodeInfo.ACTIVE_WINDOW_ID) { 1681 return mSecurityPolicy.mRetrievalAlowingWindowId; 1682 } 1683 return accessibilityWindowId; 1684 } 1685 1686 private float getCompatibilityScale(int windowId) { 1687 IBinder windowToken = mWindowIdToWindowTokenMap.get(windowId); 1688 return mWindowManagerService.getWindowCompatibilityScale(windowToken); 1689 } 1690 } 1691 1692 final class SecurityPolicy { 1693 private static final int VALID_ACTIONS = 1694 AccessibilityNodeInfo.ACTION_CLICK 1695 | AccessibilityNodeInfo.ACTION_FOCUS 1696 | AccessibilityNodeInfo.ACTION_CLEAR_FOCUS 1697 | AccessibilityNodeInfo.ACTION_SELECT 1698 | AccessibilityNodeInfo.ACTION_CLEAR_SELECTION 1699 | AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS 1700 | AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS 1701 | AccessibilityNodeInfo.ACTION_NEXT_AT_GRANULARITY 1702 | AccessibilityNodeInfo.ACTION_PREVIOUS_AT_GRANULARITY; 1703 1704 private static final int RETRIEVAL_ALLOWING_EVENT_TYPES = 1705 AccessibilityEvent.TYPE_VIEW_CLICKED 1706 | AccessibilityEvent.TYPE_VIEW_FOCUSED 1707 | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER 1708 | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT 1709 | AccessibilityEvent.TYPE_VIEW_LONG_CLICKED 1710 | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED 1711 | AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED 1712 | AccessibilityEvent.TYPE_VIEW_SELECTED 1713 | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED 1714 | AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED 1715 | AccessibilityEvent.TYPE_VIEW_SCROLLED 1716 | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED 1717 | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED; 1718 1719 private static final int RETRIEVAL_ALLOWING_WINDOW_CHANGE_EVENT_TYPES = 1720 AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED 1721 | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER 1722 | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT 1723 | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED; 1724 1725 private int mRetrievalAlowingWindowId; 1726 1727 private boolean canDispatchAccessibilityEvent(AccessibilityEvent event) { 1728 // Send window changed event only for the retrieval allowing window. 1729 return (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED 1730 || event.getWindowId() == mRetrievalAlowingWindowId); 1731 } 1732 1733 public void updateRetrievalAllowingWindowAndEventSourceLocked(AccessibilityEvent event) { 1734 final int windowId = event.getWindowId(); 1735 final int eventType = event.getEventType(); 1736 if ((eventType & RETRIEVAL_ALLOWING_WINDOW_CHANGE_EVENT_TYPES) != 0) { 1737 mRetrievalAlowingWindowId = windowId; 1738 } 1739 if ((eventType & RETRIEVAL_ALLOWING_EVENT_TYPES) == 0) { 1740 event.setSource(null); 1741 } 1742 } 1743 1744 public int getRetrievalAllowingWindowLocked() { 1745 return mRetrievalAlowingWindowId; 1746 } 1747 1748 public boolean canGetAccessibilityNodeInfoLocked(Service service, int windowId) { 1749 return canRetrieveWindowContent(service) && isRetrievalAllowingWindow(windowId); 1750 } 1751 1752 public boolean canPerformActionLocked(Service service, int windowId, int action) { 1753 return canRetrieveWindowContent(service) 1754 && isRetrievalAllowingWindow(windowId) 1755 && isActionPermitted(action); 1756 } 1757 1758 public boolean canRetrieveWindowContent(Service service) { 1759 return service.mCanRetrieveScreenContent; 1760 } 1761 1762 public void enforceCanRetrieveWindowContent(Service service) throws RemoteException { 1763 // This happens due to incorrect registration so make it apparent. 1764 if (!canRetrieveWindowContent(service)) { 1765 Slog.e(LOG_TAG, "Accessibility serivce " + service.mComponentName + " does not " + 1766 "declare android:canRetrieveWindowContent."); 1767 throw new RemoteException(); 1768 } 1769 } 1770 1771 private boolean isRetrievalAllowingWindow(int windowId) { 1772 return (mRetrievalAlowingWindowId == windowId); 1773 } 1774 1775 private boolean isActionPermitted(int action) { 1776 return (VALID_ACTIONS & action) != 0; 1777 } 1778 1779 private void enforceCallingPermission(String permission, String function) { 1780 if (OWN_PROCESS_ID == Binder.getCallingPid()) { 1781 return; 1782 } 1783 final int permissionStatus = mContext.checkCallingPermission(permission); 1784 if (permissionStatus != PackageManager.PERMISSION_GRANTED) { 1785 throw new SecurityException("You do not have " + permission 1786 + " required to call " + function); 1787 } 1788 } 1789 } 1790} 1791