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