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<AccessibilityConnectionWrapper> mWindowIdToInteractionConnectionWrapperMap =
119        new SparseArray<AccessibilityConnectionWrapper>();
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                        handleAccessibilityEnabledSettingChangedLocked();
302                    }
303                }
304            });
305
306        Uri touchExplorationRequestedUri = Settings.Secure.getUriFor(
307                Settings.Secure.TOUCH_EXPLORATION_ENABLED);
308        contentResolver.registerContentObserver(touchExplorationRequestedUri, false,
309                new ContentObserver(new Handler()) {
310                    @Override
311                    public void onChange(boolean selfChange) {
312                        super.onChange(selfChange);
313
314                        synchronized (mLock) {
315                            mIsTouchExplorationEnabled = Settings.Secure.getInt(
316                                    mContext.getContentResolver(),
317                                    Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0) == 1;
318                            updateInputFilterLocked();
319                            sendStateToClientsLocked();
320                        }
321                    }
322                });
323
324        Uri accessibilityServicesUri =
325            Settings.Secure.getUriFor(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
326        contentResolver.registerContentObserver(accessibilityServicesUri, false,
327            new ContentObserver(new Handler()) {
328                @Override
329                public void onChange(boolean selfChange) {
330                    super.onChange(selfChange);
331
332                    synchronized (mLock) {
333                        manageServicesLocked();
334                    }
335                }
336            });
337    }
338
339    public int addClient(IAccessibilityManagerClient client) throws RemoteException {
340        synchronized (mLock) {
341            final IAccessibilityManagerClient addedClient = client;
342            mClients.add(addedClient);
343            // Clients are registered all the time until their process is
344            // killed, therefore we do not have a corresponding unlinkToDeath.
345            client.asBinder().linkToDeath(new DeathRecipient() {
346                public void binderDied() {
347                    synchronized (mLock) {
348                        addedClient.asBinder().unlinkToDeath(this, 0);
349                        mClients.remove(addedClient);
350                    }
351                }
352            }, 0);
353            return getState();
354        }
355    }
356
357    public boolean sendAccessibilityEvent(AccessibilityEvent event) {
358        synchronized (mLock) {
359            if (mSecurityPolicy.canDispatchAccessibilityEvent(event)) {
360                mSecurityPolicy.updateRetrievalAllowingWindowAndEventSourceLocked(event);
361                notifyAccessibilityServicesDelayedLocked(event, false);
362                notifyAccessibilityServicesDelayedLocked(event, true);
363            }
364        }
365        event.recycle();
366        mHandledFeedbackTypes = 0;
367        return (OWN_PROCESS_ID != Binder.getCallingPid());
368    }
369
370    public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList() {
371        synchronized (mLock) {
372            return mInstalledServices;
373        }
374    }
375
376    public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType) {
377        List<AccessibilityServiceInfo> result = mEnabledServicesForFeedbackTempList;
378        result.clear();
379        List<Service> services = mServices;
380        synchronized (mLock) {
381            while (feedbackType != 0) {
382                final int feedbackTypeBit = (1 << Integer.numberOfTrailingZeros(feedbackType));
383                feedbackType &= ~feedbackTypeBit;
384                final int serviceCount = services.size();
385                for (int i = 0; i < serviceCount; i++) {
386                    Service service = services.get(i);
387                    if ((service.mFeedbackType & feedbackTypeBit) != 0) {
388                        result.add(service.mAccessibilityServiceInfo);
389                    }
390                }
391            }
392        }
393        return result;
394    }
395
396    public void interrupt() {
397        synchronized (mLock) {
398            for (int i = 0, count = mServices.size(); i < count; i++) {
399                Service service = mServices.get(i);
400                try {
401                    service.mServiceInterface.onInterrupt();
402                } catch (RemoteException re) {
403                    Slog.e(LOG_TAG, "Error during sending interrupt request to "
404                        + service.mService, re);
405                }
406            }
407        }
408    }
409
410    public void executeMessage(Message message) {
411        switch (message.what) {
412            case DO_SET_SERVICE_INFO: {
413                SomeArgs arguments = ((SomeArgs) message.obj);
414
415                AccessibilityServiceInfo info = (AccessibilityServiceInfo) arguments.arg1;
416                Service service = (Service) arguments.arg2;
417
418                synchronized (mLock) {
419                    // If the XML manifest had data to configure the service its info
420                    // should be already set. In such a case update only the dynamically
421                    // configurable properties.
422                    AccessibilityServiceInfo oldInfo = service.mAccessibilityServiceInfo;
423                    if (oldInfo != null) {
424                        oldInfo.updateDynamicallyConfigurableProperties(info);
425                        service.setDynamicallyConfigurableProperties(oldInfo);
426                    } else {
427                        service.setDynamicallyConfigurableProperties(info);
428                    }
429                }
430            } return;
431            default:
432                Slog.w(LOG_TAG, "Unknown message type: " + message.what);
433        }
434    }
435
436    public int addAccessibilityInteractionConnection(IWindow windowToken,
437            IAccessibilityInteractionConnection connection) throws RemoteException {
438        synchronized (mLock) {
439            final IWindow addedWindowToken = windowToken;
440            final IAccessibilityInteractionConnection addedConnection = connection;
441            final int windowId = sNextWindowId++;
442            AccessibilityConnectionWrapper wrapper = new AccessibilityConnectionWrapper(windowId,
443                    connection);
444            wrapper.linkToDeath();
445            mWindowIdToWindowTokenMap.put(windowId, addedWindowToken.asBinder());
446            mWindowIdToInteractionConnectionWrapperMap.put(windowId, wrapper);
447            if (DEBUG) {
448                Slog.i(LOG_TAG, "Adding interaction connection to windowId: " + windowId);
449            }
450            return windowId;
451        }
452    }
453
454    public void removeAccessibilityInteractionConnection(IWindow windowToken) {
455        synchronized (mLock) {
456            final int count = mWindowIdToWindowTokenMap.size();
457            for (int i = 0; i < count; i++) {
458                if (mWindowIdToWindowTokenMap.valueAt(i) == windowToken.asBinder()) {
459                    final int windowId = mWindowIdToWindowTokenMap.keyAt(i);
460                    AccessibilityConnectionWrapper wrapper =
461                        mWindowIdToInteractionConnectionWrapperMap.get(windowId);
462                    wrapper.unlinkToDeath();
463                    removeAccessibilityInteractionConnectionLocked(windowId);
464                    return;
465                }
466            }
467        }
468    }
469
470    public void registerEventListener(IEventListener listener) {
471        mSecurityPolicy.enforceCallingPermission(Manifest.permission.RETRIEVE_WINDOW_CONTENT,
472                FUNCTION_REGISTER_EVENT_LISTENER);
473        ComponentName componentName = new ComponentName("foo.bar",
474                "AutomationAccessibilityService");
475        synchronized (mLock) {
476            // If an automation services is connected to the system all services are stopped
477            // so the automation one is the only one running. Settings are not changed so when
478            // the automation service goes away the state is restored from the settings.
479
480            // Disable all services.
481            final int runningServiceCount = mServices.size();
482            for (int i = 0; i < runningServiceCount; i++) {
483                Service runningService = mServices.get(i);
484                runningService.unbind();
485            }
486            // If necessary enable accessibility and announce that.
487            if (!mIsAccessibilityEnabled) {
488                mIsAccessibilityEnabled = true;
489                sendStateToClientsLocked();
490            }
491        }
492        // Hook the automation service up.
493        AccessibilityServiceInfo accessibilityServiceInfo = new AccessibilityServiceInfo();
494        accessibilityServiceInfo.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
495        accessibilityServiceInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
496        Service service = new Service(componentName, accessibilityServiceInfo, true);
497        service.onServiceConnected(componentName, listener.asBinder());
498    }
499
500    /**
501     * Removes an AccessibilityInteractionConnection.
502     *
503     * @param windowId The id of the window to which the connection is targeted.
504     */
505    private void removeAccessibilityInteractionConnectionLocked(int windowId) {
506        mWindowIdToWindowTokenMap.remove(windowId);
507        mWindowIdToInteractionConnectionWrapperMap.remove(windowId);
508        if (DEBUG) {
509            Slog.i(LOG_TAG, "Removing interaction connection to windowId: " + windowId);
510        }
511    }
512
513    /**
514     * Populates the cached list of installed {@link AccessibilityService}s.
515     */
516    private void populateAccessibilityServiceListLocked() {
517        mInstalledServices.clear();
518
519        List<ResolveInfo> installedServices = mPackageManager.queryIntentServices(
520                new Intent(AccessibilityService.SERVICE_INTERFACE),
521                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
522
523        for (int i = 0, count = installedServices.size(); i < count; i++) {
524            ResolveInfo resolveInfo = installedServices.get(i);
525            AccessibilityServiceInfo accessibilityServiceInfo;
526            try {
527                accessibilityServiceInfo = new AccessibilityServiceInfo(resolveInfo, mContext);
528                mInstalledServices.add(accessibilityServiceInfo);
529            } catch (XmlPullParserException xppe) {
530                Slog.e(LOG_TAG, "Error while initializing AccessibilityServiceInfo", xppe);
531            } catch (IOException ioe) {
532                Slog.e(LOG_TAG, "Error while initializing AccessibilityServiceInfo", ioe);
533            }
534        }
535    }
536
537    /**
538     * Performs {@link AccessibilityService}s delayed notification. The delay is configurable
539     * and denotes the period after the last event before notifying the service.
540     *
541     * @param event The event.
542     * @param isDefault True to notify default listeners, not default services.
543     */
544    private void notifyAccessibilityServicesDelayedLocked(AccessibilityEvent event,
545            boolean isDefault) {
546        try {
547            for (int i = 0, count = mServices.size(); i < count; i++) {
548                Service service = mServices.get(i);
549
550                if (service.mIsDefault == isDefault) {
551                    if (canDispathEventLocked(service, event, mHandledFeedbackTypes)) {
552                        mHandledFeedbackTypes |= service.mFeedbackType;
553                        notifyAccessibilityServiceDelayedLocked(service, event);
554                    }
555                }
556            }
557        } catch (IndexOutOfBoundsException oobe) {
558            // An out of bounds exception can happen if services are going away
559            // as the for loop is running. If that happens, just bail because
560            // there are no more services to notify.
561            return;
562        }
563    }
564
565    /**
566     * Performs an {@link AccessibilityService} delayed notification. The delay is configurable
567     * and denotes the period after the last event before notifying the service.
568     *
569     * @param service The service.
570     * @param event The event.
571     */
572    private void notifyAccessibilityServiceDelayedLocked(Service service,
573            AccessibilityEvent event) {
574        synchronized (mLock) {
575            final int eventType = event.getEventType();
576            // Make a copy since during dispatch it is possible the event to
577            // be modified to remove its source if the receiving service does
578            // not have permission to access the window content.
579            AccessibilityEvent newEvent = AccessibilityEvent.obtain(event);
580            AccessibilityEvent oldEvent = service.mPendingEvents.get(eventType);
581            service.mPendingEvents.put(eventType, newEvent);
582
583            final int what = eventType | (service.mId << 16);
584            if (oldEvent != null) {
585                mHandler.removeMessages(what);
586                oldEvent.recycle();
587            }
588
589            Message message = mHandler.obtainMessage(what, service);
590            message.arg1 = eventType;
591            mHandler.sendMessageDelayed(message, service.mNotificationTimeout);
592        }
593    }
594
595    /**
596     * Notifies a service for a scheduled event given the event type.
597     *
598     * @param service The service.
599     * @param eventType The type of the event to dispatch.
600     */
601    private void notifyEventListenerLocked(Service service, int eventType) {
602        IEventListener listener = service.mServiceInterface;
603
604        // If the service died/was disabled while the message for dispatching
605        // the accessibility event was propagating the listener may be null.
606        if (listener == null) {
607            return;
608        }
609
610        AccessibilityEvent event = service.mPendingEvents.get(eventType);
611
612        // Check for null here because there is a concurrent scenario in which this
613        // happens: 1) A binder thread calls notifyAccessibilityServiceDelayedLocked
614        // which posts a message for dispatching an event. 2) The message is pulled
615        // from the queue by the handler on the service thread and the latter is
616        // just about to acquire the lock and call this method. 3) Now another binder
617        // thread acquires the lock calling notifyAccessibilityServiceDelayedLocked
618        // so the service thread waits for the lock; 4) The binder thread replaces
619        // the event with a more recent one (assume the same event type) and posts a
620        // dispatch request releasing the lock. 5) Now the main thread is unblocked and
621        // dispatches the event which is removed from the pending ones. 6) And ... now
622        // the service thread handles the last message posted by the last binder call
623        // but the event is already dispatched and hence looking it up in the pending
624        // ones yields null. This check is much simpler that keeping count for each
625        // event type of each service to catch such a scenario since only one message
626        // is processed at a time.
627        if (event == null) {
628            return;
629        }
630
631        service.mPendingEvents.remove(eventType);
632        try {
633            if (mSecurityPolicy.canRetrieveWindowContent(service)) {
634                event.setConnectionId(service.mId);
635            } else {
636                event.setSource(null);
637            }
638            event.setSealed(true);
639            listener.onAccessibilityEvent(event);
640            event.recycle();
641            if (DEBUG) {
642                Slog.i(LOG_TAG, "Event " + event + " sent to " + listener);
643            }
644        } catch (RemoteException re) {
645            Slog.e(LOG_TAG, "Error during sending " + event + " to " + service.mService, re);
646        }
647    }
648
649    /**
650     * Adds a service.
651     *
652     * @param service The service to add.
653     */
654    private void tryAddServiceLocked(Service service) {
655        try {
656            if (mServices.contains(service) || !service.isConfigured()) {
657                return;
658            }
659            service.linkToOwnDeath();
660            mServices.add(service);
661            mComponentNameToServiceMap.put(service.mComponentName, service);
662            updateInputFilterLocked();
663        } catch (RemoteException e) {
664            /* do nothing */
665        }
666    }
667
668    /**
669     * Removes a service.
670     *
671     * @param service The service.
672     * @return True if the service was removed, false otherwise.
673     */
674    private boolean tryRemoveServiceLocked(Service service) {
675        final boolean removed = mServices.remove(service);
676        if (!removed) {
677            return false;
678        }
679        mComponentNameToServiceMap.remove(service.mComponentName);
680        mHandler.removeMessages(service.mId);
681        service.unlinkToOwnDeath();
682        service.dispose();
683        updateInputFilterLocked();
684        return removed;
685    }
686
687    /**
688     * Determines if given event can be dispatched to a service based on the package of the
689     * event source and already notified services for that event type. Specifically, a
690     * service is notified if it is interested in events from the package and no other service
691     * providing the same feedback type has been notified. Exception are services the
692     * provide generic feedback (feedback type left as a safety net for unforeseen feedback
693     * types) which are always notified.
694     *
695     * @param service The potential receiver.
696     * @param event The event.
697     * @param handledFeedbackTypes The feedback types for which services have been notified.
698     * @return True if the listener should be notified, false otherwise.
699     */
700    private boolean canDispathEventLocked(Service service, AccessibilityEvent event,
701            int handledFeedbackTypes) {
702
703        if (!service.isConfigured()) {
704            return false;
705        }
706
707        int eventType = event.getEventType();
708        if ((service.mEventTypes & eventType) != eventType) {
709            return false;
710        }
711
712        Set<String> packageNames = service.mPackageNames;
713        CharSequence packageName = event.getPackageName();
714
715        if (packageNames.isEmpty() || packageNames.contains(packageName)) {
716            int feedbackType = service.mFeedbackType;
717            if ((handledFeedbackTypes & feedbackType) != feedbackType
718                    || feedbackType == AccessibilityServiceInfo.FEEDBACK_GENERIC) {
719                return true;
720            }
721        }
722
723        return false;
724    }
725
726    /**
727     * Manages services by starting enabled ones and stopping disabled ones.
728     */
729    private void manageServicesLocked() {
730        unbindAutomationService();
731        populateEnabledServicesLocked(mEnabledServices);
732        final int enabledInstalledServicesCount = updateServicesStateLocked(mInstalledServices,
733                mEnabledServices);
734        // No enabled installed services => disable accessibility to avoid
735        // sending accessibility events with no recipient across processes.
736        if (mIsAccessibilityEnabled && enabledInstalledServicesCount == 0) {
737            Settings.Secure.putInt(mContext.getContentResolver(),
738                    Settings.Secure.ACCESSIBILITY_ENABLED, 0);
739        }
740    }
741
742    /**
743     * Unbinds all bound services.
744     */
745    private void unbindAllServicesLocked() {
746        List<Service> services = mServices;
747
748        for (int i = 0, count = services.size(); i < count; i++) {
749            Service service = services.get(i);
750            if (service.unbind()) {
751                i--;
752                count--;
753            }
754        }
755    }
756
757    /**
758     * Unbinds the automation service if such is running.
759     */
760    private void unbindAutomationService() {
761        List<Service> runningServices = mServices;
762        int runningServiceCount = mServices.size();
763        for (int i = 0; i < runningServiceCount; i++) {
764            Service service = runningServices.get(i);
765            if (service.mIsAutomation) {
766                 service.unbind();
767                 return;
768            }
769        }
770    }
771
772    /**
773     * Populates a list with the {@link ComponentName}s of all enabled
774     * {@link AccessibilityService}s.
775     *
776     * @param enabledServices The list.
777     */
778    private void populateEnabledServicesLocked(Set<ComponentName> enabledServices) {
779        enabledServices.clear();
780
781        String servicesValue = Settings.Secure.getString(mContext.getContentResolver(),
782                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
783
784        if (servicesValue != null) {
785            TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
786            splitter.setString(servicesValue);
787            while (splitter.hasNext()) {
788                String str = splitter.next();
789                if (str == null || str.length() <= 0) {
790                    continue;
791                }
792                ComponentName enabledService = ComponentName.unflattenFromString(str);
793                if (enabledService != null) {
794                    enabledServices.add(enabledService);
795                }
796            }
797        }
798    }
799
800    /**
801     * Updates the state of each service by starting (or keeping running) enabled ones and
802     * stopping the rest.
803     *
804     * @param installedServices All installed {@link AccessibilityService}s.
805     * @param enabledServices The {@link ComponentName}s of the enabled services.
806     * @return The number of enabled installed services.
807     */
808    private int updateServicesStateLocked(List<AccessibilityServiceInfo> installedServices,
809            Set<ComponentName> enabledServices) {
810
811        Map<ComponentName, Service> componentNameToServiceMap = mComponentNameToServiceMap;
812        boolean isEnabled = mIsAccessibilityEnabled;
813
814        int enabledInstalledServices = 0;
815        for (int i = 0, count = installedServices.size(); i < count; i++) {
816            AccessibilityServiceInfo installedService = installedServices.get(i);
817            ComponentName componentName = ComponentName.unflattenFromString(
818                    installedService.getId());
819            Service service = componentNameToServiceMap.get(componentName);
820
821            if (isEnabled) {
822                if (enabledServices.contains(componentName)) {
823                    if (service == null) {
824                        service = new Service(componentName, installedService, false);
825                    }
826                    service.bind();
827                    enabledInstalledServices++;
828                } else {
829                    if (service != null) {
830                        service.unbind();
831                    }
832                }
833            } else {
834                if (service != null) {
835                    service.unbind();
836                }
837            }
838        }
839
840        return enabledInstalledServices;
841    }
842
843    /**
844     * Sends the state to the clients.
845     */
846    private void sendStateToClientsLocked() {
847        final int state = getState();
848        for (int i = 0, count = mClients.size(); i < count; i++) {
849            try {
850                mClients.get(i).setState(state);
851            } catch (RemoteException re) {
852                mClients.remove(i);
853                count--;
854                i--;
855            }
856        }
857    }
858
859    /**
860     * Gets the current state as a set of flags.
861     *
862     * @return The state.
863     */
864    private int getState() {
865        int state = 0;
866        if (mIsAccessibilityEnabled) {
867            state |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
868        }
869        // Touch exploration relies on enabled accessibility.
870        if (mIsAccessibilityEnabled && mIsTouchExplorationEnabled) {
871            state |= AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED;
872        }
873        return state;
874    }
875
876    /**
877     * Updates the touch exploration state.
878     */
879    private void updateInputFilterLocked() {
880        if (mIsAccessibilityEnabled && mIsTouchExplorationEnabled) {
881            if (!mHasInputFilter) {
882                mHasInputFilter = true;
883                if (mInputFilter == null) {
884                    mInputFilter = new AccessibilityInputFilter(mContext);
885                }
886                mWindowManagerService.setInputFilter(mInputFilter);
887            }
888            return;
889        }
890        if (mHasInputFilter) {
891            mHasInputFilter = false;
892            mWindowManagerService.setInputFilter(null);
893        }
894    }
895
896    /**
897     * Updated the state based on the accessibility enabled setting.
898     */
899    private void handleAccessibilityEnabledSettingChangedLocked() {
900        mIsAccessibilityEnabled = Settings.Secure.getInt(
901                mContext.getContentResolver(),
902                Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
903        if (mIsAccessibilityEnabled) {
904            manageServicesLocked();
905        } else {
906            unbindAllServicesLocked();
907        }
908        updateInputFilterLocked();
909        sendStateToClientsLocked();
910    }
911
912    private class AccessibilityConnectionWrapper implements DeathRecipient {
913        private final int mWindowId;
914        private final IAccessibilityInteractionConnection mConnection;
915
916        public AccessibilityConnectionWrapper(int windowId,
917                IAccessibilityInteractionConnection connection) {
918            mWindowId = windowId;
919            mConnection = connection;
920        }
921
922        public void linkToDeath() throws RemoteException {
923            mConnection.asBinder().linkToDeath(this, 0);
924        }
925
926        public void unlinkToDeath() {
927            mConnection.asBinder().unlinkToDeath(this, 0);
928        }
929
930        @Override
931        public void binderDied() {
932            unlinkToDeath();
933            synchronized (mLock) {
934                removeAccessibilityInteractionConnectionLocked(mWindowId);
935            }
936        }
937    }
938
939    /**
940     * This class represents an accessibility service. It stores all per service
941     * data required for the service management, provides API for starting/stopping the
942     * service and is responsible for adding/removing the service in the data structures
943     * for service management. The class also exposes configuration interface that is
944     * passed to the service it represents as soon it is bound. It also serves as the
945     * connection for the service.
946     */
947    class Service extends IAccessibilityServiceConnection.Stub
948            implements ServiceConnection, DeathRecipient {
949        int mId = 0;
950
951        AccessibilityServiceInfo mAccessibilityServiceInfo;
952
953        IBinder mService;
954
955        IEventListener mServiceInterface;
956
957        int mEventTypes;
958
959        int mFeedbackType;
960
961        Set<String> mPackageNames = new HashSet<String>();
962
963        boolean mIsDefault;
964
965        long mNotificationTimeout;
966
967        ComponentName mComponentName;
968
969        Intent mIntent;
970
971        boolean mCanRetrieveScreenContent;
972
973        boolean mIsAutomation;
974
975        final Rect mTempBounds = new Rect();
976
977        // the events pending events to be dispatched to this service
978        final SparseArray<AccessibilityEvent> mPendingEvents =
979            new SparseArray<AccessibilityEvent>();
980
981        public Service(ComponentName componentName,
982                AccessibilityServiceInfo accessibilityServiceInfo, boolean isAutomation) {
983            mId = sIdCounter++;
984            mComponentName = componentName;
985            mAccessibilityServiceInfo = accessibilityServiceInfo;
986            mIsAutomation = isAutomation;
987            if (!isAutomation) {
988                mCanRetrieveScreenContent = accessibilityServiceInfo.getCanRetrieveWindowContent();
989                mIntent = new Intent().setComponent(mComponentName);
990                mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
991                        com.android.internal.R.string.accessibility_binding_label);
992                mIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
993                        mContext, 0, new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS), 0));
994            } else {
995                mCanRetrieveScreenContent = true;
996            }
997            setDynamicallyConfigurableProperties(accessibilityServiceInfo);
998        }
999
1000        public void setDynamicallyConfigurableProperties(AccessibilityServiceInfo info) {
1001            mEventTypes = info.eventTypes;
1002            mFeedbackType = info.feedbackType;
1003            String[] packageNames = info.packageNames;
1004            if (packageNames != null) {
1005                mPackageNames.addAll(Arrays.asList(packageNames));
1006            }
1007            mNotificationTimeout = info.notificationTimeout;
1008            mIsDefault = (info.flags & AccessibilityServiceInfo.DEFAULT) != 0;
1009
1010            synchronized (mLock) {
1011                tryAddServiceLocked(this);
1012            }
1013        }
1014
1015        /**
1016         * Binds to the accessibility service.
1017         *
1018         * @return True if binding is successful.
1019         */
1020        public boolean bind() {
1021            if (!mIsAutomation && mService == null) {
1022                return mContext.bindService(mIntent, this, Context.BIND_AUTO_CREATE);
1023            }
1024            return false;
1025        }
1026
1027        /**
1028         * Unbinds form the accessibility service and removes it from the data
1029         * structures for service management.
1030         *
1031         * @return True if unbinding is successful.
1032         */
1033        public boolean unbind() {
1034            if (mService != null) {
1035                synchronized (mLock) {
1036                    tryRemoveServiceLocked(this);
1037                }
1038                if (!mIsAutomation) {
1039                    mContext.unbindService(this);
1040                }
1041                return true;
1042            }
1043            return false;
1044        }
1045
1046        /**
1047         * Returns if the service is configured i.e. at least event types of interest
1048         * and feedback type must be set.
1049         *
1050         * @return True if the service is configured, false otherwise.
1051         */
1052        public boolean isConfigured() {
1053            return (mEventTypes != 0 && mFeedbackType != 0 && mService != null);
1054        }
1055
1056        public void setServiceInfo(AccessibilityServiceInfo info) {
1057            mCaller.obtainMessageOO(DO_SET_SERVICE_INFO, info, this).sendToTarget();
1058        }
1059
1060        public void onServiceConnected(ComponentName componentName, IBinder service) {
1061            mService = service;
1062            mServiceInterface = IEventListener.Stub.asInterface(service);
1063            try {
1064                mServiceInterface.setConnection(this, mId);
1065                synchronized (mLock) {
1066                    tryAddServiceLocked(this);
1067                }
1068            } catch (RemoteException re) {
1069                Slog.w(LOG_TAG, "Error while setting Controller for service: " + service, re);
1070            }
1071        }
1072
1073        public float findAccessibilityNodeInfoByViewIdInActiveWindow(int viewId,
1074                int interactionId, IAccessibilityInteractionConnectionCallback callback,
1075                long interrogatingTid)
1076                throws RemoteException {
1077            IAccessibilityInteractionConnection connection = null;
1078            synchronized (mLock) {
1079                mSecurityPolicy.enforceCanRetrieveWindowContent(this);
1080                final boolean permissionGranted = mSecurityPolicy.canRetrieveWindowContent(this);
1081                if (!permissionGranted) {
1082                    return 0;
1083                } else {
1084                    connection = getConnectionToRetrievalAllowingWindowLocked();
1085                    if (connection == null) {
1086                        if (DEBUG) {
1087                            Slog.e(LOG_TAG, "No interaction connection to a retrieve "
1088                                    + "allowing window.");
1089                        }
1090                        return 0;
1091                    }
1092                }
1093            }
1094            final int interrogatingPid = Binder.getCallingPid();
1095            final long identityToken = Binder.clearCallingIdentity();
1096            try {
1097                connection.findAccessibilityNodeInfoByViewId(viewId, interactionId, callback,
1098                        interrogatingPid, interrogatingTid);
1099            } catch (RemoteException re) {
1100                if (DEBUG) {
1101                    Slog.e(LOG_TAG, "Error finding node.");
1102                }
1103            } finally {
1104                Binder.restoreCallingIdentity(identityToken);
1105            }
1106            return getCompatibilityScale(mSecurityPolicy.getRetrievalAllowingWindowLocked());
1107        }
1108
1109        public float findAccessibilityNodeInfosByViewTextInActiveWindow(
1110                String text, int interactionId,
1111                IAccessibilityInteractionConnectionCallback callback, long threadId)
1112                throws RemoteException {
1113            return findAccessibilityNodeInfosByViewText(text,
1114                    mSecurityPolicy.mRetrievalAlowingWindowId, View.NO_ID, interactionId, callback,
1115                    threadId);
1116        }
1117
1118        public float findAccessibilityNodeInfosByViewText(String text,
1119                int accessibilityWindowId, int accessibilityViewId, int interactionId,
1120                IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
1121                throws RemoteException {
1122            IAccessibilityInteractionConnection connection = null;
1123            synchronized (mLock) {
1124                mSecurityPolicy.enforceCanRetrieveWindowContent(this);
1125                final boolean permissionGranted =
1126                    mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, accessibilityWindowId);
1127                if (!permissionGranted) {
1128                    return 0;
1129                } else {
1130                    connection = getConnectionToRetrievalAllowingWindowLocked();
1131                    if (connection == null) {
1132                        if (DEBUG) {
1133                            Slog.e(LOG_TAG, "No interaction connection to focused window.");
1134                        }
1135                        return 0;
1136                    }
1137                }
1138            }
1139            final int interrogatingPid = Binder.getCallingPid();
1140            final long identityToken = Binder.clearCallingIdentity();
1141            try {
1142                connection.findAccessibilityNodeInfosByViewText(text, accessibilityViewId,
1143                        interactionId, callback, interrogatingPid, interrogatingTid);
1144            } catch (RemoteException re) {
1145                if (DEBUG) {
1146                    Slog.e(LOG_TAG, "Error finding node.");
1147                }
1148            } finally {
1149                Binder.restoreCallingIdentity(identityToken);
1150            }
1151            return getCompatibilityScale(accessibilityWindowId);
1152        }
1153
1154        public float findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
1155                int accessibilityViewId, int interactionId,
1156                IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
1157                throws RemoteException {
1158            IAccessibilityInteractionConnection connection = null;
1159            synchronized (mLock) {
1160                mSecurityPolicy.enforceCanRetrieveWindowContent(this);
1161                final boolean permissionGranted =
1162                    mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, accessibilityWindowId);
1163                if (!permissionGranted) {
1164                    return 0;
1165                } else {
1166                    AccessibilityConnectionWrapper wrapper =
1167                        mWindowIdToInteractionConnectionWrapperMap.get(accessibilityWindowId);
1168                    if (wrapper == null) {
1169                        if (DEBUG) {
1170                            Slog.e(LOG_TAG, "No interaction connection to window: "
1171                                    + accessibilityWindowId);
1172                        }
1173                        return 0;
1174                    }
1175                    connection = wrapper.mConnection;
1176                }
1177            }
1178            final int interrogatingPid = Binder.getCallingPid();
1179            final long identityToken = Binder.clearCallingIdentity();
1180            try {
1181                connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityViewId,
1182                        interactionId, callback, interrogatingPid, interrogatingTid);
1183            } catch (RemoteException re) {
1184                if (DEBUG) {
1185                    Slog.e(LOG_TAG, "Error requesting node with accessibilityViewId: "
1186                            + accessibilityViewId);
1187                }
1188            } finally {
1189                Binder.restoreCallingIdentity(identityToken);
1190            }
1191            return getCompatibilityScale(accessibilityWindowId);
1192        }
1193
1194        public boolean performAccessibilityAction(int accessibilityWindowId,
1195                int accessibilityViewId, int action, int interactionId,
1196                IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) {
1197            IAccessibilityInteractionConnection connection = null;
1198            synchronized (mLock) {
1199                final boolean permissionGranted = mSecurityPolicy.canPerformActionLocked(this,
1200                        accessibilityWindowId, action);
1201                if (!permissionGranted) {
1202                    return false;
1203                } else {
1204                    AccessibilityConnectionWrapper wrapper =
1205                        mWindowIdToInteractionConnectionWrapperMap.get(accessibilityWindowId);
1206                    if (wrapper == null) {
1207                        if (DEBUG) {
1208                            Slog.e(LOG_TAG, "No interaction connection to window: "
1209                                    + accessibilityWindowId);
1210                        }
1211                        return false;
1212                    }
1213                    connection = wrapper.mConnection;
1214                }
1215            }
1216            final int interrogatingPid = Binder.getCallingPid();
1217            final long identityToken = Binder.clearCallingIdentity();
1218            try {
1219                connection.performAccessibilityAction(accessibilityViewId, action, interactionId,
1220                        callback, interrogatingPid, interrogatingTid);
1221            } catch (RemoteException re) {
1222                if (DEBUG) {
1223                    Slog.e(LOG_TAG, "Error requesting node with accessibilityViewId: "
1224                            + accessibilityViewId);
1225                }
1226            } finally {
1227                Binder.restoreCallingIdentity(identityToken);
1228            }
1229            return true;
1230        }
1231
1232        public void onServiceDisconnected(ComponentName componentName) {
1233            /* do nothing - #binderDied takes care */
1234        }
1235
1236        public void linkToOwnDeath() throws RemoteException {
1237            mService.linkToDeath(this, 0);
1238        }
1239
1240        public void unlinkToOwnDeath() {
1241            mService.unlinkToDeath(this, 0);
1242        }
1243
1244        public void dispose() {
1245            try {
1246                // Clear the proxy in the other process so this
1247                // IAccessibilityServiceConnection can be garbage collected.
1248                mServiceInterface.setConnection(null, mId);
1249            } catch (RemoteException re) {
1250                /* ignore */
1251            }
1252            mService = null;
1253            mServiceInterface = null;
1254        }
1255
1256        public void binderDied() {
1257            synchronized (mLock) {
1258                unlinkToOwnDeath();
1259                tryRemoveServiceLocked(this);
1260                // We no longer have an automation service, so restore
1261                // the state based on values in the settings database.
1262                if (mIsAutomation) {
1263                    handleAccessibilityEnabledSettingChangedLocked();
1264                }
1265            }
1266        }
1267
1268        private IAccessibilityInteractionConnection getConnectionToRetrievalAllowingWindowLocked() {
1269            final int windowId = mSecurityPolicy.getRetrievalAllowingWindowLocked();
1270            if (DEBUG) {
1271                Slog.i(LOG_TAG, "Trying to get interaction connection to windowId: " + windowId);
1272            }
1273            AccessibilityConnectionWrapper wrapper =
1274                mWindowIdToInteractionConnectionWrapperMap.get(windowId);
1275            return (wrapper != null) ? wrapper.mConnection : null;
1276        }
1277
1278        private float getCompatibilityScale(int windowId) {
1279            IBinder windowToken = mWindowIdToWindowTokenMap.get(windowId);
1280            return mWindowManagerService.getWindowCompatibilityScale(windowToken);
1281        }
1282    }
1283
1284    final class SecurityPolicy {
1285        private static final int VALID_ACTIONS = AccessibilityNodeInfo.ACTION_FOCUS
1286            | AccessibilityNodeInfo.ACTION_CLEAR_FOCUS | AccessibilityNodeInfo.ACTION_SELECT
1287            | AccessibilityNodeInfo.ACTION_CLEAR_SELECTION;
1288
1289        private static final int RETRIEVAL_ALLOWING_EVENT_TYPES =
1290            AccessibilityEvent.TYPE_VIEW_CLICKED | AccessibilityEvent.TYPE_VIEW_FOCUSED
1291            | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT
1292            | AccessibilityEvent.TYPE_VIEW_LONG_CLICKED | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
1293            | AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED | AccessibilityEvent.TYPE_VIEW_SELECTED
1294            | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
1295            | AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED
1296            | AccessibilityEvent.TYPE_VIEW_SCROLLED;
1297
1298        private static final int RETRIEVAL_ALLOWING_WINDOW_CHANGE_EVENT_TYPES =
1299            AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER
1300            | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT;
1301
1302        private int mRetrievalAlowingWindowId;
1303
1304        private boolean canDispatchAccessibilityEvent(AccessibilityEvent event) {
1305            // Send window changed event only for the retrieval allowing window.
1306            return (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
1307                    || event.getWindowId() == mRetrievalAlowingWindowId);
1308        }
1309
1310        public void updateRetrievalAllowingWindowAndEventSourceLocked(AccessibilityEvent event) {
1311            final int windowId = event.getWindowId();
1312            final int eventType = event.getEventType();
1313            if ((eventType & RETRIEVAL_ALLOWING_WINDOW_CHANGE_EVENT_TYPES) != 0) {
1314                mRetrievalAlowingWindowId = windowId;
1315            }
1316            if ((eventType & RETRIEVAL_ALLOWING_EVENT_TYPES) == 0) {
1317                event.setSource(null);
1318            }
1319        }
1320
1321        public int getRetrievalAllowingWindowLocked() {
1322            return mRetrievalAlowingWindowId;
1323        }
1324
1325        public boolean canGetAccessibilityNodeInfoLocked(Service service, int windowId) {
1326            return canRetrieveWindowContent(service) && isRetrievalAllowingWindow(windowId);
1327        }
1328
1329        public boolean canPerformActionLocked(Service service, int windowId, int action) {
1330            return canRetrieveWindowContent(service)
1331                && isRetrievalAllowingWindow(windowId)
1332                && isActionPermitted(action);
1333        }
1334
1335        public boolean canRetrieveWindowContent(Service service) {
1336            return service.mCanRetrieveScreenContent;
1337        }
1338
1339        public void enforceCanRetrieveWindowContent(Service service) throws RemoteException {
1340            // This happens due to incorrect registration so make it apparent.
1341            if (!canRetrieveWindowContent(service)) {
1342                Slog.e(LOG_TAG, "Accessibility serivce " + service.mComponentName + " does not " +
1343                        "declare android:canRetrieveWindowContent.");
1344                throw new RemoteException();
1345            }
1346        }
1347
1348        private boolean isRetrievalAllowingWindow(int windowId) {
1349            return (mRetrievalAlowingWindowId == windowId);
1350        }
1351
1352        private boolean isActionPermitted(int action) {
1353             return (VALID_ACTIONS & action) != 0;
1354        }
1355
1356        private void enforceCallingPermission(String permission, String function) {
1357            if (OWN_PROCESS_ID == Binder.getCallingPid()) {
1358                return;
1359            }
1360            final int permissionStatus = mContext.checkCallingPermission(permission);
1361            if (permissionStatus != PackageManager.PERMISSION_GRANTED) {
1362                throw new SecurityException("You do not have " + permission
1363                        + " required to call " + function);
1364            }
1365        }
1366    }
1367}
1368