Home | History | Annotate | Download | only in server
      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