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