AccessibilityManagerService.java revision d07d60b9bc0fd2e88f58d6e68dbfadad1bdd31cf
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 android.Manifest;
20import android.accessibilityservice.AccessibilityService;
21import android.accessibilityservice.AccessibilityServiceInfo;
22import android.accessibilityservice.IAccessibilityServiceConnection;
23import android.accessibilityservice.IEventListener;
24import android.app.PendingIntent;
25import android.content.BroadcastReceiver;
26import android.content.ComponentName;
27import android.content.ContentResolver;
28import android.content.Context;
29import android.content.Intent;
30import android.content.IntentFilter;
31import android.content.ServiceConnection;
32import android.content.pm.PackageManager;
33import android.content.pm.ResolveInfo;
34import android.database.ContentObserver;
35import android.graphics.Rect;
36import android.net.Uri;
37import android.os.Binder;
38import android.os.Handler;
39import android.os.IBinder;
40import android.os.Message;
41import android.os.RemoteException;
42import android.os.ServiceManager;
43import android.provider.Settings;
44import android.text.TextUtils;
45import android.text.TextUtils.SimpleStringSplitter;
46import android.util.Slog;
47import android.util.SparseArray;
48import android.view.IWindow;
49import android.view.View;
50import android.view.accessibility.AccessibilityEvent;
51import android.view.accessibility.AccessibilityManager;
52import android.view.accessibility.AccessibilityNodeInfo;
53import android.view.accessibility.IAccessibilityInteractionConnection;
54import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
55import android.view.accessibility.IAccessibilityManager;
56import android.view.accessibility.IAccessibilityManagerClient;
57
58import com.android.internal.content.PackageMonitor;
59import com.android.internal.os.HandlerCaller;
60import com.android.internal.os.HandlerCaller.SomeArgs;
61import com.android.server.wm.WindowManagerService;
62
63import org.xmlpull.v1.XmlPullParserException;
64
65import java.io.IOException;
66import java.util.ArrayList;
67import java.util.Arrays;
68import java.util.HashMap;
69import java.util.HashSet;
70import java.util.Iterator;
71import java.util.List;
72import java.util.Map;
73import java.util.Set;
74
75/**
76 * This class is instantiated by the system as a system level service and can be
77 * accessed only by the system. The task of this service is to be a centralized
78 * event dispatch for {@link AccessibilityEvent}s generated across all processes
79 * on the device. Events are dispatched to {@link AccessibilityService}s.
80 *
81 * @hide
82 */
83public class AccessibilityManagerService extends IAccessibilityManager.Stub
84        implements HandlerCaller.Callback {
85
86    private static final boolean DEBUG = false;
87
88    private static final String LOG_TAG = "AccessibilityManagerService";
89
90    private static final String FUNCTION_REGISTER_EVENT_LISTENER =
91        "registerEventListener";
92
93    private static int sIdCounter = 0;
94
95    private static final int OWN_PROCESS_ID = android.os.Process.myPid();
96
97    private static final int DO_SET_SERVICE_INFO = 10;
98
99    private static int sNextWindowId;
100
101    final HandlerCaller mCaller;
102
103    final Context mContext;
104
105    final Object mLock = new Object();
106
107    final List<Service> mServices = new ArrayList<Service>();
108
109    final List<IAccessibilityManagerClient> mClients =
110        new ArrayList<IAccessibilityManagerClient>();
111
112    final Map<ComponentName, Service> mComponentNameToServiceMap = new HashMap<ComponentName, Service>();
113
114    private final List<AccessibilityServiceInfo> mInstalledServices = new ArrayList<AccessibilityServiceInfo>();
115
116    private final Set<ComponentName> mEnabledServices = new HashSet<ComponentName>();
117
118    private final SparseArray<IAccessibilityInteractionConnection> mWindowIdToInteractionConnectionMap =
119        new SparseArray<IAccessibilityInteractionConnection>();
120
121    private final SparseArray<IBinder> mWindowIdToWindowTokenMap = new SparseArray<IBinder>();
122
123    private final SimpleStringSplitter mStringColonSplitter = new SimpleStringSplitter(':');
124
125    private PackageManager mPackageManager;
126
127    private int mHandledFeedbackTypes = 0;
128
129    private boolean mIsAccessibilityEnabled;
130
131    private AccessibilityInputFilter mInputFilter;
132
133    private boolean mHasInputFilter;
134
135    private final List<AccessibilityServiceInfo> mEnabledServicesForFeedbackTempList = new ArrayList<AccessibilityServiceInfo>();
136
137    private boolean mIsTouchExplorationEnabled;
138
139    private final WindowManagerService mWindowManagerService;
140
141    private final SecurityPolicy mSecurityPolicy;
142
143    /**
144     * Handler for delayed event dispatch.
145     */
146    private Handler mHandler = new Handler() {
147
148        @Override
149        public void handleMessage(Message message) {
150            Service service = (Service) message.obj;
151            int eventType = message.arg1;
152
153            synchronized (mLock) {
154                notifyEventListenerLocked(service, eventType);
155            }
156        }
157    };
158
159    /**
160     * Creates a new instance.
161     *
162     * @param context A {@link Context} instance.
163     */
164    public AccessibilityManagerService(Context context) {
165        mContext = context;
166        mPackageManager = mContext.getPackageManager();
167        mCaller = new HandlerCaller(context, this);
168        mWindowManagerService = (WindowManagerService) ServiceManager.getService(
169                Context.WINDOW_SERVICE);
170        mSecurityPolicy = new SecurityPolicy();
171
172        registerPackageChangeAndBootCompletedBroadcastReceiver();
173        registerSettingsContentObservers();
174    }
175
176    /**
177     * Registers a {@link BroadcastReceiver} for the events of
178     * adding/changing/removing/restarting a package and boot completion.
179     */
180    private void registerPackageChangeAndBootCompletedBroadcastReceiver() {
181        Context context = mContext;
182
183        PackageMonitor monitor = new PackageMonitor() {
184            @Override
185            public void onSomePackagesChanged() {
186                synchronized (mLock) {
187                    populateAccessibilityServiceListLocked();
188                    manageServicesLocked();
189                }
190            }
191
192            @Override
193            public void onPackageRemoved(String packageName, int uid) {
194                synchronized (mLock) {
195                    Iterator<ComponentName> it = mEnabledServices.iterator();
196                    while (it.hasNext()) {
197                        ComponentName comp = it.next();
198                        String compPkg = comp.getPackageName();
199                        if (compPkg.equals(packageName)) {
200                            it.remove();
201                            updateEnabledAccessibilitySerivcesSettingLocked(mEnabledServices);
202                            return;
203                        }
204                    }
205                }
206            }
207
208            @Override
209            public boolean onHandleForceStop(Intent intent, String[] packages,
210                    int uid, boolean doit) {
211                synchronized (mLock) {
212                    boolean changed = false;
213                    Iterator<ComponentName> it = mEnabledServices.iterator();
214                    while (it.hasNext()) {
215                        ComponentName comp = it.next();
216                        String compPkg = comp.getPackageName();
217                        for (String pkg : packages) {
218                            if (compPkg.equals(pkg)) {
219                                if (!doit) {
220                                    return true;
221                                }
222                                it.remove();
223                                changed = true;
224                            }
225                        }
226                    }
227                    if (changed) {
228                        updateEnabledAccessibilitySerivcesSettingLocked(mEnabledServices);
229                    }
230                    return false;
231                }
232            }
233
234            @Override
235            public void onReceive(Context context, Intent intent) {
236                if (intent.getAction() == Intent.ACTION_BOOT_COMPLETED) {
237                    synchronized (mLock) {
238                        populateAccessibilityServiceListLocked();
239                        // get accessibility enabled setting on boot
240                        mIsAccessibilityEnabled = Settings.Secure.getInt(
241                                mContext.getContentResolver(),
242                                Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
243
244                        manageServicesLocked();
245
246                        // get touch exploration enabled setting on boot
247                        mIsTouchExplorationEnabled = Settings.Secure.getInt(
248                                mContext.getContentResolver(),
249                                Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0) == 1;
250                        updateInputFilterLocked();
251
252                        sendStateToClientsLocked();
253                    }
254
255                    return;
256                }
257
258                super.onReceive(context, intent);
259            }
260
261            private void updateEnabledAccessibilitySerivcesSettingLocked(
262                    Set<ComponentName> enabledServices) {
263                Iterator<ComponentName> it = enabledServices.iterator();
264                StringBuilder str = new StringBuilder();
265                while (it.hasNext()) {
266                    if (str.length() > 0) {
267                        str.append(':');
268                    }
269                    str.append(it.next().flattenToShortString());
270                }
271                Settings.Secure.putString(mContext.getContentResolver(),
272                        Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
273                        str.toString());
274            }
275        };
276
277        // package changes
278        monitor.register(context, true);
279
280        // boot completed
281        IntentFilter bootFiler = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
282        mContext.registerReceiver(monitor, bootFiler);
283    }
284
285    /**
286     * {@link ContentObserver}s for {@link Settings.Secure#ACCESSIBILITY_ENABLED}
287     * and {@link Settings.Secure#ENABLED_ACCESSIBILITY_SERVICES} settings.
288     */
289    private void registerSettingsContentObservers() {
290        ContentResolver contentResolver = mContext.getContentResolver();
291
292        Uri accessibilityEnabledUri = Settings.Secure.getUriFor(
293                Settings.Secure.ACCESSIBILITY_ENABLED);
294        contentResolver.registerContentObserver(accessibilityEnabledUri, false,
295            new ContentObserver(new Handler()) {
296                @Override
297                public void onChange(boolean selfChange) {
298                    super.onChange(selfChange);
299
300                    synchronized (mLock) {
301                        mIsAccessibilityEnabled = Settings.Secure.getInt(
302                                mContext.getContentResolver(),
303                                Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
304                        if (mIsAccessibilityEnabled) {
305                            manageServicesLocked();
306                        } else {
307                            unbindAllServicesLocked();
308                        }
309                        updateInputFilterLocked();
310                        sendStateToClientsLocked();
311                    }
312                }
313            });
314
315        Uri touchExplorationRequestedUri = Settings.Secure.getUriFor(
316                Settings.Secure.TOUCH_EXPLORATION_ENABLED);
317        contentResolver.registerContentObserver(touchExplorationRequestedUri, false,
318                new ContentObserver(new Handler()) {
319                    @Override
320                    public void onChange(boolean selfChange) {
321                        super.onChange(selfChange);
322
323                        synchronized (mLock) {
324                            mIsTouchExplorationEnabled = Settings.Secure.getInt(
325                                    mContext.getContentResolver(),
326                                    Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0) == 1;
327                            updateInputFilterLocked();
328                            sendStateToClientsLocked();
329                        }
330                    }
331                });
332
333        Uri accessibilityServicesUri =
334            Settings.Secure.getUriFor(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
335        contentResolver.registerContentObserver(accessibilityServicesUri, false,
336            new ContentObserver(new Handler()) {
337                @Override
338                public void onChange(boolean selfChange) {
339                    super.onChange(selfChange);
340
341                    synchronized (mLock) {
342                        manageServicesLocked();
343                    }
344                }
345            });
346    }
347
348    public int addClient(IAccessibilityManagerClient client) throws RemoteException {
349        synchronized (mLock) {
350            final IAccessibilityManagerClient addedClient = client;
351            mClients.add(addedClient);
352            // Clients are registered all the time until their process is
353            // killed, therefore we do not have a corresponding unlinkToDeath.
354            client.asBinder().linkToDeath(new DeathRecipient() {
355                public void binderDied() {
356                    synchronized (mLock) {
357                        mClients.remove(addedClient);
358                    }
359                }
360            }, 0);
361            return getState();
362        }
363    }
364
365    public boolean sendAccessibilityEvent(AccessibilityEvent event) {
366        synchronized (mLock) {
367            if (mSecurityPolicy.canDispatchAccessibilityEvent(event)) {
368                mSecurityPolicy.updateRetrievalAllowingWindowAndEventSourceLocked(event);
369                notifyAccessibilityServicesDelayedLocked(event, false);
370                notifyAccessibilityServicesDelayedLocked(event, true);
371            }
372        }
373        event.recycle();
374        mHandledFeedbackTypes = 0;
375        return (OWN_PROCESS_ID != Binder.getCallingPid());
376    }
377
378    public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList() {
379        synchronized (mLock) {
380            return mInstalledServices;
381        }
382    }
383
384    public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType) {
385        List<AccessibilityServiceInfo> result = mEnabledServicesForFeedbackTempList;
386        result.clear();
387        List<Service> services = mServices;
388        synchronized (mLock) {
389            while (feedbackType != 0) {
390                final int feedbackTypeBit = (1 << Integer.numberOfTrailingZeros(feedbackType));
391                feedbackType &= ~feedbackTypeBit;
392                final int serviceCount = services.size();
393                for (int i = 0; i < serviceCount; i++) {
394                    Service service = services.get(i);
395                    if ((service.mFeedbackType & feedbackTypeBit) != 0) {
396                        result.add(service.mAccessibilityServiceInfo);
397                    }
398                }
399            }
400        }
401        return result;
402    }
403
404    public void interrupt() {
405        synchronized (mLock) {
406            for (int i = 0, count = mServices.size(); i < count; i++) {
407                Service service = mServices.get(i);
408                try {
409                    service.mServiceInterface.onInterrupt();
410                } catch (RemoteException re) {
411                    Slog.e(LOG_TAG, "Error during sending interrupt request to "
412                        + service.mService, re);
413                }
414            }
415        }
416    }
417
418    public void executeMessage(Message message) {
419        switch (message.what) {
420            case DO_SET_SERVICE_INFO: {
421                SomeArgs arguments = ((SomeArgs) message.obj);
422
423                AccessibilityServiceInfo info = (AccessibilityServiceInfo) arguments.arg1;
424                Service service = (Service) arguments.arg2;
425
426                synchronized (mLock) {
427                    // If the XML manifest had data to configure the service its info
428                    // should be already set. In such a case update only the dynamically
429                    // configurable properties.
430                    AccessibilityServiceInfo oldInfo = service.mAccessibilityServiceInfo;
431                    if (oldInfo != null) {
432                        oldInfo.updateDynamicallyConfigurableProperties(info);
433                        service.setDynamicallyConfigurableProperties(oldInfo);
434                    } else {
435                        service.setDynamicallyConfigurableProperties(info);
436                    }
437                }
438            } return;
439            default:
440                Slog.w(LOG_TAG, "Unknown message type: " + message.what);
441        }
442    }
443
444    public int addAccessibilityInteractionConnection(IWindow windowToken,
445            IAccessibilityInteractionConnection connection) throws RemoteException {
446        synchronized (mLock) {
447            final IWindow addedWindowToken = windowToken;
448            final int windowId = sNextWindowId++;
449            connection.asBinder().linkToDeath(new DeathRecipient() {
450                public void binderDied() {
451                    synchronized (mLock) {
452                        removeAccessibilityInteractionConnection(addedWindowToken);
453                    }
454                }
455            }, 0);
456            mWindowIdToWindowTokenMap.put(windowId, addedWindowToken.asBinder());
457            mWindowIdToInteractionConnectionMap.put(windowId, connection);
458            if (DEBUG) {
459                Slog.i(LOG_TAG, "Adding interaction connection to windowId: " + windowId);
460            }
461            return windowId;
462        }
463    }
464
465    public void removeAccessibilityInteractionConnection(IWindow windowToken) {
466        synchronized (mLock) {
467            final int count = mWindowIdToWindowTokenMap.size();
468            for (int i = 0; i < count; i++) {
469                if (mWindowIdToWindowTokenMap.valueAt(i) == windowToken.asBinder()) {
470                    final int windowId = mWindowIdToWindowTokenMap.keyAt(i);
471                    mWindowIdToWindowTokenMap.remove(windowId);
472                    mWindowIdToInteractionConnectionMap.remove(windowId);
473                    if (DEBUG) {
474                        Slog.i(LOG_TAG, "Removing interaction connection to windowId: " + windowId);
475                    }
476                    return;
477                }
478            }
479        }
480    }
481
482    public IAccessibilityServiceConnection registerEventListener(IEventListener listener) {
483        mSecurityPolicy.enforceCallingPermission(Manifest.permission.RETRIEVE_WINDOW_CONTENT,
484                FUNCTION_REGISTER_EVENT_LISTENER);
485        ComponentName componentName = new ComponentName("foo.bar",
486                "AutomationAccessibilityService");
487        synchronized (mLock) {
488            Service oldService = mComponentNameToServiceMap.get(componentName);
489            if (oldService != null) {
490                tryRemoveServiceLocked(oldService);
491            }
492            // This API is intended for testing so enable accessibility to make
493            // sure clients can start poking with the window content.
494            Settings.Secure.putInt(mContext.getContentResolver(),
495                    Settings.Secure.ACCESSIBILITY_ENABLED, 1);
496            // Also disable all accessibility services to avoid interference
497            // with the tests.
498            Settings.Secure.putString(mContext.getContentResolver(),
499                    Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "");
500        }
501        AccessibilityServiceInfo accessibilityServiceInfo = new AccessibilityServiceInfo();
502        accessibilityServiceInfo.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
503        accessibilityServiceInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
504        Service service = new Service(componentName, accessibilityServiceInfo, true);
505        service.onServiceConnected(componentName, listener.asBinder());
506        return service;
507    }
508
509    /**
510     * Populates the cached list of installed {@link AccessibilityService}s.
511     */
512    private void populateAccessibilityServiceListLocked() {
513        mInstalledServices.clear();
514
515        List<ResolveInfo> installedServices = mPackageManager.queryIntentServices(
516                new Intent(AccessibilityService.SERVICE_INTERFACE),
517                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
518
519        for (int i = 0, count = installedServices.size(); i < count; i++) {
520            ResolveInfo resolveInfo = installedServices.get(i);
521            AccessibilityServiceInfo accessibilityServiceInfo;
522            try {
523                accessibilityServiceInfo = new AccessibilityServiceInfo(resolveInfo, mContext);
524                mInstalledServices.add(accessibilityServiceInfo);
525            } catch (XmlPullParserException xppe) {
526                Slog.e(LOG_TAG, "Error while initializing AccessibilityServiceInfo", xppe);
527            } catch (IOException ioe) {
528                Slog.e(LOG_TAG, "Error while initializing AccessibilityServiceInfo", ioe);
529            }
530        }
531    }
532
533    /**
534     * Performs {@link AccessibilityService}s delayed notification. The delay is configurable
535     * and denotes the period after the last event before notifying the service.
536     *
537     * @param event The event.
538     * @param isDefault True to notify default listeners, not default services.
539     */
540    private void notifyAccessibilityServicesDelayedLocked(AccessibilityEvent event,
541            boolean isDefault) {
542        try {
543            for (int i = 0, count = mServices.size(); i < count; i++) {
544                Service service = mServices.get(i);
545
546                if (service.mIsDefault == isDefault) {
547                    if (canDispathEventLocked(service, event, mHandledFeedbackTypes)) {
548                        mHandledFeedbackTypes |= service.mFeedbackType;
549                        notifyAccessibilityServiceDelayedLocked(service, event);
550                    }
551                }
552            }
553        } catch (IndexOutOfBoundsException oobe) {
554            // An out of bounds exception can happen if services are going away
555            // as the for loop is running. If that happens, just bail because
556            // there are no more services to notify.
557            return;
558        }
559    }
560
561    /**
562     * Performs an {@link AccessibilityService} delayed notification. The delay is configurable
563     * and denotes the period after the last event before notifying the service.
564     *
565     * @param service The service.
566     * @param event The event.
567     */
568    private void notifyAccessibilityServiceDelayedLocked(Service service,
569            AccessibilityEvent event) {
570        synchronized (mLock) {
571            final int eventType = event.getEventType();
572            // Make a copy since during dispatch it is possible the event to
573            // be modified to remove its source if the receiving service does
574            // not have permission to access the window content.
575            AccessibilityEvent newEvent = AccessibilityEvent.obtain(event);
576            AccessibilityEvent oldEvent = service.mPendingEvents.get(eventType);
577            service.mPendingEvents.put(eventType, newEvent);
578
579            final int what = eventType | (service.mId << 16);
580            if (oldEvent != null) {
581                mHandler.removeMessages(what);
582                oldEvent.recycle();
583            }
584
585            Message message = mHandler.obtainMessage(what, service);
586            message.arg1 = eventType;
587            mHandler.sendMessageDelayed(message, service.mNotificationTimeout);
588        }
589    }
590
591    /**
592     * Notifies a service for a scheduled event given the event type.
593     *
594     * @param service The service.
595     * @param eventType The type of the event to dispatch.
596     */
597    private void notifyEventListenerLocked(Service service, int eventType) {
598        IEventListener listener = service.mServiceInterface;
599        AccessibilityEvent event = service.mPendingEvents.get(eventType);
600
601        // Check for null here because there is a concurrent scenario in which this
602        // happens: 1) A binder thread calls notifyAccessibilityServiceDelayedLocked
603        // which posts a message for dispatching an event. 2) The message is pulled
604        // from the queue by the handler on the service thread and the latter is
605        // just about to acquire the lock and call this method. 3) Now another binder
606        // thread acquires the lock calling notifyAccessibilityServiceDelayedLocked
607        // so the service thread waits for the lock; 4) The binder thread replaces
608        // the event with a more recent one (assume the same event type) and posts a
609        // dispatch request releasing the lock. 5) Now the main thread is unblocked and
610        // dispatches the event which is removed from the pending ones. 6) And ... now
611        // the service thread handles the last message posted by the last binder call
612        // but the event is already dispatched and hence looking it up in the pending
613        // ones yields null. This check is much simpler that keeping count for each
614        // event type of each service to catch such a scenario since only one message
615        // is processed at a time.
616        if (event == null) {
617            return;
618        }
619
620        service.mPendingEvents.remove(eventType);
621        try {
622            if (mSecurityPolicy.canRetrieveWindowContent(service)) {
623                event.setConnection(service);
624            } else {
625                event.setSource(null);
626            }
627            event.setSealed(true);
628            listener.onAccessibilityEvent(event);
629            event.recycle();
630            if (DEBUG) {
631                Slog.i(LOG_TAG, "Event " + event + " sent to " + listener);
632            }
633        } catch (RemoteException re) {
634            Slog.e(LOG_TAG, "Error during sending " + event + " to " + service.mService, re);
635        }
636    }
637
638    /**
639     * Adds a service.
640     *
641     * @param service The service to add.
642     */
643    private void tryAddServiceLocked(Service service) {
644        try {
645            if (mServices.contains(service) || !service.isConfigured()) {
646                return;
647            }
648            service.linkToOwnDeath();
649            mServices.add(service);
650            mComponentNameToServiceMap.put(service.mComponentName, service);
651            updateInputFilterLocked();
652        } catch (RemoteException e) {
653            /* do nothing */
654        }
655    }
656
657    /**
658     * Removes a service.
659     *
660     * @param service The service.
661     * @return True if the service was removed, false otherwise.
662     */
663    private boolean tryRemoveServiceLocked(Service service) {
664        final boolean removed = mServices.remove(service);
665        if (!removed) {
666            return false;
667        }
668        mComponentNameToServiceMap.remove(service.mComponentName);
669        mHandler.removeMessages(service.mId);
670        service.unlinkToOwnDeath();
671        updateInputFilterLocked();
672        return removed;
673    }
674
675    /**
676     * Determines if given event can be dispatched to a service based on the package of the
677     * event source and already notified services for that event type. Specifically, a
678     * service is notified if it is interested in events from the package and no other service
679     * providing the same feedback type has been notified. Exception are services the
680     * provide generic feedback (feedback type left as a safety net for unforeseen feedback
681     * types) which are always notified.
682     *
683     * @param service The potential receiver.
684     * @param event The event.
685     * @param handledFeedbackTypes The feedback types for which services have been notified.
686     * @return True if the listener should be notified, false otherwise.
687     */
688    private boolean canDispathEventLocked(Service service, AccessibilityEvent event,
689            int handledFeedbackTypes) {
690
691        if (!service.isConfigured()) {
692            return false;
693        }
694
695        int eventType = event.getEventType();
696        if ((service.mEventTypes & eventType) != eventType) {
697            return false;
698        }
699
700        Set<String> packageNames = service.mPackageNames;
701        CharSequence packageName = event.getPackageName();
702
703        if (packageNames.isEmpty() || packageNames.contains(packageName)) {
704            int feedbackType = service.mFeedbackType;
705            if ((handledFeedbackTypes & feedbackType) != feedbackType
706                    || feedbackType == AccessibilityServiceInfo.FEEDBACK_GENERIC) {
707                return true;
708            }
709        }
710
711        return false;
712    }
713
714    /**
715     * Manages services by starting enabled ones and stopping disabled ones.
716     */
717    private void manageServicesLocked() {
718        populateEnabledServicesLocked(mEnabledServices);
719        updateServicesStateLocked(mInstalledServices, mEnabledServices);
720        disableAccessibilityIfNoEnabledServices(mEnabledServices);
721    }
722
723    /**
724     * Unbinds all bound services.
725     */
726    private void unbindAllServicesLocked() {
727        List<Service> services = mServices;
728
729        for (int i = 0, count = services.size(); i < count; i++) {
730            Service service = services.get(i);
731            if (service.unbind()) {
732                i--;
733                count--;
734            }
735        }
736    }
737
738    /**
739     * Populates a list with the {@link ComponentName}s of all enabled
740     * {@link AccessibilityService}s.
741     *
742     * @param enabledServices The list.
743     */
744    private void populateEnabledServicesLocked(Set<ComponentName> enabledServices) {
745        enabledServices.clear();
746
747        String servicesValue = Settings.Secure.getString(mContext.getContentResolver(),
748                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
749
750        if (servicesValue != null) {
751            TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
752            splitter.setString(servicesValue);
753            while (splitter.hasNext()) {
754                String str = splitter.next();
755                if (str == null || str.length() <= 0) {
756                    continue;
757                }
758                ComponentName enabledService = ComponentName.unflattenFromString(str);
759                if (enabledService != null) {
760                    enabledServices.add(enabledService);
761                }
762            }
763        }
764    }
765
766    /**
767     * Updates the state of each service by starting (or keeping running) enabled ones and
768     * stopping the rest.
769     *
770     * @param installedServices All installed {@link AccessibilityService}s.
771     * @param enabledServices The {@link ComponentName}s of the enabled services.
772     */
773    private void updateServicesStateLocked(List<AccessibilityServiceInfo> installedServices,
774            Set<ComponentName> enabledServices) {
775
776        Map<ComponentName, Service> componentNameToServiceMap = mComponentNameToServiceMap;
777        boolean isEnabled = mIsAccessibilityEnabled;
778
779        for (int i = 0, count = installedServices.size(); i < count; i++) {
780            AccessibilityServiceInfo installedService = installedServices.get(i);
781            ComponentName componentName = ComponentName.unflattenFromString(
782                    installedService.getId());
783            Service service = componentNameToServiceMap.get(componentName);
784
785            if (isEnabled) {
786                if (enabledServices.contains(componentName)) {
787                    if (service == null) {
788                        service = new Service(componentName, installedService, false);
789                    }
790                    service.bind();
791                } else {
792                    if (service != null) {
793                        service.unbind();
794                    }
795                }
796            } else {
797                if (service != null) {
798                    service.unbind();
799                }
800            }
801        }
802    }
803
804    /**
805     * Disables accessibility if there are no enabled accessibility services which
806     * to consume the generated accessibility events.
807     *
808     * @param enabledServices The set of enabled services.
809     */
810    private void disableAccessibilityIfNoEnabledServices(Set<ComponentName> enabledServices) {
811        if (enabledServices.isEmpty()) {
812            Settings.Secure.putInt(mContext.getContentResolver(),
813                    Settings.Secure.ACCESSIBILITY_ENABLED, 0);
814        }
815    }
816
817    /**
818     * Sends the state to the clients.
819     */
820    private void sendStateToClientsLocked() {
821        final int state = getState();
822        for (int i = 0, count = mClients.size(); i < count; i++) {
823            try {
824                mClients.get(i).setState(state);
825            } catch (RemoteException re) {
826                mClients.remove(i);
827                count--;
828                i--;
829            }
830        }
831    }
832
833    /**
834     * Gets the current state as a set of flags.
835     *
836     * @return The state.
837     */
838    private int getState() {
839        int state = 0;
840        if (mIsAccessibilityEnabled) {
841            state |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
842        }
843        // Touch exploration relies on enabled accessibility.
844        if (mIsAccessibilityEnabled && mIsTouchExplorationEnabled) {
845            state |= AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED;
846        }
847        return state;
848    }
849
850    /**
851     * Updates the touch exploration state.
852     */
853    private void updateInputFilterLocked() {
854        if (mIsAccessibilityEnabled && mIsTouchExplorationEnabled) {
855            if (!mHasInputFilter) {
856                mHasInputFilter = true;
857                if (mInputFilter == null) {
858                    mInputFilter = new AccessibilityInputFilter(mContext);
859                }
860                mWindowManagerService.setInputFilter(mInputFilter);
861            }
862            return;
863        }
864        if (mHasInputFilter) {
865            mHasInputFilter = false;
866            mWindowManagerService.setInputFilter(null);
867        }
868    }
869
870    /**
871     * This class represents an accessibility service. It stores all per service
872     * data required for the service management, provides API for starting/stopping the
873     * service and is responsible for adding/removing the service in the data structures
874     * for service management. The class also exposes configuration interface that is
875     * passed to the service it represents as soon it is bound. It also serves as the
876     * connection for the service.
877     */
878    class Service extends IAccessibilityServiceConnection.Stub
879            implements ServiceConnection, DeathRecipient {
880        int mId = 0;
881
882        AccessibilityServiceInfo mAccessibilityServiceInfo;
883
884        IBinder mService;
885
886        IEventListener mServiceInterface;
887
888        int mEventTypes;
889
890        int mFeedbackType;
891
892        Set<String> mPackageNames = new HashSet<String>();
893
894        boolean mIsDefault;
895
896        long mNotificationTimeout;
897
898        ComponentName mComponentName;
899
900        Intent mIntent;
901
902        boolean mCanRetrieveScreenContent;
903
904        boolean mIsAutomation;
905
906        final Rect mTempBounds = new Rect();
907
908        // the events pending events to be dispatched to this service
909        final SparseArray<AccessibilityEvent> mPendingEvents =
910            new SparseArray<AccessibilityEvent>();
911
912        public Service(ComponentName componentName,
913                AccessibilityServiceInfo accessibilityServiceInfo, boolean isAutomation) {
914            mId = sIdCounter++;
915            mComponentName = componentName;
916            mAccessibilityServiceInfo = accessibilityServiceInfo;
917            mIsAutomation = isAutomation;
918            if (!isAutomation) {
919                mCanRetrieveScreenContent = accessibilityServiceInfo.getCanRetrieveWindowContent();
920                mIntent = new Intent().setComponent(mComponentName);
921                mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
922                        com.android.internal.R.string.accessibility_binding_label);
923                mIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
924                        mContext, 0, new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS), 0));
925            } else {
926                mCanRetrieveScreenContent = true;
927            }
928            setDynamicallyConfigurableProperties(accessibilityServiceInfo);
929        }
930
931        public void setDynamicallyConfigurableProperties(AccessibilityServiceInfo info) {
932            mEventTypes = info.eventTypes;
933            mFeedbackType = info.feedbackType;
934            String[] packageNames = info.packageNames;
935            if (packageNames != null) {
936                mPackageNames.addAll(Arrays.asList(packageNames));
937            }
938            mNotificationTimeout = info.notificationTimeout;
939            mIsDefault = (info.flags & AccessibilityServiceInfo.DEFAULT) != 0;
940
941            synchronized (mLock) {
942                tryAddServiceLocked(this);
943            }
944        }
945
946        /**
947         * Binds to the accessibility service.
948         *
949         * @return True if binding is successful.
950         */
951        public boolean bind() {
952            if (!mIsAutomation && mService == null) {
953                return mContext.bindService(mIntent, this, Context.BIND_AUTO_CREATE);
954            }
955            return false;
956        }
957
958        /**
959         * Unbinds form the accessibility service and removes it from the data
960         * structures for service management.
961         *
962         * @return True if unbinding is successful.
963         */
964        public boolean unbind() {
965            if (mService != null) {
966                synchronized (mLock) {
967                    tryRemoveServiceLocked(this);
968                }
969                if (!mIsAutomation) {
970                    mContext.unbindService(this);
971                }
972                mService = null;
973                return true;
974            }
975            return false;
976        }
977
978        /**
979         * Returns if the service is configured i.e. at least event types of interest
980         * and feedback type must be set.
981         *
982         * @return True if the service is configured, false otherwise.
983         */
984        public boolean isConfigured() {
985            return (mEventTypes != 0 && mFeedbackType != 0 && mService != null);
986        }
987
988        public void setServiceInfo(AccessibilityServiceInfo info) {
989            mCaller.obtainMessageOO(DO_SET_SERVICE_INFO, info, this).sendToTarget();
990        }
991
992        public void onServiceConnected(ComponentName componentName, IBinder service) {
993            mService = service;
994            mServiceInterface = IEventListener.Stub.asInterface(service);
995            try {
996                mServiceInterface.setConnection(this);
997                synchronized (mLock) {
998                    tryAddServiceLocked(this);
999                }
1000            } catch (RemoteException re) {
1001                Slog.w(LOG_TAG, "Error while setting Controller for service: " + service, re);
1002            }
1003        }
1004
1005        public float findAccessibilityNodeInfoByViewIdInActiveWindow(int viewId,
1006                int interactionId, IAccessibilityInteractionConnectionCallback callback,
1007                long interrogatingTid)
1008                throws RemoteException {
1009            IAccessibilityInteractionConnection connection = null;
1010            synchronized (mLock) {
1011                mSecurityPolicy.enforceCanRetrieveWindowContent(this);
1012                final boolean permissionGranted = mSecurityPolicy.canRetrieveWindowContent(this);
1013                if (!permissionGranted) {
1014                    return 0;
1015                } else {
1016                    connection = getConnectionToRetrievalAllowingWindowLocked();
1017                    if (connection == null) {
1018                        if (DEBUG) {
1019                            Slog.e(LOG_TAG, "No interaction connection to a retrieve "
1020                                    + "allowing window.");
1021                        }
1022                        return 0;
1023                    }
1024                }
1025            }
1026            final int interrogatingPid = Binder.getCallingPid();
1027            final long identityToken = Binder.clearCallingIdentity();
1028            try {
1029                connection.findAccessibilityNodeInfoByViewId(viewId, interactionId, callback,
1030                        interrogatingPid, interrogatingTid);
1031            } catch (RemoteException re) {
1032                if (DEBUG) {
1033                    Slog.e(LOG_TAG, "Error finding node.");
1034                }
1035            } finally {
1036                Binder.restoreCallingIdentity(identityToken);
1037            }
1038            return getCompatibilityScale(mSecurityPolicy.getRetrievalAllowingWindowLocked());
1039        }
1040
1041        public float findAccessibilityNodeInfosByViewTextInActiveWindow(
1042                String text, int interactionId,
1043                IAccessibilityInteractionConnectionCallback callback, long threadId)
1044                throws RemoteException {
1045            return findAccessibilityNodeInfosByViewText(text,
1046                    mSecurityPolicy.mRetrievalAlowingWindowId, View.NO_ID, interactionId, callback,
1047                    threadId);
1048        }
1049
1050        public float findAccessibilityNodeInfosByViewText(String text,
1051                int accessibilityWindowId, int accessibilityViewId, int interactionId,
1052                IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
1053                throws RemoteException {
1054            IAccessibilityInteractionConnection connection = null;
1055            synchronized (mLock) {
1056                mSecurityPolicy.enforceCanRetrieveWindowContent(this);
1057                final boolean permissionGranted =
1058                    mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, accessibilityWindowId);
1059                if (!permissionGranted) {
1060                    return 0;
1061                } else {
1062                    connection = getConnectionToRetrievalAllowingWindowLocked();
1063                    if (connection == null) {
1064                        if (DEBUG) {
1065                            Slog.e(LOG_TAG, "No interaction connection to focused window.");
1066                        }
1067                        return 0;
1068                    }
1069                }
1070            }
1071            final int interrogatingPid = Binder.getCallingPid();
1072            final long identityToken = Binder.clearCallingIdentity();
1073            try {
1074                connection.findAccessibilityNodeInfosByViewText(text, accessibilityViewId,
1075                        interactionId, callback, interrogatingPid, interrogatingTid);
1076            } catch (RemoteException re) {
1077                if (DEBUG) {
1078                    Slog.e(LOG_TAG, "Error finding node.");
1079                }
1080            } finally {
1081                Binder.restoreCallingIdentity(identityToken);
1082            }
1083            return getCompatibilityScale(accessibilityWindowId);
1084        }
1085
1086        public float findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
1087                int accessibilityViewId, int interactionId,
1088                IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
1089                throws RemoteException {
1090            IAccessibilityInteractionConnection connection = null;
1091            synchronized (mLock) {
1092                mSecurityPolicy.enforceCanRetrieveWindowContent(this);
1093                final boolean permissionGranted =
1094                    mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, accessibilityWindowId);
1095                if (!permissionGranted) {
1096                    return 0;
1097                } else {
1098                    connection = mWindowIdToInteractionConnectionMap.get(accessibilityWindowId);
1099                    if (connection == null) {
1100                        if (DEBUG) {
1101                            Slog.e(LOG_TAG, "No interaction connection to window: "
1102                                    + accessibilityWindowId);
1103                        }
1104                        return 0;
1105                    }
1106                }
1107            }
1108            final int interrogatingPid = Binder.getCallingPid();
1109            final long identityToken = Binder.clearCallingIdentity();
1110            try {
1111                connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityViewId,
1112                        interactionId, callback, interrogatingPid, interrogatingTid);
1113            } catch (RemoteException re) {
1114                if (DEBUG) {
1115                    Slog.e(LOG_TAG, "Error requesting node with accessibilityViewId: "
1116                            + accessibilityViewId);
1117                }
1118            } finally {
1119                Binder.restoreCallingIdentity(identityToken);
1120            }
1121            return getCompatibilityScale(accessibilityWindowId);
1122        }
1123
1124        public boolean performAccessibilityAction(int accessibilityWindowId,
1125                int accessibilityViewId, int action, int interactionId,
1126                IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) {
1127            IAccessibilityInteractionConnection connection = null;
1128            synchronized (mLock) {
1129                final boolean permissionGranted = mSecurityPolicy.canPerformActionLocked(this,
1130                        accessibilityWindowId, action);
1131                if (!permissionGranted) {
1132                    return false;
1133                } else {
1134                    connection = mWindowIdToInteractionConnectionMap.get(accessibilityWindowId);
1135                    if (connection == null) {
1136                        if (DEBUG) {
1137                            Slog.e(LOG_TAG, "No interaction connection to window: "
1138                                    + accessibilityWindowId);
1139                        }
1140                        return false;
1141                    }
1142                }
1143            }
1144            final int interrogatingPid = Binder.getCallingPid();
1145            final long identityToken = Binder.clearCallingIdentity();
1146            try {
1147                connection.performAccessibilityAction(accessibilityViewId, action, interactionId,
1148                        callback, interrogatingPid, interrogatingTid);
1149            } catch (RemoteException re) {
1150                if (DEBUG) {
1151                    Slog.e(LOG_TAG, "Error requesting node with accessibilityViewId: "
1152                            + accessibilityViewId);
1153                }
1154            } finally {
1155                Binder.restoreCallingIdentity(identityToken);
1156            }
1157            return true;
1158        }
1159
1160        public void onServiceDisconnected(ComponentName componentName) {
1161            /* do nothing - #binderDied takes care */
1162        }
1163
1164        public void linkToOwnDeath() throws RemoteException {
1165            mService.linkToDeath(this, 0);
1166        }
1167
1168        public void unlinkToOwnDeath() {
1169            mService.unlinkToDeath(this, 0);
1170        }
1171
1172        public void binderDied() {
1173            synchronized (mLock) {
1174                tryRemoveServiceLocked(this);
1175            }
1176        }
1177
1178        private IAccessibilityInteractionConnection getConnectionToRetrievalAllowingWindowLocked() {
1179            final int windowId = mSecurityPolicy.getRetrievalAllowingWindowLocked();
1180            if (DEBUG) {
1181                Slog.i(LOG_TAG, "Trying to get interaction connection to windowId: " + windowId);
1182            }
1183            return mWindowIdToInteractionConnectionMap.get(windowId);
1184        }
1185
1186        private float getCompatibilityScale(int windowId) {
1187            IBinder windowToken = mWindowIdToWindowTokenMap.get(windowId);
1188            return mWindowManagerService.getWindowCompatibilityScale(windowToken);
1189        }
1190    }
1191
1192    final class SecurityPolicy {
1193        private static final int VALID_ACTIONS = AccessibilityNodeInfo.ACTION_FOCUS
1194            | AccessibilityNodeInfo.ACTION_CLEAR_FOCUS | AccessibilityNodeInfo.ACTION_SELECT
1195            | AccessibilityNodeInfo.ACTION_CLEAR_SELECTION;
1196
1197        private static final int RETRIEVAL_ALLOWING_EVENT_TYPES =
1198            AccessibilityEvent.TYPE_VIEW_CLICKED | AccessibilityEvent.TYPE_VIEW_FOCUSED
1199            | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT
1200            | AccessibilityEvent.TYPE_VIEW_LONG_CLICKED | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
1201            | AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED | AccessibilityEvent.TYPE_VIEW_SELECTED
1202            | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
1203            | AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED
1204            | AccessibilityEvent.TYPE_VIEW_SCROLLED;
1205
1206        private static final int RETRIEVAL_ALLOWING_WINDOW_CHANGE_EVENT_TYPES =
1207            AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER
1208            | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT;
1209
1210        private int mRetrievalAlowingWindowId;
1211
1212        private boolean canDispatchAccessibilityEvent(AccessibilityEvent event) {
1213            // Send window changed event only for the retrieval allowing window.
1214            return (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
1215                    || event.getWindowId() == mRetrievalAlowingWindowId);
1216        }
1217
1218        public void updateRetrievalAllowingWindowAndEventSourceLocked(AccessibilityEvent event) {
1219            final int windowId = event.getWindowId();
1220            final int eventType = event.getEventType();
1221            if ((eventType & RETRIEVAL_ALLOWING_WINDOW_CHANGE_EVENT_TYPES) != 0) {
1222                mRetrievalAlowingWindowId = windowId;
1223            }
1224            if ((eventType & RETRIEVAL_ALLOWING_EVENT_TYPES) == 0) {
1225                event.setSource(null);
1226            }
1227        }
1228
1229        public int getRetrievalAllowingWindowLocked() {
1230            return mRetrievalAlowingWindowId;
1231        }
1232
1233        public boolean canGetAccessibilityNodeInfoLocked(Service service, int windowId) {
1234            return canRetrieveWindowContent(service) && isRetrievalAllowingWindow(windowId);
1235        }
1236
1237        public boolean canPerformActionLocked(Service service, int windowId, int action) {
1238            return canRetrieveWindowContent(service)
1239                && isRetrievalAllowingWindow(windowId)
1240                && isActionPermitted(action);
1241        }
1242
1243        public boolean canRetrieveWindowContent(Service service) {
1244            return service.mCanRetrieveScreenContent;
1245        }
1246
1247        public void enforceCanRetrieveWindowContent(Service service) throws RemoteException {
1248            // This happens due to incorrect registration so make it apparent.
1249            if (!canRetrieveWindowContent(service)) {
1250                Slog.e(LOG_TAG, "Accessibility serivce " + service.mComponentName + " does not " +
1251                        "declare android:canRetrieveWindowContent.");
1252                throw new RemoteException();
1253            }
1254        }
1255
1256        private boolean isRetrievalAllowingWindow(int windowId) {
1257            return (mRetrievalAlowingWindowId == windowId);
1258        }
1259
1260        private boolean isActionPermitted(int action) {
1261             return (VALID_ACTIONS & action) != 0;
1262        }
1263
1264        private void enforceCallingPermission(String permission, String function) {
1265            if (OWN_PROCESS_ID == Binder.getCallingPid()) {
1266                return;
1267            }
1268            final int permissionStatus = mContext.checkCallingPermission(permission);
1269            if (permissionStatus != PackageManager.PERMISSION_GRANTED) {
1270                throw new SecurityException("You do not have " + permission
1271                        + " required to call " + function);
1272            }
1273        }
1274    }
1275}
1276