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