AccessibilityManagerService.java revision c9c9a48e7bafae63cb35a9aa69255e80aba83988
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.FLAG_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.AlertDialog;
28import android.app.PendingIntent;
29import android.app.StatusBarManager;
30import android.content.BroadcastReceiver;
31import android.content.ComponentName;
32import android.content.ContentResolver;
33import android.content.Context;
34import android.content.DialogInterface;
35import android.content.DialogInterface.OnClickListener;
36import android.content.Intent;
37import android.content.IntentFilter;
38import android.content.ServiceConnection;
39import android.content.pm.PackageManager;
40import android.content.pm.ResolveInfo;
41import android.content.pm.ServiceInfo;
42import android.database.ContentObserver;
43import android.graphics.Rect;
44import android.hardware.input.InputManager;
45import android.net.Uri;
46import android.os.Binder;
47import android.os.Build;
48import android.os.Bundle;
49import android.os.Handler;
50import android.os.IBinder;
51import android.os.Message;
52import android.os.RemoteException;
53import android.os.ServiceManager;
54import android.os.SystemClock;
55import android.provider.Settings;
56import android.text.TextUtils;
57import android.text.TextUtils.SimpleStringSplitter;
58import android.util.Slog;
59import android.util.SparseArray;
60import android.view.IWindow;
61import android.view.IWindowManager;
62import android.view.InputDevice;
63import android.view.KeyCharacterMap;
64import android.view.KeyEvent;
65import android.view.WindowManager;
66import android.view.accessibility.AccessibilityEvent;
67import android.view.accessibility.AccessibilityInteractionClient;
68import android.view.accessibility.AccessibilityManager;
69import android.view.accessibility.AccessibilityNodeInfo;
70import android.view.accessibility.IAccessibilityInteractionConnection;
71import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
72import android.view.accessibility.IAccessibilityManager;
73import android.view.accessibility.IAccessibilityManagerClient;
74
75import com.android.internal.R;
76import com.android.internal.content.PackageMonitor;
77import com.android.internal.statusbar.IStatusBarService;
78
79import org.xmlpull.v1.XmlPullParserException;
80
81import java.io.IOException;
82import java.util.ArrayList;
83import java.util.Arrays;
84import java.util.HashMap;
85import java.util.HashSet;
86import java.util.Iterator;
87import java.util.List;
88import java.util.Map;
89import java.util.Set;
90
91/**
92 * This class is instantiated by the system as a system level service and can be
93 * accessed only by the system. The task of this service is to be a centralized
94 * event dispatch for {@link AccessibilityEvent}s generated across all processes
95 * on the device. Events are dispatched to {@link AccessibilityService}s.
96 *
97 * @hide
98 */
99public class AccessibilityManagerService extends IAccessibilityManager.Stub {
100
101    private static final boolean DEBUG = false;
102
103    private static final String LOG_TAG = "AccessibilityManagerService";
104
105    private static final String FUNCTION_REGISTER_UI_TEST_AUTOMATION_SERVICE =
106        "registerUiTestAutomationService";
107
108    private static final char COMPONENT_NAME_SEPARATOR = ':';
109
110    private static final int OWN_PROCESS_ID = android.os.Process.myPid();
111
112    private static final int MSG_SHOW_ENABLE_TOUCH_EXPLORATION_DIALOG = 1;
113
114    private static final int MSG_TOGGLE_TOUCH_EXPLORATION = 2;
115
116    private static final int MSG_SEND_ACCESSIBILITY_EVENT_TO_INPUT_FILTER = 3;
117
118    private static int sIdCounter = 0;
119
120    private static int sNextWindowId;
121
122    final Context mContext;
123
124    final Object mLock = new Object();
125
126    final List<Service> mServices = new ArrayList<Service>();
127
128    final List<IAccessibilityManagerClient> mClients =
129        new ArrayList<IAccessibilityManagerClient>();
130
131    final Map<ComponentName, Service> mComponentNameToServiceMap = new HashMap<ComponentName, Service>();
132
133    private final List<AccessibilityServiceInfo> mInstalledServices = new ArrayList<AccessibilityServiceInfo>();
134
135    private final Set<ComponentName> mEnabledServices = new HashSet<ComponentName>();
136
137    private final Set<ComponentName> mTouchExplorationGrantedServices = new HashSet<ComponentName>();
138
139    private final SparseArray<AccessibilityConnectionWrapper> mWindowIdToInteractionConnectionWrapperMap =
140        new SparseArray<AccessibilityConnectionWrapper>();
141
142    private final SparseArray<IBinder> mWindowIdToWindowTokenMap = new SparseArray<IBinder>();
143
144    private final SimpleStringSplitter mStringColonSplitter = new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);
145
146    private PackageManager mPackageManager;
147
148    private int mHandledFeedbackTypes = 0;
149
150    private boolean mIsAccessibilityEnabled;
151
152    private AccessibilityInputFilter mInputFilter;
153
154    private boolean mHasInputFilter;
155
156    private final List<AccessibilityServiceInfo> mEnabledServicesForFeedbackTempList = new ArrayList<AccessibilityServiceInfo>();
157
158    private boolean mIsTouchExplorationEnabled;
159
160    private final IWindowManager mWindowManager;
161
162    private final SecurityPolicy mSecurityPolicy;
163
164    private final MainHanler mMainHandler;
165
166    private Service mUiAutomationService;
167
168    private Service mQueryBridge;
169
170    private boolean mTouchExplorationGestureEnded;
171
172    private boolean mTouchExplorationGestureStarted;
173
174    private AlertDialog mEnableTouchExplorationDialog;
175
176    /**
177     * Creates a new instance.
178     *
179     * @param context A {@link Context} instance.
180     */
181    public AccessibilityManagerService(Context context) {
182        mContext = context;
183        mPackageManager = mContext.getPackageManager();
184        mWindowManager = (IWindowManager) ServiceManager.getService(Context.WINDOW_SERVICE);
185        mSecurityPolicy = new SecurityPolicy();
186        mMainHandler = new MainHanler();
187        registerPackageChangeAndBootCompletedBroadcastReceiver();
188        registerSettingsContentObservers();
189    }
190
191    /**
192     * Registers a {@link BroadcastReceiver} for the events of
193     * adding/changing/removing/restarting a package and boot completion.
194     */
195    private void registerPackageChangeAndBootCompletedBroadcastReceiver() {
196        Context context = mContext;
197
198        PackageMonitor monitor = new PackageMonitor() {
199            @Override
200            public void onSomePackagesChanged() {
201                synchronized (mLock) {
202                    // We will update when the automation service dies.
203                    if (mUiAutomationService == null) {
204                        populateAccessibilityServiceListLocked();
205                        manageServicesLocked();
206                    }
207                }
208            }
209
210            @Override
211            public void onPackageRemoved(String packageName, int uid) {
212                synchronized (mLock) {
213                    Iterator<ComponentName> it = mEnabledServices.iterator();
214                    while (it.hasNext()) {
215                        ComponentName comp = it.next();
216                        String compPkg = comp.getPackageName();
217                        if (compPkg.equals(packageName)) {
218                            it.remove();
219                            // Update the enabled services setting.
220                            persistComponentNamesToSettingLocked(
221                                    Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
222                                    mEnabledServices);
223                            // Update the touch exploration granted services setting.
224                            mTouchExplorationGrantedServices.remove(comp);
225                            persistComponentNamesToSettingLocked(
226                                    Settings.Secure.
227                                            TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
228                                    mEnabledServices);
229                            return;
230                        }
231                    }
232                }
233            }
234
235            @Override
236            public boolean onHandleForceStop(Intent intent, String[] packages,
237                    int uid, boolean doit) {
238                synchronized (mLock) {
239                    Iterator<ComponentName> it = mEnabledServices.iterator();
240                    while (it.hasNext()) {
241                        ComponentName comp = it.next();
242                        String compPkg = comp.getPackageName();
243                        for (String pkg : packages) {
244                            if (compPkg.equals(pkg)) {
245                                if (!doit) {
246                                    return true;
247                                }
248                                it.remove();
249                                persistComponentNamesToSettingLocked(
250                                        Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
251                                        mEnabledServices);
252                            }
253                        }
254                    }
255                    return false;
256                }
257            }
258
259            @Override
260            public void onReceive(Context context, Intent intent) {
261                if (intent.getAction() == Intent.ACTION_BOOT_COMPLETED) {
262                    synchronized (mLock) {
263                        // We will update when the automation service dies.
264                        if (mUiAutomationService == null) {
265                            populateAccessibilityServiceListLocked();
266                            populateEnabledAccessibilityServicesLocked();
267                            populateTouchExplorationGrantedAccessibilityServicesLocked();
268                            handleAccessibilityEnabledSettingChangedLocked();
269                            handleTouchExplorationEnabledSettingChangedLocked();
270                            updateInputFilterLocked();
271                            sendStateToClientsLocked();
272                        }
273                    }
274                    return;
275                }
276
277                super.onReceive(context, intent);
278            }
279        };
280
281        // package changes
282        monitor.register(context, null, true);
283
284        // boot completed
285        IntentFilter bootFiler = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
286        mContext.registerReceiver(monitor, bootFiler, null, monitor.getRegisteredHandler());
287    }
288
289    /**
290     * {@link ContentObserver}s for {@link Settings.Secure#ACCESSIBILITY_ENABLED}
291     * and {@link Settings.Secure#ENABLED_ACCESSIBILITY_SERVICES} settings.
292     */
293    private void registerSettingsContentObservers() {
294        ContentResolver contentResolver = mContext.getContentResolver();
295
296        Uri accessibilityEnabledUri = Settings.Secure.getUriFor(
297                Settings.Secure.ACCESSIBILITY_ENABLED);
298        contentResolver.registerContentObserver(accessibilityEnabledUri, false,
299            new ContentObserver(new Handler()) {
300                @Override
301                public void onChange(boolean selfChange) {
302                    super.onChange(selfChange);
303                    synchronized (mLock) {
304                        // We will update when the automation service dies.
305                        if (mUiAutomationService == null) {
306                            handleAccessibilityEnabledSettingChangedLocked();
307                            updateInputFilterLocked();
308                            sendStateToClientsLocked();
309                        }
310                    }
311                }
312            });
313
314        Uri touchExplorationRequestedUri = Settings.Secure.getUriFor(
315                Settings.Secure.TOUCH_EXPLORATION_ENABLED);
316        contentResolver.registerContentObserver(touchExplorationRequestedUri, false,
317                new ContentObserver(new Handler()) {
318                    @Override
319                    public void onChange(boolean selfChange) {
320                        super.onChange(selfChange);
321                        synchronized (mLock) {
322                            // We will update when the automation service dies.
323                            if (mUiAutomationService == null) {
324                                handleTouchExplorationEnabledSettingChangedLocked();
325                                updateInputFilterLocked();
326                                sendStateToClientsLocked();
327                            }
328                        }
329                    }
330                });
331
332        Uri accessibilityServicesUri =
333            Settings.Secure.getUriFor(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
334        contentResolver.registerContentObserver(accessibilityServicesUri, false,
335            new ContentObserver(new Handler()) {
336                @Override
337                public void onChange(boolean selfChange) {
338                    super.onChange(selfChange);
339                    synchronized (mLock) {
340                        // We will update when the automation service dies.
341                        if (mUiAutomationService == null) {
342                            populateEnabledAccessibilityServicesLocked();
343                            manageServicesLocked();
344                        }
345                    }
346                }
347            });
348
349        Uri touchExplorationGrantedServicesUri = Settings.Secure.getUriFor(
350                Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES);
351        contentResolver.registerContentObserver(touchExplorationGrantedServicesUri, false,
352            new ContentObserver(new Handler()) {
353                @Override
354                public void onChange(boolean selfChange) {
355                    super.onChange(selfChange);
356                    synchronized (mLock) {
357                        // We will update when the automation service dies.
358                        if (mUiAutomationService == null) {
359                            populateTouchExplorationGrantedAccessibilityServicesLocked();
360                            handleTouchExplorationGrantedAccessibilityServicesChangedLocked();
361                        }
362                    }
363                }
364            });
365    }
366
367    public int addClient(IAccessibilityManagerClient client) throws RemoteException {
368        synchronized (mLock) {
369            final IAccessibilityManagerClient addedClient = client;
370            mClients.add(addedClient);
371            // Clients are registered all the time until their process is
372            // killed, therefore we do not have a corresponding unlinkToDeath.
373            client.asBinder().linkToDeath(new DeathRecipient() {
374                public void binderDied() {
375                    synchronized (mLock) {
376                        addedClient.asBinder().unlinkToDeath(this, 0);
377                        mClients.remove(addedClient);
378                    }
379                }
380            }, 0);
381            return getState();
382        }
383    }
384
385    public boolean sendAccessibilityEvent(AccessibilityEvent event) {
386        final int eventType = event.getEventType();
387
388        // The event for gesture start should be strictly before the
389        // first hover enter event for the gesture.
390        if (eventType == AccessibilityEvent.TYPE_VIEW_HOVER_ENTER
391                && mTouchExplorationGestureStarted) {
392            mTouchExplorationGestureStarted = false;
393            AccessibilityEvent gestureStartEvent = AccessibilityEvent.obtain(
394                    AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START);
395            sendAccessibilityEvent(gestureStartEvent);
396        }
397
398        synchronized (mLock) {
399            if (mSecurityPolicy.canDispatchAccessibilityEvent(event)) {
400                mSecurityPolicy.updateActiveWindowAndEventSourceLocked(event);
401                notifyAccessibilityServicesDelayedLocked(event, false);
402                notifyAccessibilityServicesDelayedLocked(event, true);
403            }
404            if (mHasInputFilter && mInputFilter != null) {
405                mMainHandler.obtainMessage(MSG_SEND_ACCESSIBILITY_EVENT_TO_INPUT_FILTER,
406                        AccessibilityEvent.obtain(event)).sendToTarget();
407
408            }
409            event.recycle();
410            mHandledFeedbackTypes = 0;
411        }
412
413        // The event for gesture end should be strictly after the
414        // last hover exit event for the gesture.
415        if (eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT
416                && mTouchExplorationGestureEnded) {
417            mTouchExplorationGestureEnded = false;
418            AccessibilityEvent gestureEndEvent = AccessibilityEvent.obtain(
419                    AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END);
420            sendAccessibilityEvent(gestureEndEvent);
421        }
422
423        return (OWN_PROCESS_ID != Binder.getCallingPid());
424    }
425
426    public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList() {
427        synchronized (mLock) {
428            return mInstalledServices;
429        }
430    }
431
432    public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType) {
433        List<AccessibilityServiceInfo> result = mEnabledServicesForFeedbackTempList;
434        result.clear();
435        List<Service> services = mServices;
436        synchronized (mLock) {
437            while (feedbackType != 0) {
438                final int feedbackTypeBit = (1 << Integer.numberOfTrailingZeros(feedbackType));
439                feedbackType &= ~feedbackTypeBit;
440                final int serviceCount = services.size();
441                for (int i = 0; i < serviceCount; i++) {
442                    Service service = services.get(i);
443                    if ((service.mFeedbackType & feedbackTypeBit) != 0) {
444                        result.add(service.mAccessibilityServiceInfo);
445                    }
446                }
447            }
448        }
449        return result;
450    }
451
452    public void interrupt() {
453        synchronized (mLock) {
454            for (int i = 0, count = mServices.size(); i < count; i++) {
455                Service service = mServices.get(i);
456                try {
457                    service.mServiceInterface.onInterrupt();
458                } catch (RemoteException re) {
459                    Slog.e(LOG_TAG, "Error during sending interrupt request to "
460                        + service.mService, re);
461                }
462            }
463        }
464    }
465
466    public int addAccessibilityInteractionConnection(IWindow windowToken,
467            IAccessibilityInteractionConnection connection) throws RemoteException {
468        synchronized (mLock) {
469            final IWindow addedWindowToken = windowToken;
470            final int windowId = sNextWindowId++;
471            AccessibilityConnectionWrapper wrapper = new AccessibilityConnectionWrapper(windowId,
472                    connection);
473            wrapper.linkToDeath();
474            mWindowIdToWindowTokenMap.put(windowId, addedWindowToken.asBinder());
475            mWindowIdToInteractionConnectionWrapperMap.put(windowId, wrapper);
476            if (DEBUG) {
477                Slog.i(LOG_TAG, "Adding interaction connection to windowId: " + windowId);
478            }
479            return windowId;
480        }
481    }
482
483    public void removeAccessibilityInteractionConnection(IWindow windowToken) {
484        synchronized (mLock) {
485            final int count = mWindowIdToWindowTokenMap.size();
486            for (int i = 0; i < count; i++) {
487                if (mWindowIdToWindowTokenMap.valueAt(i) == windowToken.asBinder()) {
488                    final int windowId = mWindowIdToWindowTokenMap.keyAt(i);
489                    AccessibilityConnectionWrapper wrapper =
490                        mWindowIdToInteractionConnectionWrapperMap.get(windowId);
491                    wrapper.unlinkToDeath();
492                    removeAccessibilityInteractionConnectionLocked(windowId);
493                    return;
494                }
495            }
496        }
497    }
498
499    public void registerUiTestAutomationService(IAccessibilityServiceClient serviceClient,
500            AccessibilityServiceInfo accessibilityServiceInfo) {
501        mSecurityPolicy.enforceCallingPermission(Manifest.permission.RETRIEVE_WINDOW_CONTENT,
502                FUNCTION_REGISTER_UI_TEST_AUTOMATION_SERVICE);
503        ComponentName componentName = new ComponentName("foo.bar",
504                "AutomationAccessibilityService");
505        synchronized (mLock) {
506            // If an automation services is connected to the system all services are stopped
507            // so the automation one is the only one running. Settings are not changed so when
508            // the automation service goes away the state is restored from the settings.
509
510            // Disable all services.
511            final int runningServiceCount = mServices.size();
512            for (int i = 0; i < runningServiceCount; i++) {
513                Service runningService = mServices.get(i);
514                runningService.unbind();
515            }
516            // If necessary enable accessibility and announce that.
517            if (!mIsAccessibilityEnabled) {
518                mIsAccessibilityEnabled = true;
519                sendStateToClientsLocked();
520            }
521        }
522        // Hook the automation service up.
523        mUiAutomationService = new Service(componentName, accessibilityServiceInfo, true);
524        mUiAutomationService.onServiceConnected(componentName, serviceClient.asBinder());
525    }
526
527    public void unregisterUiTestAutomationService(IAccessibilityServiceClient serviceClient) {
528        synchronized (mLock) {
529            // Automation service is not bound, so pretend it died to perform clean up.
530            if (mUiAutomationService != null
531                    && mUiAutomationService.mServiceInterface == serviceClient) {
532                mUiAutomationService.binderDied();
533            }
534        }
535    }
536
537    boolean onGesture(int gestureId) {
538        synchronized (mLock) {
539            boolean handled = notifyGestureLocked(gestureId, false);
540            if (!handled) {
541                handled = notifyGestureLocked(gestureId, true);
542            }
543            return handled;
544        }
545    }
546
547    /**
548     * Gets the bounds of the accessibility focus in the active window.
549     *
550     * @param outBounds The output to which to write the focus bounds.
551     * @return Whether accessibility focus was found and the bounds are populated.
552     */
553    boolean getAccessibilityFocusBoundsInActiveWindow(Rect outBounds) {
554        // Instead of keeping track of accessibility focus events per
555        // window to be able to find the focus in the active window,
556        // we take a stateless approach and look it up. This is fine
557        // since we do this only when the user clicks/long presses.
558        Service service = getQueryBridge();
559        final int connectionId = service.mId;
560        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
561        client.addConnection(connectionId, service);
562        try {
563            AccessibilityNodeInfo root = AccessibilityInteractionClient.getInstance()
564                    .getRootInActiveWindow(connectionId);
565            if (root == null) {
566                return false;
567            }
568            AccessibilityNodeInfo focus = root.findFocus(
569                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY);
570            if (focus == null) {
571                return false;
572            }
573            focus.getBoundsInScreen(outBounds);
574            return true;
575        } finally {
576            client.removeConnection(connectionId);
577        }
578    }
579
580    /**
581     * Gets the bounds of the active window.
582     *
583     * @param outBounds The output to which to write the bounds.
584     */
585    boolean getActiveWindowBounds(Rect outBounds) {
586        synchronized (mLock) {
587            final int windowId = mSecurityPolicy.mActiveWindowId;
588            IBinder token = mWindowIdToWindowTokenMap.get(windowId);
589            try {
590                mWindowManager.getWindowFrame(token, outBounds);
591                return true;
592            } catch (RemoteException re) {
593                /* ignore */
594            }
595            return false;
596        }
597    }
598
599    public int getActiveWindowId() {
600        return mSecurityPolicy.mActiveWindowId;
601    }
602
603    private Service getQueryBridge() {
604        if (mQueryBridge == null) {
605            AccessibilityServiceInfo info = new AccessibilityServiceInfo();
606            mQueryBridge = new Service(null, info, true);
607        }
608        return mQueryBridge;
609    }
610
611    public void touchExplorationGestureEnded() {
612        mTouchExplorationGestureEnded = true;
613    }
614
615    public void touchExplorationGestureStarted() {
616        mTouchExplorationGestureStarted = true;
617    }
618
619    private boolean notifyGestureLocked(int gestureId, boolean isDefault) {
620        // TODO: Now we are giving the gestures to the last enabled
621        //       service that can handle them which is the last one
622        //       in our list since we write the last enabled as the
623        //       last record in the enabled services setting. Ideally,
624        //       the user should make the call which service handles
625        //       gestures. However, only one service should handle
626        //       gestures to avoid user frustration when different
627        //       behavior is observed from different combinations of
628        //       enabled accessibility services.
629        for (int i = mServices.size() - 1; i >= 0; i--) {
630            Service service = mServices.get(i);
631            if (service.mRequestTouchExplorationMode && service.mIsDefault == isDefault) {
632                service.notifyGesture(gestureId);
633                return true;
634            }
635        }
636        return false;
637    }
638
639    /**
640     * Removes an AccessibilityInteractionConnection.
641     *
642     * @param windowId The id of the window to which the connection is targeted.
643     */
644    private void removeAccessibilityInteractionConnectionLocked(int windowId) {
645        mWindowIdToWindowTokenMap.remove(windowId);
646        mWindowIdToInteractionConnectionWrapperMap.remove(windowId);
647        if (DEBUG) {
648            Slog.i(LOG_TAG, "Removing interaction connection to windowId: " + windowId);
649        }
650    }
651
652    /**
653     * Populates the cached list of installed {@link AccessibilityService}s.
654     */
655    private void populateAccessibilityServiceListLocked() {
656        mInstalledServices.clear();
657
658        List<ResolveInfo> installedServices = mPackageManager.queryIntentServices(
659                new Intent(AccessibilityService.SERVICE_INTERFACE),
660                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
661
662        for (int i = 0, count = installedServices.size(); i < count; i++) {
663            ResolveInfo resolveInfo = installedServices.get(i);
664            ServiceInfo serviceInfo = resolveInfo.serviceInfo;
665            // For now we are enforcing this if the target version is JellyBean or
666            // higher and in a later release we will enforce this for everyone.
667            if (serviceInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN
668                    && !android.Manifest.permission.BIND_ACCESSIBILITY_SERVICE.equals(
669                    serviceInfo.permission)) {
670                Slog.w(LOG_TAG, "Skipping accessibilty service " + new ComponentName(
671                        serviceInfo.packageName, serviceInfo.name).flattenToShortString()
672                        + ": it does not require the permission "
673                        + android.Manifest.permission.BIND_ACCESSIBILITY_SERVICE);
674                continue;
675            }
676            AccessibilityServiceInfo accessibilityServiceInfo;
677            try {
678                accessibilityServiceInfo = new AccessibilityServiceInfo(resolveInfo, mContext);
679                mInstalledServices.add(accessibilityServiceInfo);
680            } catch (XmlPullParserException xppe) {
681                Slog.e(LOG_TAG, "Error while initializing AccessibilityServiceInfo", xppe);
682            } catch (IOException ioe) {
683                Slog.e(LOG_TAG, "Error while initializing AccessibilityServiceInfo", ioe);
684            }
685        }
686    }
687
688    private void populateEnabledAccessibilityServicesLocked() {
689        populateComponentNamesFromSettingLocked(
690                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
691                mEnabledServices);
692    }
693
694    private void populateTouchExplorationGrantedAccessibilityServicesLocked() {
695        populateComponentNamesFromSettingLocked(
696                Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
697                mTouchExplorationGrantedServices);
698    }
699
700    /**
701     * Performs {@link AccessibilityService}s delayed notification. The delay is configurable
702     * and denotes the period after the last event before notifying the service.
703     *
704     * @param event The event.
705     * @param isDefault True to notify default listeners, not default services.
706     */
707    private void notifyAccessibilityServicesDelayedLocked(AccessibilityEvent event,
708            boolean isDefault) {
709        try {
710            for (int i = 0, count = mServices.size(); i < count; i++) {
711                Service service = mServices.get(i);
712
713                if (service.mIsDefault == isDefault) {
714                    if (canDispathEventLocked(service, event, mHandledFeedbackTypes)) {
715                        mHandledFeedbackTypes |= service.mFeedbackType;
716                        service.notifyAccessibilityEvent(event);
717                    }
718                }
719            }
720        } catch (IndexOutOfBoundsException oobe) {
721            // An out of bounds exception can happen if services are going away
722            // as the for loop is running. If that happens, just bail because
723            // there are no more services to notify.
724            return;
725        }
726    }
727
728    /**
729     * Adds a service.
730     *
731     * @param service The service to add.
732     */
733    private void tryAddServiceLocked(Service service) {
734        try {
735            if (mServices.contains(service) || !service.isConfigured()) {
736                return;
737            }
738            service.linkToOwnDeath();
739            mServices.add(service);
740            mComponentNameToServiceMap.put(service.mComponentName, service);
741            updateInputFilterLocked();
742            tryEnableTouchExplorationLocked(service);
743        } catch (RemoteException e) {
744            /* do nothing */
745        }
746    }
747
748    /**
749     * Removes a service.
750     *
751     * @param service The service.
752     * @return True if the service was removed, false otherwise.
753     */
754    private boolean tryRemoveServiceLocked(Service service) {
755        final boolean removed = mServices.remove(service);
756        if (!removed) {
757            return false;
758        }
759        mComponentNameToServiceMap.remove(service.mComponentName);
760        service.unlinkToOwnDeath();
761        service.dispose();
762        updateInputFilterLocked();
763        tryDisableTouchExplorationLocked(service);
764        return removed;
765    }
766
767    /**
768     * Determines if given event can be dispatched to a service based on the package of the
769     * event source and already notified services for that event type. Specifically, a
770     * service is notified if it is interested in events from the package and no other service
771     * providing the same feedback type has been notified. Exception are services the
772     * provide generic feedback (feedback type left as a safety net for unforeseen feedback
773     * types) which are always notified.
774     *
775     * @param service The potential receiver.
776     * @param event The event.
777     * @param handledFeedbackTypes The feedback types for which services have been notified.
778     * @return True if the listener should be notified, false otherwise.
779     */
780    private boolean canDispathEventLocked(Service service, AccessibilityEvent event,
781            int handledFeedbackTypes) {
782
783        if (!service.isConfigured()) {
784            return false;
785        }
786
787        if (!event.isImportantForAccessibility()
788                && !service.mIncludeNotImportantViews) {
789            return false;
790        }
791
792        int eventType = event.getEventType();
793        if ((service.mEventTypes & eventType) != eventType) {
794            return false;
795        }
796
797        Set<String> packageNames = service.mPackageNames;
798        CharSequence packageName = event.getPackageName();
799
800        if (packageNames.isEmpty() || packageNames.contains(packageName)) {
801            int feedbackType = service.mFeedbackType;
802            if ((handledFeedbackTypes & feedbackType) != feedbackType
803                    || feedbackType == AccessibilityServiceInfo.FEEDBACK_GENERIC) {
804                return true;
805            }
806        }
807
808        return false;
809    }
810
811    /**
812     * Manages services by starting enabled ones and stopping disabled ones.
813     */
814    private void manageServicesLocked() {
815        final int enabledInstalledServicesCount = updateServicesStateLocked(mInstalledServices,
816                mEnabledServices);
817        // No enabled installed services => disable accessibility to avoid
818        // sending accessibility events with no recipient across processes.
819        if (mIsAccessibilityEnabled && enabledInstalledServicesCount == 0) {
820            Settings.Secure.putInt(mContext.getContentResolver(),
821                    Settings.Secure.ACCESSIBILITY_ENABLED, 0);
822        }
823    }
824
825    /**
826     * Unbinds all bound services.
827     */
828    private void unbindAllServicesLocked() {
829        List<Service> services = mServices;
830
831        for (int i = 0, count = services.size(); i < count; i++) {
832            Service service = services.get(i);
833            if (service.unbind()) {
834                i--;
835                count--;
836            }
837        }
838    }
839
840    /**
841     * Populates a set with the {@link ComponentName}s stored in a colon
842     * separated value setting.
843     *
844     * @param settingName The setting to parse.
845     * @param outComponentNames The output component names.
846     */
847    private void populateComponentNamesFromSettingLocked(String settingName,
848            Set<ComponentName> outComponentNames) {
849        outComponentNames.clear();
850
851        String settingValue = Settings.Secure.getString(mContext.getContentResolver(), settingName);
852
853        if (settingValue != null) {
854            TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
855            splitter.setString(settingValue);
856            while (splitter.hasNext()) {
857                String str = splitter.next();
858                if (str == null || str.length() <= 0) {
859                    continue;
860                }
861                ComponentName enabledService = ComponentName.unflattenFromString(str);
862                if (enabledService != null) {
863                    outComponentNames.add(enabledService);
864                }
865            }
866        }
867    }
868
869    /**
870     * Persists the component names in the specified setting in a
871     * colon separated fashion.
872     *
873     * @param settingName The setting name.
874     * @param componentNames The component names.
875     */
876    private void persistComponentNamesToSettingLocked(String settingName,
877            Set<ComponentName> componentNames) {
878        StringBuilder builder = new StringBuilder();
879        for (ComponentName componentName : componentNames) {
880            if (builder.length() > 0) {
881                builder.append(COMPONENT_NAME_SEPARATOR);
882            }
883            builder.append(componentName.flattenToShortString());
884        }
885        Settings.Secure.putString(mContext.getContentResolver(), settingName, builder.toString());
886    }
887
888    /**
889     * Updates the state of each service by starting (or keeping running) enabled ones and
890     * stopping the rest.
891     *
892     * @param installedServices All installed {@link AccessibilityService}s.
893     * @param enabledServices The {@link ComponentName}s of the enabled services.
894     * @return The number of enabled installed services.
895     */
896    private int updateServicesStateLocked(List<AccessibilityServiceInfo> installedServices,
897            Set<ComponentName> enabledServices) {
898
899        Map<ComponentName, Service> componentNameToServiceMap = mComponentNameToServiceMap;
900        boolean isEnabled = mIsAccessibilityEnabled;
901
902        int enabledInstalledServices = 0;
903        for (int i = 0, count = installedServices.size(); i < count; i++) {
904            AccessibilityServiceInfo installedService = installedServices.get(i);
905            ComponentName componentName = ComponentName.unflattenFromString(
906                    installedService.getId());
907            Service service = componentNameToServiceMap.get(componentName);
908
909            if (isEnabled) {
910                if (enabledServices.contains(componentName)) {
911                    if (service == null) {
912                        service = new Service(componentName, installedService, false);
913                    }
914                    service.bind();
915                    enabledInstalledServices++;
916                } else {
917                    if (service != null) {
918                        service.unbind();
919                    }
920                }
921            } else {
922                if (service != null) {
923                    service.unbind();
924                }
925            }
926        }
927
928        return enabledInstalledServices;
929    }
930
931    /**
932     * Sends the state to the clients.
933     */
934    private void sendStateToClientsLocked() {
935        final int state = getState();
936        for (int i = 0, count = mClients.size(); i < count; i++) {
937            try {
938                mClients.get(i).setState(state);
939            } catch (RemoteException re) {
940                mClients.remove(i);
941                count--;
942                i--;
943            }
944        }
945    }
946
947    /**
948     * Gets the current state as a set of flags.
949     *
950     * @return The state.
951     */
952    private int getState() {
953        int state = 0;
954        if (mIsAccessibilityEnabled) {
955            state |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
956        }
957        // Touch exploration relies on enabled accessibility.
958        if (mIsAccessibilityEnabled && mIsTouchExplorationEnabled) {
959            state |= AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED;
960        }
961        return state;
962    }
963
964    /**
965     * Updates the touch exploration state.
966     */
967    private void updateInputFilterLocked() {
968        if (mIsAccessibilityEnabled && mIsTouchExplorationEnabled) {
969            if (!mHasInputFilter) {
970                mHasInputFilter = true;
971                if (mInputFilter == null) {
972                    mInputFilter = new AccessibilityInputFilter(mContext, this);
973                }
974                try {
975                    mWindowManager.setInputFilter(mInputFilter);
976                } catch (RemoteException re) {
977                    /* ignore */
978                }
979            }
980            return;
981        }
982        if (mHasInputFilter) {
983            mHasInputFilter = false;
984            try {
985                mWindowManager.setInputFilter(null);
986            } catch (RemoteException re) {
987                /* ignore */
988            }
989        }
990    }
991
992    /**
993     * Updated the state based on the accessibility enabled setting.
994     */
995    private void handleAccessibilityEnabledSettingChangedLocked() {
996        mIsAccessibilityEnabled = Settings.Secure.getInt(
997                mContext.getContentResolver(),
998                Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
999        if (mIsAccessibilityEnabled) {
1000            manageServicesLocked();
1001        } else {
1002            unbindAllServicesLocked();
1003        }
1004    }
1005
1006    /**
1007     * Updates the state based on the touch exploration enabled setting.
1008     */
1009    private void handleTouchExplorationEnabledSettingChangedLocked() {
1010        mIsTouchExplorationEnabled = Settings.Secure.getInt(
1011                mContext.getContentResolver(),
1012                Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0) == 1;
1013    }
1014
1015    private void handleTouchExplorationGrantedAccessibilityServicesChangedLocked() {
1016        final int serviceCount = mServices.size();
1017        for (int i = 0; i < serviceCount; i++) {
1018            Service service = mServices.get(i);
1019            if (service.mRequestTouchExplorationMode
1020                    && mTouchExplorationGrantedServices.contains(service.mComponentName)) {
1021                tryEnableTouchExplorationLocked(service);
1022                return;
1023            }
1024        }
1025        if (mIsTouchExplorationEnabled) {
1026            mMainHandler.obtainMessage(MSG_TOGGLE_TOUCH_EXPLORATION, 0,
1027                    0).sendToTarget();
1028        }
1029    }
1030
1031    private void tryEnableTouchExplorationLocked(final Service service) {
1032        if (!mIsTouchExplorationEnabled && service.mRequestTouchExplorationMode) {
1033            final boolean canToggleTouchExploration = mTouchExplorationGrantedServices.contains(
1034                    service.mComponentName);
1035            if (!service.mIsAutomation && !canToggleTouchExploration) {
1036                mMainHandler.obtainMessage(MSG_SHOW_ENABLE_TOUCH_EXPLORATION_DIALOG,
1037                        service).sendToTarget();
1038            } else {
1039                mMainHandler.obtainMessage(MSG_TOGGLE_TOUCH_EXPLORATION, 1, 0).sendToTarget();
1040            }
1041        }
1042    }
1043
1044    private void tryDisableTouchExplorationLocked(Service service) {
1045        if (mIsTouchExplorationEnabled) {
1046            synchronized (mLock) {
1047                final int serviceCount = mServices.size();
1048                for (int i = 0; i < serviceCount; i++) {
1049                    Service other = mServices.get(i);
1050                    if (other != service && other.mRequestTouchExplorationMode) {
1051                        return;
1052                    }
1053                }
1054                mMainHandler.obtainMessage(MSG_TOGGLE_TOUCH_EXPLORATION, 0, 0).sendToTarget();
1055            }
1056        }
1057    }
1058
1059    private class AccessibilityConnectionWrapper implements DeathRecipient {
1060        private final int mWindowId;
1061        private final IAccessibilityInteractionConnection mConnection;
1062
1063        public AccessibilityConnectionWrapper(int windowId,
1064                IAccessibilityInteractionConnection connection) {
1065            mWindowId = windowId;
1066            mConnection = connection;
1067        }
1068
1069        public void linkToDeath() throws RemoteException {
1070            mConnection.asBinder().linkToDeath(this, 0);
1071        }
1072
1073        public void unlinkToDeath() {
1074            mConnection.asBinder().unlinkToDeath(this, 0);
1075        }
1076
1077        @Override
1078        public void binderDied() {
1079            unlinkToDeath();
1080            synchronized (mLock) {
1081                removeAccessibilityInteractionConnectionLocked(mWindowId);
1082            }
1083        }
1084    }
1085
1086    private class MainHanler extends Handler {
1087        @Override
1088        public void handleMessage(Message msg) {
1089            final int type = msg.what;
1090            switch (type) {
1091                case MSG_TOGGLE_TOUCH_EXPLORATION: {
1092                    final int value = msg.arg1;
1093                    Settings.Secure.putInt(mContext.getContentResolver(),
1094                            Settings.Secure.TOUCH_EXPLORATION_ENABLED, value);
1095                } break;
1096                case MSG_SHOW_ENABLE_TOUCH_EXPLORATION_DIALOG: {
1097                    final Service service = (Service) msg.obj;
1098                    String label = service.mResolveInfo.loadLabel(
1099                            mContext.getPackageManager()).toString();
1100                    synchronized (mLock) {
1101                        if (mIsTouchExplorationEnabled) {
1102                            return;
1103                        }
1104                        if (mEnableTouchExplorationDialog != null
1105                                && mEnableTouchExplorationDialog.isShowing()) {
1106                            return;
1107                        }
1108                        mEnableTouchExplorationDialog = new AlertDialog.Builder(mContext)
1109                            .setIcon(android.R.drawable.ic_dialog_alert)
1110                            .setPositiveButton(android.R.string.ok, new OnClickListener() {
1111                                @Override
1112                                public void onClick(DialogInterface dialog, int which) {
1113                                    // The user allowed the service to toggle touch exploration.
1114                                    mTouchExplorationGrantedServices.add(service.mComponentName);
1115                                    persistComponentNamesToSettingLocked(
1116                                            Settings.Secure.
1117                                                   TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
1118                                            mTouchExplorationGrantedServices);
1119                                    // Enable touch exploration.
1120                                    Settings.Secure.putInt(mContext.getContentResolver(),
1121                                            Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1);
1122                                }
1123                            })
1124                            .setNegativeButton(android.R.string.cancel, new OnClickListener() {
1125                                @Override
1126                                public void onClick(DialogInterface dialog, int which) {
1127                                    dialog.dismiss();
1128                                }
1129                            })
1130                            .setTitle(R.string.enable_explore_by_touch_warning_title)
1131                            .setMessage(mContext.getString(
1132                                R.string.enable_explore_by_touch_warning_message, label))
1133                            .create();
1134                        mEnableTouchExplorationDialog.getWindow().setType(
1135                                WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG);
1136                        mEnableTouchExplorationDialog.setCanceledOnTouchOutside(true);
1137                        mEnableTouchExplorationDialog.show();
1138                    }
1139                } break;
1140                case MSG_SEND_ACCESSIBILITY_EVENT_TO_INPUT_FILTER: {
1141                    AccessibilityEvent event = (AccessibilityEvent) msg.obj;
1142                    if (mHasInputFilter && mInputFilter != null) {
1143                        mInputFilter.onAccessibilityEvent(event);
1144                    }
1145                    event.recycle();
1146                } break;
1147            }
1148        }
1149    }
1150
1151    /**
1152     * This class represents an accessibility service. It stores all per service
1153     * data required for the service management, provides API for starting/stopping the
1154     * service and is responsible for adding/removing the service in the data structures
1155     * for service management. The class also exposes configuration interface that is
1156     * passed to the service it represents as soon it is bound. It also serves as the
1157     * connection for the service.
1158     */
1159    class Service extends IAccessibilityServiceConnection.Stub
1160            implements ServiceConnection, DeathRecipient {
1161
1162        // We pick the MSB to avoid collision since accessibility event types are
1163        // used as message types allowing us to remove messages per event type.
1164        private static final int MSG_ON_GESTURE = 0x80000000;
1165
1166        int mId = 0;
1167
1168        AccessibilityServiceInfo mAccessibilityServiceInfo;
1169
1170        IBinder mService;
1171
1172        IAccessibilityServiceClient mServiceInterface;
1173
1174        int mEventTypes;
1175
1176        int mFeedbackType;
1177
1178        Set<String> mPackageNames = new HashSet<String>();
1179
1180        boolean mIsDefault;
1181
1182        boolean mRequestTouchExplorationMode;
1183
1184        boolean mIncludeNotImportantViews;
1185
1186        long mNotificationTimeout;
1187
1188        ComponentName mComponentName;
1189
1190        Intent mIntent;
1191
1192        boolean mCanRetrieveScreenContent;
1193
1194        boolean mIsAutomation;
1195
1196        final Rect mTempBounds = new Rect();
1197
1198        final ResolveInfo mResolveInfo;
1199
1200        // the events pending events to be dispatched to this service
1201        final SparseArray<AccessibilityEvent> mPendingEvents =
1202            new SparseArray<AccessibilityEvent>();
1203
1204        /**
1205         * Handler for delayed event dispatch.
1206         */
1207        public Handler mHandler = new Handler(mMainHandler.getLooper()) {
1208            @Override
1209            public void handleMessage(Message message) {
1210                final int type = message.what;
1211                switch (type) {
1212                    case MSG_ON_GESTURE: {
1213                        final int gestureId = message.arg1;
1214                        notifyGestureInternal(gestureId);
1215                    } break;
1216                    default: {
1217                        final int eventType = type;
1218                        notifyAccessibilityEventInternal(eventType);
1219                    } break;
1220                }
1221            }
1222        };
1223
1224        public Service(ComponentName componentName,
1225                AccessibilityServiceInfo accessibilityServiceInfo, boolean isAutomation) {
1226            mResolveInfo = accessibilityServiceInfo.getResolveInfo();
1227            mId = sIdCounter++;
1228            mComponentName = componentName;
1229            mAccessibilityServiceInfo = accessibilityServiceInfo;
1230            mIsAutomation = isAutomation;
1231            if (!isAutomation) {
1232                mCanRetrieveScreenContent = accessibilityServiceInfo.getCanRetrieveWindowContent();
1233                mRequestTouchExplorationMode =
1234                    (accessibilityServiceInfo.flags
1235                            & AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0;
1236                mIntent = new Intent().setComponent(mComponentName);
1237                mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
1238                        com.android.internal.R.string.accessibility_binding_label);
1239                mIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
1240                        mContext, 0, new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS), 0));
1241            } else {
1242                mCanRetrieveScreenContent = true;
1243            }
1244            setDynamicallyConfigurableProperties(accessibilityServiceInfo);
1245        }
1246
1247        public void setDynamicallyConfigurableProperties(AccessibilityServiceInfo info) {
1248            mEventTypes = info.eventTypes;
1249            mFeedbackType = info.feedbackType;
1250            String[] packageNames = info.packageNames;
1251            if (packageNames != null) {
1252                mPackageNames.addAll(Arrays.asList(packageNames));
1253            }
1254            mNotificationTimeout = info.notificationTimeout;
1255            mIsDefault = (info.flags & DEFAULT) != 0;
1256
1257            if (mIsAutomation || info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion
1258                    >= Build.VERSION_CODES.JELLY_BEAN) {
1259                mIncludeNotImportantViews =
1260                    (info.flags & FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) != 0;
1261            }
1262
1263            mRequestTouchExplorationMode = (info.flags
1264                    & AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0;
1265
1266            // If this service is up and running we may have to enable touch
1267            // exploration, otherwise this will happen when the service connects.
1268            synchronized (mLock) {
1269                if (isConfigured()) {
1270                    if (mRequestTouchExplorationMode) {
1271                        tryEnableTouchExplorationLocked(this);
1272                    } else {
1273                        tryDisableTouchExplorationLocked(this);
1274                    }
1275                }
1276            }
1277        }
1278
1279        /**
1280         * Binds to the accessibility service.
1281         *
1282         * @return True if binding is successful.
1283         */
1284        public boolean bind() {
1285            if (!mIsAutomation && mService == null) {
1286                return mContext.bindService(mIntent, this, Context.BIND_AUTO_CREATE);
1287            }
1288            return false;
1289        }
1290
1291        /**
1292         * Unbinds form the accessibility service and removes it from the data
1293         * structures for service management.
1294         *
1295         * @return True if unbinding is successful.
1296         */
1297        public boolean unbind() {
1298            if (mService != null) {
1299                synchronized (mLock) {
1300                    tryRemoveServiceLocked(this);
1301                }
1302                if (!mIsAutomation) {
1303                    mContext.unbindService(this);
1304                }
1305                return true;
1306            }
1307            return false;
1308        }
1309
1310        /**
1311         * Returns if the service is configured i.e. at least event types of interest
1312         * and feedback type must be set.
1313         *
1314         * @return True if the service is configured, false otherwise.
1315         */
1316        public boolean isConfigured() {
1317            return (mEventTypes != 0 && mFeedbackType != 0 && mService != null);
1318        }
1319
1320        @Override
1321        public AccessibilityServiceInfo getServiceInfo() {
1322            synchronized (mLock) {
1323                return mAccessibilityServiceInfo;
1324            }
1325        }
1326
1327        @Override
1328        public void setServiceInfo(AccessibilityServiceInfo info) {
1329            synchronized (mLock) {
1330                // If the XML manifest had data to configure the service its info
1331                // should be already set. In such a case update only the dynamically
1332                // configurable properties.
1333                AccessibilityServiceInfo oldInfo = mAccessibilityServiceInfo;
1334                if (oldInfo != null) {
1335                    oldInfo.updateDynamicallyConfigurableProperties(info);
1336                    setDynamicallyConfigurableProperties(oldInfo);
1337                } else {
1338                    setDynamicallyConfigurableProperties(info);
1339                }
1340            }
1341        }
1342
1343        @Override
1344        public void onServiceConnected(ComponentName componentName, IBinder service) {
1345            mService = service;
1346            mServiceInterface = IAccessibilityServiceClient.Stub.asInterface(service);
1347            try {
1348                mServiceInterface.setConnection(this, mId);
1349                synchronized (mLock) {
1350                    tryAddServiceLocked(this);
1351                }
1352            } catch (RemoteException re) {
1353                Slog.w(LOG_TAG, "Error while setting Controller for service: " + service, re);
1354            }
1355        }
1356
1357        @Override
1358        public float findAccessibilityNodeInfoByViewId(int accessibilityWindowId,
1359                long accessibilityNodeId, int viewId, int interactionId,
1360                IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
1361                throws RemoteException {
1362            final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId);
1363            IAccessibilityInteractionConnection connection = null;
1364            synchronized (mLock) {
1365                mSecurityPolicy.enforceCanRetrieveWindowContent(this);
1366                final boolean permissionGranted = mSecurityPolicy.canRetrieveWindowContent(this);
1367                if (!permissionGranted) {
1368                    return 0;
1369                } else {
1370                    connection = getConnectionLocked(resolvedWindowId);
1371                    if (connection == null) {
1372                        return 0;
1373                    }
1374                }
1375            }
1376            final int flags = (mIncludeNotImportantViews) ?
1377                    AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0;
1378            final int interrogatingPid = Binder.getCallingPid();
1379            final long identityToken = Binder.clearCallingIdentity();
1380            try {
1381                connection.findAccessibilityNodeInfoByViewId(accessibilityNodeId, viewId,
1382                        interactionId, callback, flags, interrogatingPid, interrogatingTid);
1383                return getCompatibilityScale(resolvedWindowId);
1384            } catch (RemoteException re) {
1385                if (DEBUG) {
1386                    Slog.e(LOG_TAG, "Error findAccessibilityNodeInfoByViewId().");
1387                }
1388            } finally {
1389                Binder.restoreCallingIdentity(identityToken);
1390            }
1391            return 0;
1392        }
1393
1394        @Override
1395        public float findAccessibilityNodeInfosByText(int accessibilityWindowId,
1396                long accessibilityNodeId, String text, int interactionId,
1397                IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
1398                throws RemoteException {
1399            final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId);
1400            IAccessibilityInteractionConnection connection = null;
1401            synchronized (mLock) {
1402                mSecurityPolicy.enforceCanRetrieveWindowContent(this);
1403                final boolean permissionGranted =
1404                    mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
1405                if (!permissionGranted) {
1406                    return 0;
1407                } else {
1408                    connection = getConnectionLocked(resolvedWindowId);
1409                    if (connection == null) {
1410                        return 0;
1411                    }
1412                }
1413            }
1414            final int flags = (mIncludeNotImportantViews) ?
1415                    AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0;
1416            final int interrogatingPid = Binder.getCallingPid();
1417            final long identityToken = Binder.clearCallingIdentity();
1418            try {
1419                connection.findAccessibilityNodeInfosByText(accessibilityNodeId, text,
1420                        interactionId, callback, flags, interrogatingPid,
1421                        interrogatingTid);
1422                return getCompatibilityScale(resolvedWindowId);
1423            } catch (RemoteException re) {
1424                if (DEBUG) {
1425                    Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfosByText()");
1426                }
1427            } finally {
1428                Binder.restoreCallingIdentity(identityToken);
1429            }
1430            return 0;
1431        }
1432
1433        @Override
1434        public float findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
1435                long accessibilityNodeId, int interactionId,
1436                IAccessibilityInteractionConnectionCallback callback, int flags,
1437                long interrogatingTid) throws RemoteException {
1438            final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId);
1439            IAccessibilityInteractionConnection connection = null;
1440            synchronized (mLock) {
1441                mSecurityPolicy.enforceCanRetrieveWindowContent(this);
1442                final boolean permissionGranted =
1443                    mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
1444                if (!permissionGranted) {
1445                    return 0;
1446                } else {
1447                    connection = getConnectionLocked(resolvedWindowId);
1448                    if (connection == null) {
1449                        return 0;
1450                    }
1451                }
1452            }
1453            final int allFlags = flags | ((mIncludeNotImportantViews) ?
1454                    AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0);
1455            final int interrogatingPid = Binder.getCallingPid();
1456            final long identityToken = Binder.clearCallingIdentity();
1457            try {
1458                connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId,
1459                        interactionId, callback, allFlags, interrogatingPid, interrogatingTid);
1460                return getCompatibilityScale(resolvedWindowId);
1461            } catch (RemoteException re) {
1462                if (DEBUG) {
1463                    Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfoByAccessibilityId()");
1464                }
1465            } finally {
1466                Binder.restoreCallingIdentity(identityToken);
1467            }
1468            return 0;
1469        }
1470
1471        @Override
1472        public float findFocus(int accessibilityWindowId, long accessibilityNodeId,
1473                int focusType, int interactionId,
1474                IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
1475                throws RemoteException {
1476            final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId);
1477            IAccessibilityInteractionConnection connection = null;
1478            synchronized (mLock) {
1479                mSecurityPolicy.enforceCanRetrieveWindowContent(this);
1480                final boolean permissionGranted =
1481                    mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
1482                if (!permissionGranted) {
1483                    return 0;
1484                } else {
1485                    connection = getConnectionLocked(resolvedWindowId);
1486                    if (connection == null) {
1487                        return 0;
1488                    }
1489                }
1490            }
1491            final int flags = (mIncludeNotImportantViews) ?
1492                    AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0;
1493            final int interrogatingPid = Binder.getCallingPid();
1494            final long identityToken = Binder.clearCallingIdentity();
1495            try {
1496                connection.findFocus(accessibilityNodeId, focusType, interactionId, callback,
1497                        flags, interrogatingPid, interrogatingTid);
1498                return getCompatibilityScale(resolvedWindowId);
1499            } catch (RemoteException re) {
1500                if (DEBUG) {
1501                    Slog.e(LOG_TAG, "Error calling findAccessibilityFocus()");
1502                }
1503            } finally {
1504                Binder.restoreCallingIdentity(identityToken);
1505            }
1506            return 0;
1507        }
1508
1509        @Override
1510        public float focusSearch(int accessibilityWindowId, long accessibilityNodeId,
1511                int direction, int interactionId,
1512                IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
1513                throws RemoteException {
1514            final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId);
1515            IAccessibilityInteractionConnection connection = null;
1516            synchronized (mLock) {
1517                mSecurityPolicy.enforceCanRetrieveWindowContent(this);
1518                final boolean permissionGranted =
1519                    mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
1520                if (!permissionGranted) {
1521                    return 0;
1522                } else {
1523                    connection = getConnectionLocked(resolvedWindowId);
1524                    if (connection == null) {
1525                        return 0;
1526                    }
1527                }
1528            }
1529            final int flags = (mIncludeNotImportantViews) ?
1530                    AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0;
1531            final int interrogatingPid = Binder.getCallingPid();
1532            final long identityToken = Binder.clearCallingIdentity();
1533            try {
1534                connection.focusSearch(accessibilityNodeId, direction, interactionId, callback,
1535                        flags, interrogatingPid, interrogatingTid);
1536                return getCompatibilityScale(resolvedWindowId);
1537            } catch (RemoteException re) {
1538                if (DEBUG) {
1539                    Slog.e(LOG_TAG, "Error calling accessibilityFocusSearch()");
1540                }
1541            } finally {
1542                Binder.restoreCallingIdentity(identityToken);
1543            }
1544            return 0;
1545        }
1546
1547        @Override
1548        public boolean performAccessibilityAction(int accessibilityWindowId,
1549                long accessibilityNodeId, int action, Bundle arguments, int interactionId,
1550                IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) {
1551            final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId);
1552            IAccessibilityInteractionConnection connection = null;
1553            synchronized (mLock) {
1554                final boolean permissionGranted = mSecurityPolicy.canPerformActionLocked(this,
1555                        resolvedWindowId, action, arguments);
1556                if (!permissionGranted) {
1557                    return false;
1558                } else {
1559                    connection = getConnectionLocked(resolvedWindowId);
1560                    if (connection == null) {
1561                        return false;
1562                    }
1563                }
1564            }
1565            final int flags = (mIncludeNotImportantViews) ?
1566                    AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0;
1567            final int interrogatingPid = Binder.getCallingPid();
1568            final long identityToken = Binder.clearCallingIdentity();
1569            try {
1570                connection.performAccessibilityAction(accessibilityNodeId, action, arguments,
1571                        interactionId, callback, flags, interrogatingPid, interrogatingTid);
1572            } catch (RemoteException re) {
1573                if (DEBUG) {
1574                    Slog.e(LOG_TAG, "Error calling performAccessibilityAction()");
1575                }
1576            } finally {
1577                Binder.restoreCallingIdentity(identityToken);
1578            }
1579            return true;
1580        }
1581
1582        public boolean performGlobalAction(int action) {
1583            switch (action) {
1584                case AccessibilityService.GLOBAL_ACTION_BACK: {
1585                    sendDownAndUpKeyEvents(KeyEvent.KEYCODE_BACK);
1586                } return true;
1587                case AccessibilityService.GLOBAL_ACTION_HOME: {
1588                    sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HOME);
1589                } return true;
1590                case AccessibilityService.GLOBAL_ACTION_RECENTS: {
1591                    openRecents();
1592                } return true;
1593                case AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS: {
1594                    expandStatusBar();
1595                } return true;
1596            }
1597            return false;
1598        }
1599
1600        public void onServiceDisconnected(ComponentName componentName) {
1601            /* do nothing - #binderDied takes care */
1602        }
1603
1604        public void linkToOwnDeath() throws RemoteException {
1605            mService.linkToDeath(this, 0);
1606        }
1607
1608        public void unlinkToOwnDeath() {
1609            mService.unlinkToDeath(this, 0);
1610        }
1611
1612        public void dispose() {
1613            try {
1614                // Clear the proxy in the other process so this
1615                // IAccessibilityServiceConnection can be garbage collected.
1616                mServiceInterface.setConnection(null, mId);
1617            } catch (RemoteException re) {
1618                /* ignore */
1619            }
1620            mService = null;
1621            mServiceInterface = null;
1622        }
1623
1624        public void binderDied() {
1625            synchronized (mLock) {
1626                // The death recipient is unregistered in tryRemoveServiceLocked
1627                tryRemoveServiceLocked(this);
1628                // We no longer have an automation service, so restore
1629                // the state based on values in the settings database.
1630                if (mIsAutomation) {
1631                    mUiAutomationService = null;
1632
1633                    populateEnabledAccessibilityServicesLocked();
1634                    populateTouchExplorationGrantedAccessibilityServicesLocked();
1635
1636                    handleAccessibilityEnabledSettingChangedLocked();
1637                    sendStateToClientsLocked();
1638
1639                    handleTouchExplorationEnabledSettingChangedLocked();
1640                    updateInputFilterLocked();
1641
1642                    populateAccessibilityServiceListLocked();
1643                    manageServicesLocked();
1644                }
1645            }
1646        }
1647
1648        /**
1649         * Performs a notification for an {@link AccessibilityEvent}.
1650         *
1651         * @param event The event.
1652         */
1653        public void notifyAccessibilityEvent(AccessibilityEvent event) {
1654            synchronized (mLock) {
1655                final int eventType = event.getEventType();
1656                // Make a copy since during dispatch it is possible the event to
1657                // be modified to remove its source if the receiving service does
1658                // not have permission to access the window content.
1659                AccessibilityEvent newEvent = AccessibilityEvent.obtain(event);
1660                AccessibilityEvent oldEvent = mPendingEvents.get(eventType);
1661                mPendingEvents.put(eventType, newEvent);
1662
1663                final int what = eventType;
1664                if (oldEvent != null) {
1665                    mHandler.removeMessages(what);
1666                    oldEvent.recycle();
1667                }
1668
1669                Message message = mHandler.obtainMessage(what);
1670                mHandler.sendMessageDelayed(message, mNotificationTimeout);
1671            }
1672        }
1673
1674        /**
1675         * Notifies an accessibility service client for a scheduled event given the event type.
1676         *
1677         * @param eventType The type of the event to dispatch.
1678         */
1679        private void notifyAccessibilityEventInternal(int eventType) {
1680            IAccessibilityServiceClient listener;
1681            AccessibilityEvent event;
1682
1683            synchronized (mLock) {
1684                listener = mServiceInterface;
1685
1686                // If the service died/was disabled while the message for dispatching
1687                // the accessibility event was propagating the listener may be null.
1688                if (listener == null) {
1689                    return;
1690                }
1691
1692                event = mPendingEvents.get(eventType);
1693
1694                // Check for null here because there is a concurrent scenario in which this
1695                // happens: 1) A binder thread calls notifyAccessibilityServiceDelayedLocked
1696                // which posts a message for dispatching an event. 2) The message is pulled
1697                // from the queue by the handler on the service thread and the latter is
1698                // just about to acquire the lock and call this method. 3) Now another binder
1699                // thread acquires the lock calling notifyAccessibilityServiceDelayedLocked
1700                // so the service thread waits for the lock; 4) The binder thread replaces
1701                // the event with a more recent one (assume the same event type) and posts a
1702                // dispatch request releasing the lock. 5) Now the main thread is unblocked and
1703                // dispatches the event which is removed from the pending ones. 6) And ... now
1704                // the service thread handles the last message posted by the last binder call
1705                // but the event is already dispatched and hence looking it up in the pending
1706                // ones yields null. This check is much simpler that keeping count for each
1707                // event type of each service to catch such a scenario since only one message
1708                // is processed at a time.
1709                if (event == null) {
1710                    return;
1711                }
1712
1713                mPendingEvents.remove(eventType);
1714                if (mSecurityPolicy.canRetrieveWindowContent(this)) {
1715                    event.setConnectionId(mId);
1716                } else {
1717                    event.setSource(null);
1718                }
1719                event.setSealed(true);
1720            }
1721
1722            try {
1723                listener.onAccessibilityEvent(event);
1724                if (DEBUG) {
1725                    Slog.i(LOG_TAG, "Event " + event + " sent to " + listener);
1726                }
1727            } catch (RemoteException re) {
1728                Slog.e(LOG_TAG, "Error during sending " + event + " to " + listener, re);
1729            } finally {
1730                event.recycle();
1731            }
1732        }
1733
1734        public void notifyGesture(int gestureId) {
1735            mHandler.obtainMessage(MSG_ON_GESTURE, gestureId, 0).sendToTarget();
1736        }
1737
1738        private void notifyGestureInternal(int gestureId) {
1739            IAccessibilityServiceClient listener = mServiceInterface;
1740            if (listener != null) {
1741                try {
1742                    listener.onGesture(gestureId);
1743                } catch (RemoteException re) {
1744                    Slog.e(LOG_TAG, "Error during sending gesture " + gestureId
1745                            + " to " + mService, re);
1746                }
1747            }
1748        }
1749
1750        private void sendDownAndUpKeyEvents(int keyCode) {
1751            final long token = Binder.clearCallingIdentity();
1752
1753            // Inject down.
1754            final long downTime = SystemClock.uptimeMillis();
1755            KeyEvent down = KeyEvent.obtain(downTime, downTime, KeyEvent.ACTION_DOWN, keyCode, 0, 0,
1756                    KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FROM_SYSTEM,
1757                    InputDevice.SOURCE_KEYBOARD, null);
1758            InputManager.getInstance().injectInputEvent(down,
1759                    InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
1760            down.recycle();
1761
1762            // Inject up.
1763            final long upTime = SystemClock.uptimeMillis();
1764            KeyEvent up = KeyEvent.obtain(downTime, upTime, KeyEvent.ACTION_UP, keyCode, 0, 0,
1765                    KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FROM_SYSTEM,
1766                    InputDevice.SOURCE_KEYBOARD, null);
1767            InputManager.getInstance().injectInputEvent(up,
1768                    InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
1769            up.recycle();
1770
1771            Binder.restoreCallingIdentity(token);
1772        }
1773
1774        private void expandStatusBar() {
1775            final long token = Binder.clearCallingIdentity();
1776
1777            StatusBarManager statusBarManager = (StatusBarManager) mContext.getSystemService(
1778                    android.app.Service.STATUS_BAR_SERVICE);
1779            statusBarManager.expand();
1780
1781            Binder.restoreCallingIdentity(token);
1782        }
1783
1784        private void openRecents() {
1785            final long token = Binder.clearCallingIdentity();
1786
1787            IStatusBarService statusBarService = IStatusBarService.Stub.asInterface(
1788                    ServiceManager.getService("statusbar"));
1789            try {
1790                statusBarService.toggleRecentApps();
1791            } catch (RemoteException e) {
1792                Slog.e(LOG_TAG, "Error toggling recent apps.");
1793            }
1794
1795            Binder.restoreCallingIdentity(token);
1796        }
1797
1798        private IAccessibilityInteractionConnection getConnectionLocked(int windowId) {
1799            if (DEBUG) {
1800                Slog.i(LOG_TAG, "Trying to get interaction connection to windowId: " + windowId);
1801            }
1802            AccessibilityConnectionWrapper wrapper = mWindowIdToInteractionConnectionWrapperMap.get(
1803                    windowId);
1804            if (wrapper != null && wrapper.mConnection != null) {
1805                return wrapper.mConnection;
1806            }
1807            if (DEBUG) {
1808                Slog.e(LOG_TAG, "No interaction connection to window: " + windowId);
1809            }
1810            return null;
1811        }
1812
1813        private int resolveAccessibilityWindowId(int accessibilityWindowId) {
1814            if (accessibilityWindowId == AccessibilityNodeInfo.ACTIVE_WINDOW_ID) {
1815                return mSecurityPolicy.mActiveWindowId;
1816            }
1817            return accessibilityWindowId;
1818        }
1819
1820        private float getCompatibilityScale(int windowId) {
1821            IBinder windowToken = mWindowIdToWindowTokenMap.get(windowId);
1822            try {
1823                return mWindowManager.getWindowCompatibilityScale(windowToken);
1824            } catch (RemoteException re) {
1825                /* ignore */
1826            }
1827            return 1.0f;
1828        }
1829    }
1830
1831    final class SecurityPolicy {
1832        private static final int VALID_ACTIONS =
1833            AccessibilityNodeInfo.ACTION_CLICK
1834            | AccessibilityNodeInfo.ACTION_LONG_CLICK
1835            | AccessibilityNodeInfo.ACTION_FOCUS
1836            | AccessibilityNodeInfo.ACTION_CLEAR_FOCUS
1837            | AccessibilityNodeInfo.ACTION_SELECT
1838            | AccessibilityNodeInfo.ACTION_CLEAR_SELECTION
1839            | AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS
1840            | AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS
1841            | AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
1842            | AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
1843            | AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT
1844            | AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT
1845            | AccessibilityNodeInfo.ACTION_SCROLL_FORWARD
1846            | AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD;
1847
1848        private static final int RETRIEVAL_ALLOWING_EVENT_TYPES =
1849            AccessibilityEvent.TYPE_VIEW_CLICKED
1850            | AccessibilityEvent.TYPE_VIEW_FOCUSED
1851            | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER
1852            | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT
1853            | AccessibilityEvent.TYPE_VIEW_LONG_CLICKED
1854            | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
1855            | AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
1856            | AccessibilityEvent.TYPE_VIEW_SELECTED
1857            | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
1858            | AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED
1859            | AccessibilityEvent.TYPE_VIEW_SCROLLED
1860            | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED
1861            | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED;
1862
1863        private int mActiveWindowId;
1864
1865        private boolean canDispatchAccessibilityEvent(AccessibilityEvent event) {
1866            // Send window changed event only for the retrieval allowing window.
1867            return (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
1868                    || event.getWindowId() == mActiveWindowId);
1869        }
1870
1871        public void updateActiveWindowAndEventSourceLocked(AccessibilityEvent event) {
1872            // The active window is either the window that has input focus or
1873            // the window that the user is currently touching. If the user is
1874            // touching a window that does not have input focus as soon as the
1875            // the user stops touching that window the focused window becomes
1876            // the active one.
1877            final int windowId = event.getWindowId();
1878            final int eventType = event.getEventType();
1879            switch (eventType) {
1880                case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: {
1881                    if (getFocusedWindowId() == windowId) {
1882                        mActiveWindowId = windowId;
1883                    }
1884                } break;
1885                case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER:
1886                case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT: {
1887                    mActiveWindowId = windowId;
1888                } break;
1889                case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END: {
1890                    mActiveWindowId = getFocusedWindowId();
1891                } break;
1892            }
1893            if ((eventType & RETRIEVAL_ALLOWING_EVENT_TYPES) == 0) {
1894                event.setSource(null);
1895            }
1896        }
1897
1898        public int getRetrievalAllowingWindowLocked() {
1899            return mActiveWindowId;
1900        }
1901
1902        public boolean canGetAccessibilityNodeInfoLocked(Service service, int windowId) {
1903            return canRetrieveWindowContent(service) && isRetrievalAllowingWindow(windowId);
1904        }
1905
1906        public boolean canPerformActionLocked(Service service, int windowId, int action,
1907                Bundle arguments) {
1908            return canRetrieveWindowContent(service)
1909                && isRetrievalAllowingWindow(windowId)
1910                && isActionPermitted(action);
1911        }
1912
1913        public boolean canRetrieveWindowContent(Service service) {
1914            return service.mCanRetrieveScreenContent;
1915        }
1916
1917        public void enforceCanRetrieveWindowContent(Service service) throws RemoteException {
1918            // This happens due to incorrect registration so make it apparent.
1919            if (!canRetrieveWindowContent(service)) {
1920                Slog.e(LOG_TAG, "Accessibility serivce " + service.mComponentName + " does not " +
1921                        "declare android:canRetrieveWindowContent.");
1922                throw new RemoteException();
1923            }
1924        }
1925
1926        private boolean isRetrievalAllowingWindow(int windowId) {
1927            return (mActiveWindowId == windowId);
1928        }
1929
1930        private boolean isActionPermitted(int action) {
1931             return (VALID_ACTIONS & action) != 0;
1932        }
1933
1934        private void enforceCallingPermission(String permission, String function) {
1935            if (OWN_PROCESS_ID == Binder.getCallingPid()) {
1936                return;
1937            }
1938            final int permissionStatus = mContext.checkCallingPermission(permission);
1939            if (permissionStatus != PackageManager.PERMISSION_GRANTED) {
1940                throw new SecurityException("You do not have " + permission
1941                        + " required to call " + function);
1942            }
1943        }
1944
1945        private int getFocusedWindowId() {
1946            final long identity = Binder.clearCallingIdentity();
1947            try {
1948                // We call this only on window focus change or after touch
1949                // exploration gesture end and the shown windows are not that
1950                // many, so the linear look up is just fine.
1951                IBinder token = mWindowManager.getFocusedWindowToken();
1952                if (token != null) {
1953                    SparseArray<IBinder> windows = mWindowIdToWindowTokenMap;
1954                    final int windowCount = windows.size();
1955                    for (int i = 0; i < windowCount; i++) {
1956                        if (windows.valueAt(i) == token) {
1957                            return windows.keyAt(i);
1958                        }
1959                    }
1960                }
1961            } catch (RemoteException re) {
1962                /* ignore */
1963            } finally {
1964                Binder.restoreCallingIdentity(identity);
1965            }
1966            return -1;
1967        }
1968    }
1969}
1970