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