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; 18 19 import com.android.internal.content.PackageMonitor; 20 import com.android.internal.os.HandlerCaller; 21 import com.android.internal.os.HandlerCaller.SomeArgs; 22 23 import android.accessibilityservice.AccessibilityService; 24 import android.accessibilityservice.AccessibilityServiceInfo; 25 import android.accessibilityservice.IAccessibilityServiceConnection; 26 import android.accessibilityservice.IEventListener; 27 import android.app.PendingIntent; 28 import android.content.BroadcastReceiver; 29 import android.content.ComponentName; 30 import android.content.ContentResolver; 31 import android.content.Context; 32 import android.content.Intent; 33 import android.content.IntentFilter; 34 import android.content.ServiceConnection; 35 import android.content.pm.PackageManager; 36 import android.content.pm.ResolveInfo; 37 import android.content.pm.ServiceInfo; 38 import android.database.ContentObserver; 39 import android.net.Uri; 40 import android.os.Binder; 41 import android.os.DeadObjectException; 42 import android.os.Handler; 43 import android.os.IBinder; 44 import android.os.Message; 45 import android.os.RemoteException; 46 import android.provider.Settings; 47 import android.text.TextUtils; 48 import android.text.TextUtils.SimpleStringSplitter; 49 import android.util.Config; 50 import android.util.Slog; 51 import android.util.SparseArray; 52 import android.view.accessibility.AccessibilityEvent; 53 import android.view.accessibility.IAccessibilityManager; 54 import android.view.accessibility.IAccessibilityManagerClient; 55 56 import java.util.ArrayList; 57 import java.util.Arrays; 58 import java.util.HashMap; 59 import java.util.HashSet; 60 import java.util.Iterator; 61 import java.util.List; 62 import java.util.Map; 63 import java.util.Set; 64 65 /** 66 * This class is instantiated by the system as a system level service and can be 67 * accessed only by the system. The task of this service is to be a centralized 68 * event dispatch for {@link AccessibilityEvent}s generated across all processes 69 * on the device. Events are dispatched to {@link AccessibilityService}s. 70 * 71 * @hide 72 */ 73 public class AccessibilityManagerService extends IAccessibilityManager.Stub 74 implements HandlerCaller.Callback { 75 76 private static final String LOG_TAG = "AccessibilityManagerService"; 77 78 private static int sIdCounter = 0; 79 80 private static final int OWN_PROCESS_ID = android.os.Process.myPid(); 81 82 private static final int DO_SET_SERVICE_INFO = 10; 83 84 final HandlerCaller mCaller; 85 86 final Context mContext; 87 88 final Object mLock = new Object(); 89 90 final List<Service> mServices = new ArrayList<Service>(); 91 92 final List<IAccessibilityManagerClient> mClients = 93 new ArrayList<IAccessibilityManagerClient>(); 94 95 final Map<ComponentName, Service> mComponentNameToServiceMap = 96 new HashMap<ComponentName, Service>(); 97 98 private final List<ServiceInfo> mInstalledServices = new ArrayList<ServiceInfo>(); 99 100 private final Set<ComponentName> mEnabledServices = new HashSet<ComponentName>(); 101 102 private final SimpleStringSplitter mStringColonSplitter = new SimpleStringSplitter(':'); 103 104 private PackageManager mPackageManager; 105 106 private int mHandledFeedbackTypes = 0; 107 108 private boolean mIsEnabled; 109 110 /** 111 * Handler for delayed event dispatch. 112 */ 113 private Handler mHandler = new Handler() { 114 115 @Override 116 public void handleMessage(Message message) { 117 Service service = (Service) message.obj; 118 int eventType = message.arg1; 119 120 synchronized (mLock) { 121 notifyEventListenerLocked(service, eventType); 122 AccessibilityEvent oldEvent = service.mPendingEvents.get(eventType); 123 service.mPendingEvents.remove(eventType); 124 tryRecycleLocked(oldEvent); 125 } 126 } 127 }; 128 129 /** 130 * Creates a new instance. 131 * 132 * @param context A {@link Context} instance. 133 */ 134 AccessibilityManagerService(Context context) { 135 mContext = context; 136 mPackageManager = mContext.getPackageManager(); 137 mCaller = new HandlerCaller(context, this); 138 139 registerPackageChangeAndBootCompletedBroadcastReceiver(); 140 registerSettingsContentObservers(); 141 } 142 143 /** 144 * Registers a {@link BroadcastReceiver} for the events of 145 * adding/changing/removing/restarting a package and boot completion. 146 */ 147 private void registerPackageChangeAndBootCompletedBroadcastReceiver() { 148 Context context = mContext; 149 150 PackageMonitor monitor = new PackageMonitor() { 151 @Override 152 public void onSomePackagesChanged() { 153 synchronized (mLock) { 154 populateAccessibilityServiceListLocked(); 155 manageServicesLocked(); 156 } 157 } 158 159 @Override 160 public boolean onHandleForceStop(Intent intent, String[] packages, 161 int uid, boolean doit) { 162 synchronized (mLock) { 163 boolean changed = false; 164 Iterator<ComponentName> it = mEnabledServices.iterator(); 165 while (it.hasNext()) { 166 ComponentName comp = it.next(); 167 String compPkg = comp.getPackageName(); 168 for (String pkg : packages) { 169 if (compPkg.equals(pkg)) { 170 if (!doit) { 171 return true; 172 } 173 it.remove(); 174 changed = true; 175 } 176 } 177 } 178 if (changed) { 179 it = mEnabledServices.iterator(); 180 StringBuilder str = new StringBuilder(); 181 while (it.hasNext()) { 182 if (str.length() > 0) { 183 str.append(':'); 184 } 185 str.append(it.next().flattenToShortString()); 186 } 187 Settings.Secure.putString(mContext.getContentResolver(), 188 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, 189 str.toString()); 190 manageServicesLocked(); 191 } 192 return false; 193 } 194 } 195 196 @Override 197 public void onReceive(Context context, Intent intent) { 198 if (intent.getAction() == Intent.ACTION_BOOT_COMPLETED) { 199 synchronized (mLock) { 200 populateAccessibilityServiceListLocked(); 201 202 // get the accessibility enabled setting on boot 203 mIsEnabled = Settings.Secure.getInt(mContext.getContentResolver(), 204 Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1; 205 206 // if accessibility is enabled inform our clients we are on 207 if (mIsEnabled) { 208 updateClientsLocked(); 209 } 210 211 manageServicesLocked(); 212 } 213 214 return; 215 } 216 217 super.onReceive(context, intent); 218 } 219 }; 220 221 // package changes 222 monitor.register(context, true); 223 224 // boot completed 225 IntentFilter bootFiler = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); 226 mContext.registerReceiver(monitor, bootFiler); 227 } 228 229 /** 230 * {@link ContentObserver}s for {@link Settings.Secure#ACCESSIBILITY_ENABLED} 231 * and {@link Settings.Secure#ENABLED_ACCESSIBILITY_SERVICES} settings. 232 */ 233 private void registerSettingsContentObservers() { 234 ContentResolver contentResolver = mContext.getContentResolver(); 235 236 Uri enabledUri = Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_ENABLED); 237 contentResolver.registerContentObserver(enabledUri, false, 238 new ContentObserver(new Handler()) { 239 @Override 240 public void onChange(boolean selfChange) { 241 super.onChange(selfChange); 242 243 mIsEnabled = Settings.Secure.getInt(mContext.getContentResolver(), 244 Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1; 245 246 synchronized (mLock) { 247 if (mIsEnabled) { 248 manageServicesLocked(); 249 } else { 250 unbindAllServicesLocked(); 251 } 252 updateClientsLocked(); 253 } 254 } 255 }); 256 257 Uri providersUri = 258 Settings.Secure.getUriFor(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); 259 contentResolver.registerContentObserver(providersUri, false, 260 new ContentObserver(new Handler()) { 261 @Override 262 public void onChange(boolean selfChange) { 263 super.onChange(selfChange); 264 265 synchronized (mLock) { 266 manageServicesLocked(); 267 } 268 } 269 }); 270 } 271 272 public void addClient(IAccessibilityManagerClient client) { 273 synchronized (mLock) { 274 try { 275 client.setEnabled(mIsEnabled); 276 mClients.add(client); 277 } catch (RemoteException re) { 278 Slog.w(LOG_TAG, "Dead AccessibilityManagerClient: " + client, re); 279 } 280 } 281 } 282 283 public boolean sendAccessibilityEvent(AccessibilityEvent event) { 284 synchronized (mLock) { 285 notifyAccessibilityServicesDelayedLocked(event, false); 286 notifyAccessibilityServicesDelayedLocked(event, true); 287 } 288 // event not scheduled for dispatch => recycle 289 if (mHandledFeedbackTypes == 0) { 290 event.recycle(); 291 } else { 292 mHandledFeedbackTypes = 0; 293 } 294 295 return (OWN_PROCESS_ID != Binder.getCallingPid()); 296 } 297 298 public List<ServiceInfo> getAccessibilityServiceList() { 299 synchronized (mLock) { 300 return mInstalledServices; 301 } 302 } 303 304 public void interrupt() { 305 synchronized (mLock) { 306 for (int i = 0, count = mServices.size(); i < count; i++) { 307 Service service = mServices.get(i); 308 try { 309 service.mServiceInterface.onInterrupt(); 310 } catch (RemoteException re) { 311 if (re instanceof DeadObjectException) { 312 Slog.w(LOG_TAG, "Dead " + service.mService + ". Cleaning up."); 313 if (removeDeadServiceLocked(service)) { 314 count--; 315 i--; 316 } 317 } else { 318 Slog.e(LOG_TAG, "Error during sending interrupt request to " 319 + service.mService, re); 320 } 321 } 322 } 323 } 324 } 325 326 public void executeMessage(Message message) { 327 switch (message.what) { 328 case DO_SET_SERVICE_INFO: 329 SomeArgs arguments = ((SomeArgs) message.obj); 330 331 AccessibilityServiceInfo info = (AccessibilityServiceInfo) arguments.arg1; 332 Service service = (Service) arguments.arg2; 333 334 synchronized (mLock) { 335 service.mEventTypes = info.eventTypes; 336 service.mFeedbackType = info.feedbackType; 337 String[] packageNames = info.packageNames; 338 if (packageNames != null) { 339 service.mPackageNames.addAll(Arrays.asList(packageNames)); 340 } 341 service.mNotificationTimeout = info.notificationTimeout; 342 service.mIsDefault = (info.flags & AccessibilityServiceInfo.DEFAULT) != 0; 343 } 344 return; 345 default: 346 Slog.w(LOG_TAG, "Unknown message type: " + message.what); 347 } 348 } 349 350 /** 351 * Populates the cached list of installed {@link AccessibilityService}s. 352 */ 353 private void populateAccessibilityServiceListLocked() { 354 mInstalledServices.clear(); 355 356 List<ResolveInfo> installedServices = mPackageManager.queryIntentServices( 357 new Intent(AccessibilityService.SERVICE_INTERFACE), PackageManager.GET_SERVICES); 358 359 for (int i = 0, count = installedServices.size(); i < count; i++) { 360 mInstalledServices.add(installedServices.get(i).serviceInfo); 361 } 362 } 363 364 /** 365 * Performs {@link AccessibilityService}s delayed notification. The delay is configurable 366 * and denotes the period after the last event before notifying the service. 367 * 368 * @param event The event. 369 * @param isDefault True to notify default listeners, not default services. 370 */ 371 private void notifyAccessibilityServicesDelayedLocked(AccessibilityEvent event, 372 boolean isDefault) { 373 try { 374 for (int i = 0, count = mServices.size(); i < count; i++) { 375 Service service = mServices.get(i); 376 377 if (service.mIsDefault == isDefault) { 378 if (canDispathEventLocked(service, event, mHandledFeedbackTypes)) { 379 mHandledFeedbackTypes |= service.mFeedbackType; 380 notifyAccessibilityServiceDelayedLocked(service, event); 381 } 382 } 383 } 384 } catch (IndexOutOfBoundsException oobe) { 385 // An out of bounds exception can happen if services are going away 386 // as the for loop is running. If that happens, just bail because 387 // there are no more services to notify. 388 return; 389 } 390 } 391 392 /** 393 * Performs an {@link AccessibilityService} delayed notification. The delay is configurable 394 * and denotes the period after the last event before notifying the service. 395 * 396 * @param service The service. 397 * @param event The event. 398 */ 399 private void notifyAccessibilityServiceDelayedLocked(Service service, 400 AccessibilityEvent event) { 401 synchronized (mLock) { 402 int eventType = event.getEventType(); 403 AccessibilityEvent oldEvent = service.mPendingEvents.get(eventType); 404 service.mPendingEvents.put(eventType, event); 405 406 int what = eventType | (service.mId << 16); 407 if (oldEvent != null) { 408 mHandler.removeMessages(what); 409 tryRecycleLocked(oldEvent); 410 } 411 412 Message message = mHandler.obtainMessage(what, service); 413 message.arg1 = event.getEventType(); 414 mHandler.sendMessageDelayed(message, service.mNotificationTimeout); 415 } 416 } 417 418 /** 419 * Recycles an event if it can be safely recycled. The condition is that no 420 * not notified service is interested in the event. 421 * 422 * @param event The event. 423 */ 424 private void tryRecycleLocked(AccessibilityEvent event) { 425 if (event == null) { 426 return; 427 } 428 int eventType = event.getEventType(); 429 List<Service> services = mServices; 430 431 // linear in the number of service which is not large 432 for (int i = 0, count = services.size(); i < count; i++) { 433 Service service = services.get(i); 434 if (service.mPendingEvents.get(eventType) == event) { 435 return; 436 } 437 } 438 event.recycle(); 439 } 440 441 /** 442 * Notifies a service for a scheduled event given the event type. 443 * 444 * @param service The service. 445 * @param eventType The type of the event to dispatch. 446 */ 447 private void notifyEventListenerLocked(Service service, int eventType) { 448 IEventListener listener = service.mServiceInterface; 449 AccessibilityEvent event = service.mPendingEvents.get(eventType); 450 451 try { 452 listener.onAccessibilityEvent(event); 453 if (Config.DEBUG) { 454 Slog.i(LOG_TAG, "Event " + event + " sent to " + listener); 455 } 456 } catch (RemoteException re) { 457 if (re instanceof DeadObjectException) { 458 Slog.w(LOG_TAG, "Dead " + service.mService + ". Cleaning up."); 459 synchronized (mLock) { 460 removeDeadServiceLocked(service); 461 } 462 } else { 463 Slog.e(LOG_TAG, "Error during sending " + event + " to " + service.mService, re); 464 } 465 } 466 } 467 468 /** 469 * Removes a dead service. 470 * 471 * @param service The service. 472 * @return True if the service was removed, false otherwise. 473 */ 474 private boolean removeDeadServiceLocked(Service service) { 475 mServices.remove(service); 476 mHandler.removeMessages(service.mId); 477 478 if (Config.DEBUG) { 479 Slog.i(LOG_TAG, "Dead service " + service.mService + " removed"); 480 } 481 482 if (mServices.isEmpty()) { 483 mIsEnabled = false; 484 updateClientsLocked(); 485 } 486 487 return true; 488 } 489 490 /** 491 * Determines if given event can be dispatched to a service based on the package of the 492 * event source and already notified services for that event type. Specifically, a 493 * service is notified if it is interested in events from the package and no other service 494 * providing the same feedback type has been notified. Exception are services the 495 * provide generic feedback (feedback type left as a safety net for unforeseen feedback 496 * types) which are always notified. 497 * 498 * @param service The potential receiver. 499 * @param event The event. 500 * @param handledFeedbackTypes The feedback types for which services have been notified. 501 * @return True if the listener should be notified, false otherwise. 502 */ 503 private boolean canDispathEventLocked(Service service, AccessibilityEvent event, 504 int handledFeedbackTypes) { 505 506 if (!service.isConfigured()) { 507 return false; 508 } 509 510 if (!service.mService.isBinderAlive()) { 511 removeDeadServiceLocked(service); 512 return false; 513 } 514 515 int eventType = event.getEventType(); 516 if ((service.mEventTypes & eventType) != eventType) { 517 return false; 518 } 519 520 Set<String> packageNames = service.mPackageNames; 521 CharSequence packageName = event.getPackageName(); 522 523 if (packageNames.isEmpty() || packageNames.contains(packageName)) { 524 int feedbackType = service.mFeedbackType; 525 if ((handledFeedbackTypes & feedbackType) != feedbackType 526 || feedbackType == AccessibilityServiceInfo.FEEDBACK_GENERIC) { 527 return true; 528 } 529 } 530 531 return false; 532 } 533 534 /** 535 * Manages services by starting enabled ones and stopping disabled ones. 536 */ 537 private void manageServicesLocked() { 538 populateEnabledServicesLocked(mEnabledServices); 539 updateServicesStateLocked(mInstalledServices, mEnabledServices); 540 } 541 542 /** 543 * Unbinds all bound services. 544 */ 545 private void unbindAllServicesLocked() { 546 List<Service> services = mServices; 547 548 for (int i = 0, count = services.size(); i < count; i++) { 549 Service service = services.get(i); 550 551 service.unbind(); 552 mComponentNameToServiceMap.remove(service.mComponentName); 553 } 554 services.clear(); 555 } 556 557 /** 558 * Populates a list with the {@link ComponentName}s of all enabled 559 * {@link AccessibilityService}s. 560 * 561 * @param enabledServices The list. 562 */ 563 private void populateEnabledServicesLocked(Set<ComponentName> enabledServices) { 564 enabledServices.clear(); 565 566 String servicesValue = Settings.Secure.getString(mContext.getContentResolver(), 567 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); 568 569 if (servicesValue != null) { 570 TextUtils.SimpleStringSplitter splitter = mStringColonSplitter; 571 splitter.setString(servicesValue); 572 while (splitter.hasNext()) { 573 String str = splitter.next(); 574 if (str == null || str.length() <= 0) { 575 continue; 576 } 577 ComponentName enabledService = ComponentName.unflattenFromString(str); 578 if (enabledService != null) { 579 enabledServices.add(enabledService); 580 } 581 } 582 } 583 } 584 585 /** 586 * Updates the state of each service by starting (or keeping running) enabled ones and 587 * stopping the rest. 588 * 589 * @param installedServices All installed {@link AccessibilityService}s. 590 * @param enabledServices The {@link ComponentName}s of the enabled services. 591 */ 592 private void updateServicesStateLocked(List<ServiceInfo> installedServices, 593 Set<ComponentName> enabledServices) { 594 595 Map<ComponentName, Service> componentNameToServiceMap = mComponentNameToServiceMap; 596 List<Service> services = mServices; 597 boolean isEnabled = mIsEnabled; 598 599 for (int i = 0, count = installedServices.size(); i < count; i++) { 600 ServiceInfo intalledService = installedServices.get(i); 601 ComponentName componentName = new ComponentName(intalledService.packageName, 602 intalledService.name); 603 Service service = componentNameToServiceMap.get(componentName); 604 605 if (isEnabled && enabledServices.contains(componentName)) { 606 if (service == null) { 607 new Service(componentName).bind(); 608 } 609 } else { 610 if (service != null) { 611 service.unbind(); 612 componentNameToServiceMap.remove(componentName); 613 services.remove(service); 614 } 615 } 616 } 617 } 618 619 /** 620 * Updates the state of {@link android.view.accessibility.AccessibilityManager} clients. 621 */ 622 private void updateClientsLocked() { 623 for (int i = 0, count = mClients.size(); i < count; i++) { 624 try { 625 mClients.get(i).setEnabled(mIsEnabled); 626 } catch (RemoteException re) { 627 mClients.remove(i); 628 count--; 629 i--; 630 } 631 } 632 } 633 634 /** 635 * This class represents an accessibility service. It stores all per service 636 * data required for the service management, provides API for starting/stopping the 637 * service and is responsible for adding/removing the service in the data structures 638 * for service management. The class also exposes configuration interface that is 639 * passed to the service it represents as soon it is bound. It also serves as the 640 * connection for the service. 641 */ 642 class Service extends IAccessibilityServiceConnection.Stub implements ServiceConnection { 643 int mId = 0; 644 645 IBinder mService; 646 647 IEventListener mServiceInterface; 648 649 int mEventTypes; 650 651 int mFeedbackType; 652 653 Set<String> mPackageNames = new HashSet<String>(); 654 655 boolean mIsDefault; 656 657 long mNotificationTimeout; 658 659 boolean mIsActive; 660 661 ComponentName mComponentName; 662 663 Intent mIntent; 664 665 // the events pending events to be dispatched to this service 666 final SparseArray<AccessibilityEvent> mPendingEvents = 667 new SparseArray<AccessibilityEvent>(); 668 669 Service(ComponentName componentName) { 670 mId = sIdCounter++; 671 mComponentName = componentName; 672 mIntent = new Intent().setComponent(mComponentName); 673 mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL, 674 com.android.internal.R.string.accessibility_binding_label); 675 mIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity( 676 mContext, 0, new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS), 0)); 677 } 678 679 /** 680 * Binds to the accessibility service. 681 */ 682 public void bind() { 683 if (mService == null) { 684 mContext.bindService(mIntent, this, Context.BIND_AUTO_CREATE); 685 } 686 } 687 688 /** 689 * Unbinds form the accessibility service and removes it from the data 690 * structures for service management. 691 */ 692 public void unbind() { 693 if (mService != null) { 694 mContext.unbindService(this); 695 } 696 } 697 698 /** 699 * Returns if the service is configured i.e. at least event types of interest 700 * and feedback type must be set. 701 * 702 * @return True if the service is configured, false otherwise. 703 */ 704 public boolean isConfigured() { 705 return (mEventTypes != 0 && mFeedbackType != 0); 706 } 707 708 public void setServiceInfo(AccessibilityServiceInfo info) { 709 mCaller.obtainMessageOO(DO_SET_SERVICE_INFO, info, this).sendToTarget(); 710 } 711 712 public void onServiceConnected(ComponentName componentName, IBinder service) { 713 mService = service; 714 mServiceInterface = IEventListener.Stub.asInterface(service); 715 716 try { 717 mServiceInterface.setConnection(this); 718 synchronized (mLock) { 719 if (!mServices.contains(this)) { 720 mServices.add(this); 721 mComponentNameToServiceMap.put(componentName, this); 722 } 723 } 724 } catch (RemoteException re) { 725 Slog.w(LOG_TAG, "Error while setting Controller for service: " + service, re); 726 } 727 } 728 729 public void onServiceDisconnected(ComponentName componentName) { 730 synchronized (mLock) { 731 Service service = mComponentNameToServiceMap.remove(componentName); 732 mServices.remove(service); 733 } 734 } 735 } 736 } 737