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