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