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