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