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