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.AccessibilityServiceInfo;
20 import android.accessibilityservice.AccessibilityTrace;
21 import android.accessibilityservice.IAccessibilityServiceClient;
22 import android.annotation.Nullable;
23 import android.app.UiAutomation;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.os.Handler;
27 import android.os.IBinder;
28 import android.os.IBinder.DeathRecipient;
29 import android.os.RemoteCallback;
30 import android.os.RemoteException;
31 import android.util.Slog;
32 import android.view.Display;
33 import android.view.accessibility.AccessibilityEvent;
34 
35 import com.android.internal.util.DumpUtils;
36 import com.android.server.wm.WindowManagerInternal;
37 
38 import java.io.FileDescriptor;
39 import java.io.PrintWriter;
40 
41 /**
42  * Class to manage UiAutomation.
43  */
44 class UiAutomationManager {
45     private static final ComponentName COMPONENT_NAME =
46             new ComponentName("com.android.server.accessibility", "UiAutomation");
47     private static final String LOG_TAG = "UiAutomationManager";
48 
49     private final Object mLock;
50 
51     private UiAutomationService mUiAutomationService;
52 
53     private AccessibilityServiceInfo mUiAutomationServiceInfo;
54 
55     private AbstractAccessibilityServiceConnection.SystemSupport mSystemSupport;
56 
57     private AccessibilityTrace mTrace;
58 
59     private int mUiAutomationFlags;
60 
UiAutomationManager(Object lock)61     UiAutomationManager(Object lock) {
62         mLock = lock;
63     }
64 
65     private IBinder mUiAutomationServiceOwner;
66     private final DeathRecipient mUiAutomationServiceOwnerDeathRecipient =
67             new DeathRecipient() {
68                 @Override
69                 public void binderDied() {
70                     mUiAutomationServiceOwner.unlinkToDeath(this, 0);
71                     mUiAutomationServiceOwner = null;
72                     destroyUiAutomationService();
73                     Slog.v(LOG_TAG, "UiAutomation service owner died");
74                 }
75             };
76 
77     /**
78      * Register a UiAutomation if it uses the accessibility subsystem. Only one may be registered
79      * at a time.
80      *
81      * @param owner A binder object owned by the process that owns the UiAutomation to be
82      *              registered.
83      * @param serviceClient The UiAutomation's service interface.
84      * @param accessibilityServiceInfo The UiAutomation's service info
85      * @param flags The UiAutomation's flags
86      * @param id The id for the service connection
87      * @see UiAutomation#FLAG_DONT_USE_ACCESSIBILITY
88      */
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)89     void registerUiTestAutomationServiceLocked(IBinder owner,
90             IAccessibilityServiceClient serviceClient,
91             Context context, AccessibilityServiceInfo accessibilityServiceInfo,
92             int id, Handler mainHandler,
93             AccessibilitySecurityPolicy securityPolicy,
94             AbstractAccessibilityServiceConnection.SystemSupport systemSupport,
95             AccessibilityTrace trace,
96             WindowManagerInternal windowManagerInternal,
97             SystemActionPerformer systemActionPerformer,
98             AccessibilityWindowManager awm, int flags) {
99         synchronized (mLock) {
100             accessibilityServiceInfo.setComponentName(COMPONENT_NAME);
101 
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             mTrace = trace;
119             // Ignore registering UiAutomation if it is not allowed to use the accessibility
120             // subsystem.
121             if (!useAccessibility()) {
122                 return;
123             }
124             mUiAutomationService = new UiAutomationService(context, accessibilityServiceInfo, id,
125                     mainHandler, mLock, securityPolicy, systemSupport, trace, windowManagerInternal,
126                     systemActionPerformer, awm);
127             mUiAutomationServiceOwner = owner;
128             mUiAutomationServiceInfo = accessibilityServiceInfo;
129             mUiAutomationService.mServiceInterface = serviceClient;
130             mUiAutomationService.onAdded();
131             try {
132                 mUiAutomationService.mServiceInterface.asBinder().linkToDeath(mUiAutomationService,
133                         0);
134             } catch (RemoteException re) {
135                 Slog.e(LOG_TAG, "Failed registering death link: " + re);
136                 destroyUiAutomationService();
137                 return;
138             }
139 
140             mUiAutomationService.connectServiceUnknownThread();
141         }
142     }
143 
unregisterUiTestAutomationServiceLocked(IAccessibilityServiceClient serviceClient)144     void unregisterUiTestAutomationServiceLocked(IAccessibilityServiceClient serviceClient) {
145         synchronized (mLock) {
146             if (useAccessibility()
147                     && ((mUiAutomationService == null)
148                     || (serviceClient == null)
149                     || (mUiAutomationService.mServiceInterface == null)
150                     || (serviceClient.asBinder()
151                     != mUiAutomationService.mServiceInterface.asBinder()))) {
152                 throw new IllegalStateException("UiAutomationService " + serviceClient
153                         + " not registered!");
154             }
155             destroyUiAutomationService();
156         }
157     }
158 
sendAccessibilityEventLocked(AccessibilityEvent event)159     void sendAccessibilityEventLocked(AccessibilityEvent event) {
160         if (mUiAutomationService != null) {
161             mUiAutomationService.notifyAccessibilityEvent(event);
162         }
163     }
164 
isUiAutomationRunningLocked()165     boolean isUiAutomationRunningLocked() {
166         return (mUiAutomationService != null || !useAccessibility());
167     }
168 
suppressingAccessibilityServicesLocked()169     boolean suppressingAccessibilityServicesLocked() {
170         return (mUiAutomationService != null || !useAccessibility())
171                 && ((mUiAutomationFlags
172                 & UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES) == 0);
173     }
174 
useAccessibility()175     boolean useAccessibility() {
176         return ((mUiAutomationFlags & UiAutomation.FLAG_DONT_USE_ACCESSIBILITY) == 0);
177     }
178 
isTouchExplorationEnabledLocked()179     boolean isTouchExplorationEnabledLocked() {
180         return (mUiAutomationService != null)
181                 && mUiAutomationService.mRequestTouchExplorationMode;
182     }
183 
canRetrieveInteractiveWindowsLocked()184     boolean canRetrieveInteractiveWindowsLocked() {
185         return (mUiAutomationService != null) && mUiAutomationService.mRetrieveInteractiveWindows;
186     }
187 
getRequestedEventMaskLocked()188     int getRequestedEventMaskLocked() {
189         if (mUiAutomationService == null) return 0;
190         return mUiAutomationService.mEventTypes;
191     }
192 
getRelevantEventTypes()193     int getRelevantEventTypes() {
194         UiAutomationService uiAutomationService;
195         synchronized (mLock) {
196             uiAutomationService = mUiAutomationService;
197         }
198         if (uiAutomationService == null) return 0;
199         return uiAutomationService.getRelevantEventTypes();
200     }
201 
202     @Nullable
getServiceInfo()203     AccessibilityServiceInfo getServiceInfo() {
204         UiAutomationService uiAutomationService;
205         synchronized (mLock) {
206             uiAutomationService = mUiAutomationService;
207         }
208         if (uiAutomationService == null) return null;
209         return uiAutomationService.getServiceInfo();
210     }
211 
dumpUiAutomationService(FileDescriptor fd, final PrintWriter pw, String[] args)212     void dumpUiAutomationService(FileDescriptor fd, final PrintWriter pw, String[] args) {
213         UiAutomationService uiAutomationService;
214         synchronized (mLock) {
215             uiAutomationService = mUiAutomationService;
216         }
217         if (uiAutomationService != null) {
218             uiAutomationService.dump(fd, pw, args);
219         }
220     }
221 
destroyUiAutomationService()222     private void destroyUiAutomationService() {
223         synchronized (mLock) {
224             if (mUiAutomationService != null) {
225                 mUiAutomationService.mServiceInterface.asBinder().unlinkToDeath(
226                         mUiAutomationService, 0);
227                 mUiAutomationService.onRemoved();
228                 mUiAutomationService.resetLocked();
229                 mUiAutomationService = null;
230                 if (mUiAutomationServiceOwner != null) {
231                     mUiAutomationServiceOwner.unlinkToDeath(
232                             mUiAutomationServiceOwnerDeathRecipient, 0);
233                     mUiAutomationServiceOwner = null;
234                 }
235             }
236             mUiAutomationFlags = 0;
237             mSystemSupport.onClientChangeLocked(false);
238         }
239     }
240 
241     private class UiAutomationService extends AbstractAccessibilityServiceConnection {
242         private final Handler mMainHandler;
243 
UiAutomationService(Context context, AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler, Object lock, AccessibilitySecurityPolicy securityPolicy, SystemSupport systemSupport, AccessibilityTrace trace, WindowManagerInternal windowManagerInternal, SystemActionPerformer systemActionPerformer, AccessibilityWindowManager awm)244         UiAutomationService(Context context, AccessibilityServiceInfo accessibilityServiceInfo,
245                 int id, Handler mainHandler, Object lock,
246                 AccessibilitySecurityPolicy securityPolicy,
247                 SystemSupport systemSupport, AccessibilityTrace trace,
248                 WindowManagerInternal windowManagerInternal,
249                 SystemActionPerformer systemActionPerformer, AccessibilityWindowManager awm) {
250             super(context, COMPONENT_NAME, accessibilityServiceInfo, id, mainHandler, lock,
251                     securityPolicy, systemSupport, trace, windowManagerInternal,
252                     systemActionPerformer, awm);
253             mMainHandler = mainHandler;
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
isAccessibilityButtonAvailable()335         public boolean isAccessibilityButtonAvailable() {
336             return false;
337         }
338 
339         @Override
disableSelf()340         public void disableSelf() {}
341 
342         @Override
onServiceConnected(ComponentName componentName, IBinder service)343         public void onServiceConnected(ComponentName componentName, IBinder service) {}
344 
345         @Override
onServiceDisconnected(ComponentName componentName)346         public void onServiceDisconnected(ComponentName componentName) {}
347 
348         @Override
isCapturingFingerprintGestures()349         public boolean isCapturingFingerprintGestures() {
350             return false;
351         }
352 
353         @Override
onFingerprintGestureDetectionActiveChanged(boolean active)354         public void onFingerprintGestureDetectionActiveChanged(boolean active) {}
355 
356         @Override
onFingerprintGesture(int gesture)357         public void onFingerprintGesture(int gesture) {}
358 
359         @Override
takeScreenshot(int displayId, RemoteCallback callback)360         public void takeScreenshot(int displayId, RemoteCallback callback) {}
361     }
362 }
363