1 /*
2  ** Copyright 2017, 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 android.accessibilityservice.AccessibilityService;
20 import android.accessibilityservice.AccessibilityServiceInfo;
21 import android.accessibilityservice.AccessibilityTrace;
22 import android.accessibilityservice.IAccessibilityServiceClient;
23 import android.annotation.Nullable;
24 import android.app.UiAutomation;
25 import android.content.ComponentName;
26 import android.content.Context;
27 import android.os.Binder;
28 import android.os.Handler;
29 import android.os.IBinder;
30 import android.os.IBinder.DeathRecipient;
31 import android.os.RemoteCallback;
32 import android.os.RemoteException;
33 import android.util.Slog;
34 import android.view.Display;
35 import android.view.accessibility.AccessibilityEvent;
36 
37 import com.android.internal.util.DumpUtils;
38 import com.android.server.utils.Slogf;
39 import com.android.server.wm.WindowManagerInternal;
40 
41 import java.io.FileDescriptor;
42 import java.io.PrintWriter;
43 
44 /**
45  * Class to manage UiAutomation.
46  */
47 class UiAutomationManager {
48     private static final ComponentName COMPONENT_NAME =
49             new ComponentName("com.android.server.accessibility", "UiAutomation");
50     private static final String LOG_TAG = "UiAutomationManager";
51 
52     private final Object mLock;
53 
54     private UiAutomationService mUiAutomationService;
55 
56     private AbstractAccessibilityServiceConnection.SystemSupport mSystemSupport;
57 
58     private int mUiAutomationFlags;
59 
UiAutomationManager(Object lock)60     UiAutomationManager(Object lock) {
61         mLock = lock;
62     }
63 
64     private IBinder mUiAutomationServiceOwner;
65     private final DeathRecipient mUiAutomationServiceOwnerDeathRecipient =
66             new DeathRecipient() {
67                 @Override
68                 public void binderDied() {
69                     mUiAutomationServiceOwner.unlinkToDeath(this, 0);
70                     mUiAutomationServiceOwner = null;
71                     destroyUiAutomationService();
72                     Slog.v(LOG_TAG, "UiAutomation service owner died");
73                 }
74             };
75 
76     /**
77      * Register a UiAutomation if it uses the accessibility subsystem. Only one may be registered
78      * at a time.
79      *
80      * @param owner A binder object owned by the process that owns the UiAutomation to be
81      *              registered.
82      * @param serviceClient The UiAutomation's service interface.
83      * @param accessibilityServiceInfo The UiAutomation's service info
84      * @param flags The UiAutomation's flags
85      * @param id The id for the service connection
86      * @see UiAutomation#FLAG_DONT_USE_ACCESSIBILITY
87      */
registerUiTestAutomationServiceLocked(IBinder owner, IAccessibilityServiceClient serviceClient, Context context, AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler, AccessibilitySecurityPolicy securityPolicy, AbstractAccessibilityServiceConnection.SystemSupport systemSupport, AccessibilityTrace trace, WindowManagerInternal windowManagerInternal, SystemActionPerformer systemActionPerformer, AccessibilityWindowManager awm, int flags)88     void registerUiTestAutomationServiceLocked(IBinder owner,
89             IAccessibilityServiceClient serviceClient,
90             Context context, AccessibilityServiceInfo accessibilityServiceInfo,
91             int id, Handler mainHandler,
92             AccessibilitySecurityPolicy securityPolicy,
93             AbstractAccessibilityServiceConnection.SystemSupport systemSupport,
94             AccessibilityTrace trace,
95             WindowManagerInternal windowManagerInternal,
96             SystemActionPerformer systemActionPerformer,
97             AccessibilityWindowManager awm, int flags) {
98         accessibilityServiceInfo.setComponentName(COMPONENT_NAME);
99         Slogf.i(LOG_TAG, "Registering UiTestAutomationService (id=%s) when called by user %d",
100                 accessibilityServiceInfo.getId(), Binder.getCallingUserHandle().getIdentifier());
101         synchronized (mLock) {
102             if (mUiAutomationService != null) {
103                 throw new IllegalStateException(
104                         "UiAutomationService " + mUiAutomationService.mServiceInterface
105                                 + "already registered!");
106             }
107 
108             try {
109                 owner.linkToDeath(mUiAutomationServiceOwnerDeathRecipient, 0);
110             } catch (RemoteException re) {
111                 Slog.e(LOG_TAG, "Couldn't register for the death of a UiTestAutomationService!",
112                         re);
113                 return;
114             }
115 
116             mUiAutomationFlags = flags;
117             mSystemSupport = systemSupport;
118             // Ignore registering UiAutomation if it is not allowed to use the accessibility
119             // subsystem.
120             if (!useAccessibility()) {
121                 return;
122             }
123             mUiAutomationService = new UiAutomationService(context, accessibilityServiceInfo, id,
124                     mainHandler, mLock, securityPolicy, systemSupport, trace, windowManagerInternal,
125                     systemActionPerformer, awm);
126             mUiAutomationServiceOwner = owner;
127             mUiAutomationService.mServiceInterface = serviceClient;
128             try {
129                 mUiAutomationService.mServiceInterface.asBinder().linkToDeath(mUiAutomationService,
130                         0);
131             } catch (RemoteException re) {
132                 Slog.e(LOG_TAG, "Failed registering death link: " + re);
133                 destroyUiAutomationService();
134                 return;
135             }
136 
137             mUiAutomationService.onAdded();
138 
139             mUiAutomationService.connectServiceUnknownThread();
140         }
141     }
142 
unregisterUiTestAutomationServiceLocked(IAccessibilityServiceClient serviceClient)143     void unregisterUiTestAutomationServiceLocked(IAccessibilityServiceClient serviceClient) {
144         synchronized (mLock) {
145             if (useAccessibility()
146                     && ((mUiAutomationService == null)
147                     || (serviceClient == null)
148                     || (mUiAutomationService.mServiceInterface == null)
149                     || (serviceClient.asBinder()
150                     != mUiAutomationService.mServiceInterface.asBinder()))) {
151                 throw new IllegalStateException("UiAutomationService " + serviceClient
152                         + " not registered!");
153             }
154             destroyUiAutomationService();
155         }
156     }
157 
sendAccessibilityEventLocked(AccessibilityEvent event)158     void sendAccessibilityEventLocked(AccessibilityEvent event) {
159         if (mUiAutomationService != null) {
160             mUiAutomationService.notifyAccessibilityEvent(event);
161         }
162     }
163 
isUiAutomationRunningLocked()164     boolean isUiAutomationRunningLocked() {
165         return (mUiAutomationService != null || !useAccessibility());
166     }
167 
suppressingAccessibilityServicesLocked()168     boolean suppressingAccessibilityServicesLocked() {
169         return (mUiAutomationService != null || !useAccessibility())
170                 && ((mUiAutomationFlags
171                 & UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES) == 0);
172     }
173 
useAccessibility()174     boolean useAccessibility() {
175         return ((mUiAutomationFlags & UiAutomation.FLAG_DONT_USE_ACCESSIBILITY) == 0);
176     }
177 
isTouchExplorationEnabledLocked()178     boolean isTouchExplorationEnabledLocked() {
179         return (mUiAutomationService != null)
180                 && mUiAutomationService.mRequestTouchExplorationMode;
181     }
182 
canRetrieveInteractiveWindowsLocked()183     boolean canRetrieveInteractiveWindowsLocked() {
184         return (mUiAutomationService != null) && mUiAutomationService.mRetrieveInteractiveWindows;
185     }
186 
getRequestedEventMaskLocked()187     int getRequestedEventMaskLocked() {
188         if (mUiAutomationService == null) return 0;
189         return mUiAutomationService.mEventTypes;
190     }
191 
getRelevantEventTypes()192     int getRelevantEventTypes() {
193         UiAutomationService uiAutomationService;
194         synchronized (mLock) {
195             uiAutomationService = mUiAutomationService;
196         }
197         if (uiAutomationService == null) return 0;
198         return uiAutomationService.getRelevantEventTypes();
199     }
200 
201     @Nullable
getServiceInfo()202     AccessibilityServiceInfo getServiceInfo() {
203         UiAutomationService uiAutomationService;
204         synchronized (mLock) {
205             uiAutomationService = mUiAutomationService;
206         }
207         if (uiAutomationService == null) return null;
208         return uiAutomationService.getServiceInfo();
209     }
210 
dumpUiAutomationService(FileDescriptor fd, final PrintWriter pw, String[] args)211     void dumpUiAutomationService(FileDescriptor fd, final PrintWriter pw, String[] args) {
212         UiAutomationService uiAutomationService;
213         synchronized (mLock) {
214             uiAutomationService = mUiAutomationService;
215         }
216         if (uiAutomationService != null) {
217             uiAutomationService.dump(fd, pw, args);
218         }
219     }
220 
destroyUiAutomationService()221     private void destroyUiAutomationService() {
222         synchronized (mLock) {
223             if (mUiAutomationService != null) {
224                 mUiAutomationService.mServiceInterface.asBinder().unlinkToDeath(
225                         mUiAutomationService, 0);
226                 mUiAutomationService.onRemoved();
227                 mUiAutomationService.resetLocked();
228                 mUiAutomationService = null;
229                 if (mUiAutomationServiceOwner != null) {
230                     mUiAutomationServiceOwner.unlinkToDeath(
231                             mUiAutomationServiceOwnerDeathRecipient, 0);
232                     mUiAutomationServiceOwner = null;
233                 }
234             }
235             mUiAutomationFlags = 0;
236             mSystemSupport.onClientChangeLocked(false);
237         }
238     }
239 
240     private class UiAutomationService extends AbstractAccessibilityServiceConnection {
241         private final Handler mMainHandler;
242 
UiAutomationService(Context context, AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler, Object lock, AccessibilitySecurityPolicy securityPolicy, SystemSupport systemSupport, AccessibilityTrace trace, WindowManagerInternal windowManagerInternal, SystemActionPerformer systemActionPerformer, AccessibilityWindowManager awm)243         UiAutomationService(Context context, AccessibilityServiceInfo accessibilityServiceInfo,
244                 int id, Handler mainHandler, Object lock,
245                 AccessibilitySecurityPolicy securityPolicy,
246                 SystemSupport systemSupport, AccessibilityTrace trace,
247                 WindowManagerInternal windowManagerInternal,
248                 SystemActionPerformer systemActionPerformer, AccessibilityWindowManager awm) {
249             super(context, COMPONENT_NAME, accessibilityServiceInfo, id, mainHandler, lock,
250                     securityPolicy, systemSupport, trace, windowManagerInternal,
251                     systemActionPerformer, awm);
252             mMainHandler = mainHandler;
253             setDisplayTypes(DISPLAY_TYPE_DEFAULT | DISPLAY_TYPE_PROXY);
254         }
255 
connectServiceUnknownThread()256         void connectServiceUnknownThread() {
257             // This needs to be done on the main thread
258             mMainHandler.post(() -> {
259                 try {
260                     final IAccessibilityServiceClient serviceInterface;
261                     synchronized (mLock) {
262                         serviceInterface = mServiceInterface;
263                         if (serviceInterface == null) {
264                             mService = null;
265                         } else {
266                             mService = mServiceInterface.asBinder();
267                             mService.linkToDeath(this, 0);
268                         }
269                     }
270                     // If the serviceInterface is null, the UiAutomation has been shut down on
271                     // another thread.
272                     if (serviceInterface != null) {
273                         if (mTrace.isA11yTracingEnabledForTypes(
274                                 AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) {
275                             mTrace.logTrace("UiAutomationService.connectServiceUnknownThread",
276                                     AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT,
277                                     "serviceConnection=" + this + ";connectionId=" + mId
278                                     + "windowToken="
279                                     + mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
280                         }
281                         serviceInterface.init(this, mId,
282                                 mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
283                     }
284                 } catch (RemoteException re) {
285                     Slog.w(LOG_TAG, "Error initialized connection", re);
286                     destroyUiAutomationService();
287                 }
288             });
289         }
290 
291         @Override
binderDied()292         public void binderDied() {
293             destroyUiAutomationService();
294         }
295 
296         @Override
hasRightsToCurrentUserLocked()297         protected boolean hasRightsToCurrentUserLocked() {
298             // Allow UiAutomation to work for any user
299             return true;
300         }
301 
302         @Override
supportsFlagForNotImportantViews(AccessibilityServiceInfo info)303         protected boolean supportsFlagForNotImportantViews(AccessibilityServiceInfo info) {
304             return true;
305         }
306 
307         @Override
dump(FileDescriptor fd, final PrintWriter pw, String[] args)308         public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
309             if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
310             synchronized (mLock) {
311                 pw.append("Ui Automation[eventTypes="
312                         + AccessibilityEvent.eventTypeToString(mEventTypes));
313                 pw.append(", notificationTimeout=" + mNotificationTimeout);
314                 pw.append("]");
315             }
316         }
317 
318         // Since this isn't really an accessibility service, several methods are just stubbed here.
319         @Override
setSoftKeyboardShowMode(int mode)320         public boolean setSoftKeyboardShowMode(int mode) {
321             return false;
322         }
323 
324         @Override
getSoftKeyboardShowMode()325         public int getSoftKeyboardShowMode() {
326             return 0;
327         }
328 
329         @Override
switchToInputMethod(String imeId)330         public boolean switchToInputMethod(String imeId) {
331             return false;
332         }
333 
334         @Override
setInputMethodEnabled(String imeId, boolean enabled)335         public int setInputMethodEnabled(String imeId, boolean enabled) {
336             return AccessibilityService.SoftKeyboardController.ENABLE_IME_FAIL_UNKNOWN;
337         }
338 
339         @Override
isAccessibilityButtonAvailable()340         public boolean isAccessibilityButtonAvailable() {
341             return false;
342         }
343 
344         @Override
disableSelf()345         public void disableSelf() {}
346 
347         @Override
onServiceConnected(ComponentName componentName, IBinder service)348         public void onServiceConnected(ComponentName componentName, IBinder service) {}
349 
350         @Override
onServiceDisconnected(ComponentName componentName)351         public void onServiceDisconnected(ComponentName componentName) {}
352 
353         @Override
isCapturingFingerprintGestures()354         public boolean isCapturingFingerprintGestures() {
355             return false;
356         }
357 
358         @Override
onFingerprintGestureDetectionActiveChanged(boolean active)359         public void onFingerprintGestureDetectionActiveChanged(boolean active) {}
360 
361         @Override
onFingerprintGesture(int gesture)362         public void onFingerprintGesture(int gesture) {}
363 
364         @Override
takeScreenshot(int displayId, RemoteCallback callback)365         public void takeScreenshot(int displayId, RemoteCallback callback) {}
366     }
367 }
368