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