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