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