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