1 /* 2 * Copyright (C) 2022 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 package com.android.server.accessibility; 17 18 import static android.content.Context.DEVICE_ID_DEFAULT; 19 import static android.content.Context.DEVICE_ID_INVALID; 20 21 import static com.android.internal.util.FunctionalUtils.ignoreRemoteException; 22 23 import android.accessibilityservice.AccessibilityServiceInfo; 24 import android.accessibilityservice.AccessibilityTrace; 25 import android.accessibilityservice.IAccessibilityServiceClient; 26 import android.annotation.NonNull; 27 import android.companion.virtual.VirtualDevice; 28 import android.companion.virtual.VirtualDeviceManager; 29 import android.content.ComponentName; 30 import android.content.Context; 31 import android.hardware.display.DisplayManager; 32 import android.os.Build; 33 import android.os.Handler; 34 import android.os.IBinder; 35 import android.os.RemoteCallbackList; 36 import android.os.RemoteException; 37 import android.util.ArraySet; 38 import android.util.IntArray; 39 import android.util.Log; 40 import android.util.Slog; 41 import android.util.SparseArray; 42 import android.util.SparseIntArray; 43 import android.view.Display; 44 import android.view.accessibility.AccessibilityEvent; 45 import android.view.accessibility.AccessibilityManager; 46 import android.view.accessibility.IAccessibilityManagerClient; 47 48 import com.android.internal.annotations.VisibleForTesting; 49 import com.android.internal.util.IntPair; 50 import com.android.server.LocalServices; 51 import com.android.server.companion.virtual.VirtualDeviceManagerInternal; 52 import com.android.server.wm.WindowManagerInternal; 53 54 import java.io.FileDescriptor; 55 import java.io.PrintWriter; 56 import java.util.ArrayList; 57 import java.util.Arrays; 58 import java.util.List; 59 import java.util.Set; 60 import java.util.function.Consumer; 61 62 /** 63 * Manages proxy connections. 64 * 65 * Currently this acts similarly to UiAutomationManager as a global manager, though ideally each 66 * proxy connection will belong to a separate user state. 67 * 68 * TODO(241117292): Remove or cut down during simultaneous user refactoring. 69 */ 70 public class ProxyManager { 71 private static final String LOG_TAG = "ProxyManager"; 72 private static final boolean DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG) && Build.IS_DEBUGGABLE; 73 74 // Names used to populate ComponentName and ResolveInfo in connection.mA11yServiceInfo and in 75 // the infos of connection.setInstalledAndEnabledServices 76 static final String PROXY_COMPONENT_PACKAGE_NAME = "ProxyPackage"; 77 static final String PROXY_COMPONENT_CLASS_NAME = "ProxyClass"; 78 79 // AMS#mLock 80 private final Object mLock; 81 82 private final Context mContext; 83 private final Handler mMainHandler; 84 85 private final UiAutomationManager mUiAutomationManager; 86 87 // Device Id -> state. Used to determine if we should notify AccessibilityManager clients of 88 // updates. 89 private final SparseIntArray mLastStates = new SparseIntArray(); 90 91 // Each display id entry in a SparseArray represents a proxy a11y user. 92 private final SparseArray<ProxyAccessibilityServiceConnection> mProxyA11yServiceConnections = 93 new SparseArray<>(); 94 95 private final AccessibilityWindowManager mA11yWindowManager; 96 97 private AccessibilityInputFilter mA11yInputFilter; 98 99 private VirtualDeviceManagerInternal mLocalVdm; 100 101 private final SystemSupport mSystemSupport; 102 103 private VirtualDeviceManagerInternal.AppsOnVirtualDeviceListener 104 mAppsOnVirtualDeviceListener; 105 106 private VirtualDeviceManager.VirtualDeviceListener mVirtualDeviceListener; 107 108 /** 109 * Callbacks into AccessibilityManagerService. 110 */ 111 public interface SystemSupport { 112 /** 113 * Removes the device id from tracking. 114 */ removeDeviceIdLocked(int deviceId)115 void removeDeviceIdLocked(int deviceId); 116 117 /** 118 * Updates the windows tracking for the current user. 119 */ updateWindowsForAccessibilityCallbackLocked()120 void updateWindowsForAccessibilityCallbackLocked(); 121 122 /** 123 * Clears all caches. 124 */ notifyClearAccessibilityCacheLocked()125 void notifyClearAccessibilityCacheLocked(); 126 127 /** 128 * Gets the clients for all users. 129 */ 130 @NonNull getGlobalClientsLocked()131 RemoteCallbackList<IAccessibilityManagerClient> getGlobalClientsLocked(); 132 133 /** 134 * Gets the clients for the current user. 135 */ 136 @NonNull getCurrentUserClientsLocked()137 RemoteCallbackList<IAccessibilityManagerClient> getCurrentUserClientsLocked(); 138 } 139 ProxyManager(Object lock, AccessibilityWindowManager awm, Context context, Handler mainHandler, UiAutomationManager uiAutomationManager, SystemSupport systemSupport)140 public ProxyManager(Object lock, AccessibilityWindowManager awm, 141 Context context, Handler mainHandler, UiAutomationManager uiAutomationManager, 142 SystemSupport systemSupport) { 143 mLock = lock; 144 mA11yWindowManager = awm; 145 mContext = context; 146 mMainHandler = mainHandler; 147 mUiAutomationManager = uiAutomationManager; 148 mSystemSupport = systemSupport; 149 mLocalVdm = LocalServices.getService(VirtualDeviceManagerInternal.class); 150 } 151 152 /** 153 * Creates the service connection. 154 */ registerProxy(IAccessibilityServiceClient client, int displayId, int id, AccessibilitySecurityPolicy securityPolicy, AbstractAccessibilityServiceConnection.SystemSupport systemSupport, AccessibilityTrace trace, WindowManagerInternal windowManagerInternal)155 public void registerProxy(IAccessibilityServiceClient client, int displayId, 156 int id, AccessibilitySecurityPolicy securityPolicy, 157 AbstractAccessibilityServiceConnection.SystemSupport systemSupport, 158 AccessibilityTrace trace, 159 WindowManagerInternal windowManagerInternal) throws RemoteException { 160 if (DEBUG) { 161 Slog.v(LOG_TAG, "Register proxy for display id: " + displayId); 162 } 163 164 VirtualDeviceManager vdm = mContext.getSystemService(VirtualDeviceManager.class); 165 if (vdm == null) { 166 return; 167 } 168 final int deviceId = vdm.getDeviceIdForDisplayId(displayId); 169 170 // Set a default AccessibilityServiceInfo that is used before the proxy's info is 171 // populated. A proxy has the touch exploration and window capabilities. 172 AccessibilityServiceInfo info = new AccessibilityServiceInfo(); 173 info.setCapabilities(AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION 174 | AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT); 175 final String componentClassDisplayName = PROXY_COMPONENT_CLASS_NAME + displayId; 176 info.setComponentName(new ComponentName(PROXY_COMPONENT_PACKAGE_NAME, 177 componentClassDisplayName)); 178 ProxyAccessibilityServiceConnection connection = 179 new ProxyAccessibilityServiceConnection(mContext, info.getComponentName(), info, 180 id, mMainHandler, mLock, securityPolicy, systemSupport, trace, 181 windowManagerInternal, 182 mA11yWindowManager, displayId, deviceId); 183 184 synchronized (mLock) { 185 mProxyA11yServiceConnections.put(displayId, connection); 186 if (Flags.proxyUseAppsOnVirtualDeviceListener()) { 187 if (mAppsOnVirtualDeviceListener == null) { 188 mAppsOnVirtualDeviceListener = allRunningUids -> 189 notifyProxyOfRunningAppsChange(allRunningUids); 190 final VirtualDeviceManagerInternal localVdm = getLocalVdm(); 191 if (localVdm != null) { 192 localVdm.registerAppsOnVirtualDeviceListener(mAppsOnVirtualDeviceListener); 193 } 194 } 195 } 196 if (mProxyA11yServiceConnections.size() == 1) { 197 registerVirtualDeviceListener(); 198 } 199 } 200 201 // If the client dies, make sure to remove the connection. 202 IBinder.DeathRecipient deathRecipient = 203 new IBinder.DeathRecipient() { 204 @Override 205 public void binderDied() { 206 client.asBinder().unlinkToDeath(this, 0); 207 clearConnectionAndUpdateState(displayId); 208 } 209 }; 210 client.asBinder().linkToDeath(deathRecipient, 0); 211 212 mMainHandler.post(() -> { 213 if (mA11yInputFilter != null) { 214 mA11yInputFilter.disableFeaturesForDisplayIfInstalled(displayId); 215 } 216 }); 217 connection.initializeServiceInterface(client); 218 } 219 registerVirtualDeviceListener()220 private void registerVirtualDeviceListener() { 221 VirtualDeviceManager vdm = mContext.getSystemService(VirtualDeviceManager.class); 222 if (vdm == null || !android.companion.virtual.flags.Flags.vdmPublicApis()) { 223 return; 224 } 225 if (mVirtualDeviceListener == null) { 226 mVirtualDeviceListener = new VirtualDeviceManager.VirtualDeviceListener() { 227 @Override 228 public void onVirtualDeviceClosed(int deviceId) { 229 clearConnections(deviceId); 230 } 231 }; 232 } 233 234 vdm.registerVirtualDeviceListener(mContext.getMainExecutor(), mVirtualDeviceListener); 235 } 236 unregisterVirtualDeviceListener()237 private void unregisterVirtualDeviceListener() { 238 VirtualDeviceManager vdm = mContext.getSystemService(VirtualDeviceManager.class); 239 if (vdm == null || !android.companion.virtual.flags.Flags.vdmPublicApis()) { 240 return; 241 } 242 vdm.unregisterVirtualDeviceListener(mVirtualDeviceListener); 243 } 244 245 /** 246 * Unregister the proxy based on display id. 247 */ unregisterProxy(int displayId)248 public boolean unregisterProxy(int displayId) { 249 return clearConnectionAndUpdateState(displayId); 250 } 251 252 /** 253 * Clears all proxy connections belonging to {@code deviceId}. 254 */ clearConnections(int deviceId)255 public void clearConnections(int deviceId) { 256 final IntArray displaysToClear = new IntArray(); 257 synchronized (mLock) { 258 for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) { 259 final ProxyAccessibilityServiceConnection proxy = 260 mProxyA11yServiceConnections.valueAt(i); 261 if (proxy != null && proxy.getDeviceId() == deviceId) { 262 displaysToClear.add(proxy.getDisplayId()); 263 } 264 } 265 } 266 for (int i = 0; i < displaysToClear.size(); i++) { 267 clearConnectionAndUpdateState(displaysToClear.get(i)); 268 } 269 } 270 271 /** 272 * Removes the system connection of an AccessibilityDisplayProxy. 273 * 274 * This will: 275 * <ul> 276 * <li> Reset Clients to belong to the default device if appropriate. 277 * <li> Stop identifying the display's a11y windows as belonging to a proxy. 278 * <li> Re-enable any input filters for the display. 279 * <li> Notify AMS that a proxy has been removed. 280 * </ul> 281 * 282 * @param displayId the display id of the connection to be cleared. 283 * @return whether the proxy was removed. 284 */ clearConnectionAndUpdateState(int displayId)285 private boolean clearConnectionAndUpdateState(int displayId) { 286 boolean removedFromConnections = false; 287 int deviceId = DEVICE_ID_INVALID; 288 synchronized (mLock) { 289 if (mProxyA11yServiceConnections.contains(displayId)) { 290 deviceId = mProxyA11yServiceConnections.get(displayId).getDeviceId(); 291 mProxyA11yServiceConnections.remove(displayId); 292 removedFromConnections = true; 293 if (mProxyA11yServiceConnections.size() == 0) { 294 unregisterVirtualDeviceListener(); 295 } 296 } 297 } 298 299 if (removedFromConnections) { 300 updateStateForRemovedDisplay(displayId, deviceId); 301 } 302 303 if (DEBUG) { 304 Slog.v(LOG_TAG, "Unregistered proxy for display id " + displayId + ": " 305 + removedFromConnections); 306 } 307 return removedFromConnections; 308 } 309 310 /** 311 * When the connection is removed from tracking in ProxyManager, propagate changes to other a11y 312 * system components like the input filter and IAccessibilityManagerClients. 313 */ updateStateForRemovedDisplay(int displayId, int deviceId)314 private void updateStateForRemovedDisplay(int displayId, int deviceId) { 315 mA11yWindowManager.stopTrackingDisplayProxy(displayId); 316 // A11yInputFilter isn't thread-safe, so post on the system thread. 317 mMainHandler.post( 318 () -> { 319 if (mA11yInputFilter != null) { 320 final DisplayManager displayManager = (DisplayManager) 321 mContext.getSystemService(Context.DISPLAY_SERVICE); 322 final Display proxyDisplay = displayManager.getDisplay(displayId); 323 if (proxyDisplay != null) { 324 // A11yInputFilter isn't thread-safe, so post on the system thread. 325 mA11yInputFilter.enableFeaturesForDisplayIfInstalled(proxyDisplay); 326 } 327 } 328 }); 329 // If there isn't an existing proxy for the device id, reset app clients. Resetting 330 // will usually happen, since in most cases there will only be one proxy for a 331 // device. 332 if (!isProxyedDeviceId(deviceId)) { 333 synchronized (mLock) { 334 if (Flags.proxyUseAppsOnVirtualDeviceListener()) { 335 if (mProxyA11yServiceConnections.size() == 0) { 336 final VirtualDeviceManagerInternal localVdm = getLocalVdm(); 337 if (localVdm != null && mAppsOnVirtualDeviceListener != null) { 338 localVdm.unregisterAppsOnVirtualDeviceListener( 339 mAppsOnVirtualDeviceListener); 340 mAppsOnVirtualDeviceListener = null; 341 } 342 } 343 } 344 mSystemSupport.removeDeviceIdLocked(deviceId); 345 mLastStates.delete(deviceId); 346 } 347 } else { 348 // Update with the states of the remaining proxies. 349 onProxyChanged(deviceId); 350 } 351 } 352 353 /** 354 * Returns {@code true} if {@code displayId} is being proxy-ed. 355 */ isProxyedDisplay(int displayId)356 public boolean isProxyedDisplay(int displayId) { 357 synchronized (mLock) { 358 final boolean tracked = mProxyA11yServiceConnections.contains(displayId); 359 if (DEBUG) { 360 Slog.v(LOG_TAG, "Tracking proxy display " + displayId + " : " + tracked); 361 } 362 return tracked; 363 } 364 } 365 366 /** 367 * Returns {@code true} if {@code deviceId} is being proxy-ed. 368 */ isProxyedDeviceId(int deviceId)369 public boolean isProxyedDeviceId(int deviceId) { 370 if (deviceId == DEVICE_ID_DEFAULT || deviceId == DEVICE_ID_INVALID) { 371 return false; 372 } 373 boolean isTrackingDeviceId; 374 synchronized (mLock) { 375 isTrackingDeviceId = getFirstProxyForDeviceIdLocked(deviceId) != null; 376 } 377 if (DEBUG) { 378 Slog.v(LOG_TAG, "Tracking device " + deviceId + " : " + isTrackingDeviceId); 379 } 380 return isTrackingDeviceId; 381 } 382 383 /** Returns true if the display belongs to one of the caller's virtual devices. */ displayBelongsToCaller(int callingUid, int proxyDisplayId)384 public boolean displayBelongsToCaller(int callingUid, int proxyDisplayId) { 385 final VirtualDeviceManager vdm = mContext.getSystemService(VirtualDeviceManager.class); 386 final VirtualDeviceManagerInternal localVdm = getLocalVdm(); 387 if (vdm == null || localVdm == null) { 388 return false; 389 } 390 final List<VirtualDevice> virtualDevices = vdm.getVirtualDevices(); 391 for (VirtualDevice device : virtualDevices) { 392 if (localVdm.getDisplayIdsForDevice(device.getDeviceId()).contains(proxyDisplayId)) { 393 final int ownerUid = localVdm.getDeviceOwnerUid(device.getDeviceId()); 394 if (callingUid == ownerUid) { 395 return true; 396 } 397 } 398 } 399 return false; 400 } 401 402 /** 403 * Sends AccessibilityEvents to a proxy given the event's displayId. 404 */ sendAccessibilityEventLocked(AccessibilityEvent event)405 public void sendAccessibilityEventLocked(AccessibilityEvent event) { 406 final ProxyAccessibilityServiceConnection proxy = 407 mProxyA11yServiceConnections.get(event.getDisplayId()); 408 if (proxy != null) { 409 if (DEBUG) { 410 Slog.v(LOG_TAG, "Send proxy event " + event + " for display id " 411 + event.getDisplayId()); 412 } 413 proxy.notifyAccessibilityEvent(event); 414 } 415 } 416 417 /** 418 * Returns {@code true} if any proxy can retrieve windows. 419 * TODO(b/250929565): Retrieve per connection/user state. 420 */ canRetrieveInteractiveWindowsLocked()421 public boolean canRetrieveInteractiveWindowsLocked() { 422 boolean observingWindows = false; 423 for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) { 424 final ProxyAccessibilityServiceConnection proxy = 425 mProxyA11yServiceConnections.valueAt(i); 426 if (proxy.mRetrieveInteractiveWindows) { 427 observingWindows = true; 428 break; 429 } 430 } 431 if (DEBUG) { 432 Slog.v(LOG_TAG, "At least one proxy can retrieve windows: " + observingWindows); 433 } 434 return observingWindows; 435 } 436 437 /** 438 * If there is at least one proxy, accessibility is enabled. 439 */ getStateLocked(int deviceId)440 public int getStateLocked(int deviceId) { 441 int clientState = 0; 442 final boolean uiAutomationCanIntrospect = mUiAutomationManager.canIntrospect(); 443 if (uiAutomationCanIntrospect) { 444 clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED; 445 } 446 for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) { 447 final ProxyAccessibilityServiceConnection proxy = 448 mProxyA11yServiceConnections.valueAt(i); 449 if (proxy != null && proxy.getDeviceId() == deviceId) { 450 // Combine proxy states. 451 clientState |= getStateForDisplayIdLocked(proxy); 452 } 453 } 454 455 if (DEBUG) { 456 Slog.v(LOG_TAG, "For device id " + deviceId + " a11y is enabled: " 457 + ((clientState & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0)); 458 Slog.v(LOG_TAG, "For device id " + deviceId + " touch exploration is enabled: " 459 + ((clientState & AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED) 460 != 0)); 461 } 462 return clientState; 463 } 464 465 /** 466 * If there is at least one proxy, accessibility is enabled. 467 */ getStateForDisplayIdLocked(ProxyAccessibilityServiceConnection proxy)468 private int getStateForDisplayIdLocked(ProxyAccessibilityServiceConnection proxy) { 469 int clientState = 0; 470 if (proxy != null) { 471 clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED; 472 if (proxy.mRequestTouchExplorationMode) { 473 clientState |= AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED; 474 } 475 } 476 477 if (DEBUG) { 478 Slog.v(LOG_TAG, "Accessibility is enabled for all proxies: " 479 + ((clientState & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0)); 480 Slog.v(LOG_TAG, "Touch exploration is enabled for all proxies: " 481 + ((clientState & AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED) 482 != 0)); 483 } 484 return clientState; 485 } 486 487 /** 488 * Gets the last state for a device. 489 */ getLastSentStateLocked(int deviceId)490 private int getLastSentStateLocked(int deviceId) { 491 return mLastStates.get(deviceId, 0); 492 } 493 494 /** 495 * Sets the last state for a device. 496 */ setLastStateLocked(int deviceId, int proxyState)497 private void setLastStateLocked(int deviceId, int proxyState) { 498 mLastStates.put(deviceId, proxyState); 499 } 500 501 /** 502 * Updates the relevant event types of the app clients that are shown on a display owned by the 503 * specified device. 504 * 505 * A client belongs to a device id, so event types (and other state) is determined by the device 506 * id. In most cases, a device owns a single display. But if multiple displays may belong to one 507 * Virtual Device, the app clients will get the aggregated event types for all proxy-ed displays 508 * belonging to a VirtualDevice. 509 */ updateRelevantEventTypesLocked(int deviceId)510 private void updateRelevantEventTypesLocked(int deviceId) { 511 if (!isProxyedDeviceId(deviceId)) { 512 return; 513 } 514 mMainHandler.post(() -> { 515 synchronized (mLock) { 516 broadcastToClientsLocked(ignoreRemoteException(client -> { 517 int relevantEventTypes; 518 if (client.mDeviceId == deviceId) { 519 relevantEventTypes = computeRelevantEventTypesLocked(client); 520 if (client.mLastSentRelevantEventTypes != relevantEventTypes) { 521 client.mLastSentRelevantEventTypes = relevantEventTypes; 522 client.mCallback.setRelevantEventTypes(relevantEventTypes); 523 } 524 } 525 })); 526 } 527 }); 528 } 529 530 /** 531 * Returns the relevant event types for a Client. 532 */ computeRelevantEventTypesLocked(AccessibilityManagerService.Client client)533 public int computeRelevantEventTypesLocked(AccessibilityManagerService.Client client) { 534 int relevantEventTypes = 0; 535 for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) { 536 final ProxyAccessibilityServiceConnection proxy = 537 mProxyA11yServiceConnections.valueAt(i); 538 if (proxy != null && proxy.getDeviceId() == client.mDeviceId) { 539 relevantEventTypes |= proxy.getRelevantEventTypes(); 540 relevantEventTypes |= AccessibilityManagerService.isClientInPackageAllowlist( 541 mUiAutomationManager.getServiceInfo(), client) 542 ? mUiAutomationManager.getRelevantEventTypes() 543 : 0; 544 } 545 } 546 if (DEBUG) { 547 Slog.v(LOG_TAG, "Relevant event types for device id " + client.mDeviceId 548 + ": " + AccessibilityEvent.eventTypeToString(relevantEventTypes)); 549 } 550 return relevantEventTypes; 551 } 552 553 /** 554 * Adds the service interfaces to a list. 555 * @param interfaces the list to add to. 556 * @param deviceId the device id of the interested app client. 557 */ addServiceInterfacesLocked(@onNull List<IAccessibilityServiceClient> interfaces, int deviceId)558 public void addServiceInterfacesLocked(@NonNull List<IAccessibilityServiceClient> interfaces, 559 int deviceId) { 560 for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) { 561 final ProxyAccessibilityServiceConnection proxy = 562 mProxyA11yServiceConnections.valueAt(i); 563 if (proxy != null && proxy.getDeviceId() == deviceId) { 564 final IBinder proxyBinder = proxy.mService; 565 final IAccessibilityServiceClient proxyInterface = proxy.mServiceInterface; 566 if ((proxyBinder != null) && (proxyInterface != null)) { 567 interfaces.add(proxyInterface); 568 } 569 } 570 } 571 } 572 573 /** 574 * Gets the list of installed and enabled services for a device id. 575 * 576 * Note: Multiple display proxies may belong to the same device. 577 */ getInstalledAndEnabledServiceInfosLocked(int feedbackType, int deviceId)578 public List<AccessibilityServiceInfo> getInstalledAndEnabledServiceInfosLocked(int feedbackType, 579 int deviceId) { 580 List<AccessibilityServiceInfo> serviceInfos = new ArrayList<>(); 581 for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) { 582 final ProxyAccessibilityServiceConnection proxy = 583 mProxyA11yServiceConnections.valueAt(i); 584 if (proxy != null && proxy.getDeviceId() == deviceId) { 585 // Return all proxy infos for ALL mask. 586 if (feedbackType == AccessibilityServiceInfo.FEEDBACK_ALL_MASK) { 587 serviceInfos.addAll(proxy.getInstalledAndEnabledServices()); 588 } else if ((proxy.mFeedbackType & feedbackType) != 0) { 589 List<AccessibilityServiceInfo> proxyInfos = 590 proxy.getInstalledAndEnabledServices(); 591 // Iterate through each info in the proxy. 592 for (AccessibilityServiceInfo info : proxyInfos) { 593 if ((info.feedbackType & feedbackType) != 0) { 594 serviceInfos.add(info); 595 } 596 } 597 } 598 } 599 } 600 return serviceInfos; 601 } 602 603 /** 604 * Handles proxy changes. 605 * 606 * <p> 607 * Changes include if the proxy is unregistered, its service info list has 608 * changed, or its focus appearance has changed. 609 * <p> 610 * Some responses may include updating app clients. A client belongs to a device id, so state is 611 * determined by the device id. In most cases, a device owns a single display. But if multiple 612 * displays belong to one Virtual Device, the app clients will get a difference in 613 * behavior depending on what is being updated. 614 * 615 * The following state methods are updated for AccessibilityManager clients belonging to a 616 * proxied device: 617 * <ul> 618 * <li> A11yManager#setRelevantEventTypes - The combined event types of all proxies belonging to 619 * a device id. 620 * <li> A11yManager#setState - The combined states of all proxies belonging to a device id. 621 * <li> A11yManager#notifyServicesStateChanged(timeout) - The highest of all proxies belonging 622 * to a device id. 623 * <li> A11yManager#setFocusAppearance - The appearance of the most recently updated display id 624 * belonging to the device. 625 * </ul> 626 * This is similar to onUserStateChangeLocked and onClientChangeLocked, but does not require an 627 * A11yUserState and only checks proxy-relevant settings. 628 */ onProxyChanged(int deviceId, boolean forceUpdate)629 private void onProxyChanged(int deviceId, boolean forceUpdate) { 630 if (DEBUG) { 631 Slog.v(LOG_TAG, "onProxyChanged called for deviceId: " + deviceId); 632 } 633 //The following state updates are excluded: 634 // - Input-related state 635 // - Primary-device / hardware-specific state 636 synchronized (mLock) { 637 // A proxy may be registered after the client has been initialized in #addClient. 638 // For example, a user does not turn on accessibility until after the app has launched. 639 // Or the process was started with a default id context and should shift to a device. 640 // Update device ids of the clients if necessary. 641 updateDeviceIdsIfNeededLocked(deviceId); 642 // Start tracking of all displays if necessary. 643 mSystemSupport.updateWindowsForAccessibilityCallbackLocked(); 644 // Calls A11yManager#setRelevantEventTypes (test these) 645 updateRelevantEventTypesLocked(deviceId); 646 // Calls A11yManager#setState 647 scheduleUpdateProxyClientsIfNeededLocked(deviceId, forceUpdate); 648 //Calls A11yManager#notifyServicesStateChanged(timeout) 649 scheduleNotifyProxyClientsOfServicesStateChangeLocked(deviceId); 650 // Calls A11yManager#setFocusAppearance 651 updateFocusAppearanceLocked(deviceId); 652 mSystemSupport.notifyClearAccessibilityCacheLocked(); 653 } 654 } 655 656 /** 657 * Handles proxy changes, but does not force an update of app clients. 658 */ onProxyChanged(int deviceId)659 public void onProxyChanged(int deviceId) { 660 onProxyChanged(deviceId, false); 661 } 662 663 /** 664 * Updates the states of the app AccessibilityManagers. 665 */ scheduleUpdateProxyClientsIfNeededLocked(int deviceId, boolean forceUpdate)666 private void scheduleUpdateProxyClientsIfNeededLocked(int deviceId, boolean forceUpdate) { 667 final int proxyState = getStateLocked(deviceId); 668 if (DEBUG) { 669 Slog.v(LOG_TAG, "State for device id " + deviceId + " is " + proxyState); 670 Slog.v(LOG_TAG, "Last state for device id " + deviceId + " is " 671 + getLastSentStateLocked(deviceId)); 672 Slog.v(LOG_TAG, "force update: " + forceUpdate); 673 } 674 if ((getLastSentStateLocked(deviceId)) != proxyState 675 || (Flags.proxyUseAppsOnVirtualDeviceListener() && forceUpdate)) { 676 setLastStateLocked(deviceId, proxyState); 677 mMainHandler.post(() -> { 678 synchronized (mLock) { 679 broadcastToClientsLocked(ignoreRemoteException(client -> { 680 if (client.mDeviceId == deviceId) { 681 client.mCallback.setState(proxyState); 682 } 683 })); 684 } 685 }); 686 } 687 } 688 689 /** 690 * Notifies AccessibilityManager of services state changes, which includes changes to the 691 * list of service infos and timeouts. 692 * 693 * @see AccessibilityManager.AccessibilityServicesStateChangeListener 694 */ scheduleNotifyProxyClientsOfServicesStateChangeLocked(int deviceId)695 private void scheduleNotifyProxyClientsOfServicesStateChangeLocked(int deviceId) { 696 if (DEBUG) { 697 Slog.v(LOG_TAG, "Notify services state change at device id " + deviceId); 698 } 699 mMainHandler.post(()-> { 700 broadcastToClientsLocked(ignoreRemoteException(client -> { 701 if (client.mDeviceId == deviceId) { 702 synchronized (mLock) { 703 client.mCallback.notifyServicesStateChanged( 704 getRecommendedTimeoutMillisLocked(deviceId)); 705 } 706 } 707 })); 708 }); 709 } 710 711 /** 712 * Updates the focus appearance of AccessibilityManagerClients. 713 */ updateFocusAppearanceLocked(int deviceId)714 private void updateFocusAppearanceLocked(int deviceId) { 715 if (DEBUG) { 716 Slog.v(LOG_TAG, "Update proxy focus appearance at device id " + deviceId); 717 } 718 // Reasonably assume that all proxies belonging to a virtual device should have the 719 // same focus appearance, and if they should be different these should belong to different 720 // virtual devices. 721 final ProxyAccessibilityServiceConnection proxy = getFirstProxyForDeviceIdLocked(deviceId); 722 if (proxy != null) { 723 mMainHandler.post(()-> { 724 broadcastToClientsLocked(ignoreRemoteException(client -> { 725 if (client.mDeviceId == proxy.getDeviceId()) { 726 client.mCallback.setFocusAppearance( 727 proxy.getFocusStrokeWidthLocked(), 728 proxy.getFocusColorLocked()); 729 } 730 })); 731 }); 732 } 733 } 734 getFirstProxyForDeviceIdLocked(int deviceId)735 private ProxyAccessibilityServiceConnection getFirstProxyForDeviceIdLocked(int deviceId) { 736 for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) { 737 final ProxyAccessibilityServiceConnection proxy = 738 mProxyA11yServiceConnections.valueAt(i); 739 if (proxy != null && proxy.getDeviceId() == deviceId) { 740 return proxy; 741 } 742 } 743 return null; 744 } 745 broadcastToClientsLocked( @onNull Consumer<AccessibilityManagerService.Client> clientAction)746 private void broadcastToClientsLocked( 747 @NonNull Consumer<AccessibilityManagerService.Client> clientAction) { 748 final RemoteCallbackList<IAccessibilityManagerClient> userClients = 749 mSystemSupport.getCurrentUserClientsLocked(); 750 final RemoteCallbackList<IAccessibilityManagerClient> globalClients = 751 mSystemSupport.getGlobalClientsLocked(); 752 userClients.broadcastForEachCookie(clientAction); 753 globalClients.broadcastForEachCookie(clientAction); 754 } 755 756 /** 757 * Updates the timeout and notifies app clients. 758 * 759 * For real users, timeouts are tracked in A11yUserState. For proxies, timeouts are in the 760 * service connection. The value in user state is preferred, but if this value is 0 the service 761 * info value is used. 762 * 763 * This follows the pattern in readUserRecommendedUiTimeoutSettingsLocked. 764 * 765 * TODO(b/250929565): ProxyUserState or similar should hold the timeouts 766 */ updateTimeoutsIfNeeded(int nonInteractiveUiTimeout, int interactiveUiTimeout)767 public void updateTimeoutsIfNeeded(int nonInteractiveUiTimeout, int interactiveUiTimeout) { 768 synchronized (mLock) { 769 for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) { 770 final ProxyAccessibilityServiceConnection proxy = 771 mProxyA11yServiceConnections.valueAt(i); 772 if (proxy != null) { 773 if (proxy.updateTimeouts(nonInteractiveUiTimeout, interactiveUiTimeout)) { 774 scheduleNotifyProxyClientsOfServicesStateChangeLocked(proxy.getDeviceId()); 775 } 776 } 777 } 778 } 779 } 780 781 /** 782 * Gets the recommended timeout belonging to a Virtual Device. 783 * 784 * This is the highest of all display proxies belonging to the virtual device. 785 */ getRecommendedTimeoutMillisLocked(int deviceId)786 public long getRecommendedTimeoutMillisLocked(int deviceId) { 787 int combinedInteractiveTimeout = 0; 788 int combinedNonInteractiveTimeout = 0; 789 for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) { 790 final ProxyAccessibilityServiceConnection proxy = 791 mProxyA11yServiceConnections.valueAt(i); 792 if (proxy != null && proxy.getDeviceId() == deviceId) { 793 final int proxyInteractiveUiTimeout = 794 (proxy != null) ? proxy.getInteractiveTimeout() : 0; 795 final int nonInteractiveUiTimeout = 796 (proxy != null) ? proxy.getNonInteractiveTimeout() : 0; 797 combinedInteractiveTimeout = Math.max(proxyInteractiveUiTimeout, 798 combinedInteractiveTimeout); 799 combinedNonInteractiveTimeout = Math.max(nonInteractiveUiTimeout, 800 combinedNonInteractiveTimeout); 801 } 802 } 803 return IntPair.of(combinedInteractiveTimeout, combinedNonInteractiveTimeout); 804 } 805 806 /** 807 * Gets the first focus stroke width belonging to the device. 808 */ getFocusStrokeWidthLocked(int deviceId)809 public int getFocusStrokeWidthLocked(int deviceId) { 810 final ProxyAccessibilityServiceConnection proxy = getFirstProxyForDeviceIdLocked(deviceId); 811 if (proxy != null) { 812 return proxy.getFocusStrokeWidthLocked(); 813 } 814 return 0; 815 816 } 817 818 /** 819 * Gets the first focus color belonging to the device. 820 */ getFocusColorLocked(int deviceId)821 public int getFocusColorLocked(int deviceId) { 822 final ProxyAccessibilityServiceConnection proxy = getFirstProxyForDeviceIdLocked(deviceId); 823 if (proxy != null) { 824 return proxy.getFocusColorLocked(); 825 } 826 return 0; 827 } 828 829 /** 830 * Returns the first device id given a UID. 831 * @param callingUid the UID to check. 832 * @return the first matching device id, or DEVICE_ID_INVALID. 833 */ getFirstDeviceIdForUidLocked(int callingUid)834 public int getFirstDeviceIdForUidLocked(int callingUid) { 835 int firstDeviceId = DEVICE_ID_INVALID; 836 final VirtualDeviceManagerInternal localVdm = getLocalVdm(); 837 if (localVdm == null) { 838 return firstDeviceId; 839 } 840 final Set<Integer> deviceIds = localVdm.getDeviceIdsForUid(callingUid); 841 for (Integer uidDeviceId : deviceIds) { 842 if (uidDeviceId != DEVICE_ID_DEFAULT && uidDeviceId != DEVICE_ID_INVALID) { 843 firstDeviceId = uidDeviceId; 844 break; 845 } 846 } 847 return firstDeviceId; 848 } 849 850 /** 851 * Sets a Client device id if the app uid belongs to the virtual device. 852 */ updateDeviceIdsIfNeededLocked(int deviceId)853 private void updateDeviceIdsIfNeededLocked(int deviceId) { 854 final RemoteCallbackList<IAccessibilityManagerClient> userClients = 855 mSystemSupport.getCurrentUserClientsLocked(); 856 final RemoteCallbackList<IAccessibilityManagerClient> globalClients = 857 mSystemSupport.getGlobalClientsLocked(); 858 859 updateDeviceIdsIfNeededLocked(deviceId, userClients); 860 updateDeviceIdsIfNeededLocked(deviceId, globalClients); 861 } 862 863 /** 864 * Updates the device ids of IAccessibilityManagerClients if needed after a proxy change. 865 */ updateDeviceIdsIfNeededLocked(int deviceId, @NonNull RemoteCallbackList<IAccessibilityManagerClient> clients)866 private void updateDeviceIdsIfNeededLocked(int deviceId, 867 @NonNull RemoteCallbackList<IAccessibilityManagerClient> clients) { 868 final VirtualDeviceManagerInternal localVdm = getLocalVdm(); 869 if (localVdm == null) { 870 return; 871 } 872 873 for (int i = 0; i < clients.getRegisteredCallbackCount(); i++) { 874 final AccessibilityManagerService.Client client = 875 ((AccessibilityManagerService.Client) clients.getRegisteredCallbackCookie(i)); 876 if (Flags.proxyUseAppsOnVirtualDeviceListener()) { 877 if (deviceId == DEVICE_ID_DEFAULT || deviceId == DEVICE_ID_INVALID) { 878 continue; 879 } 880 boolean uidBelongsToDevice = 881 localVdm.getDeviceIdsForUid(client.mUid).contains(deviceId); 882 if (client.mDeviceId != deviceId && uidBelongsToDevice) { 883 if (DEBUG) { 884 Slog.v(LOG_TAG, "Packages moved to device id " + deviceId + " are " 885 + Arrays.toString(client.mPackageNames)); 886 } 887 client.mDeviceId = deviceId; 888 } else if (client.mDeviceId == deviceId && !uidBelongsToDevice) { 889 client.mDeviceId = DEVICE_ID_DEFAULT; 890 if (DEBUG) { 891 Slog.v(LOG_TAG, "Packages moved to the default device from device id " 892 + deviceId + " are " + Arrays.toString(client.mPackageNames)); 893 } 894 } 895 } else { 896 if (deviceId != DEVICE_ID_DEFAULT && deviceId != DEVICE_ID_INVALID 897 && localVdm.getDeviceIdsForUid(client.mUid).contains(deviceId)) { 898 if (DEBUG) { 899 Slog.v(LOG_TAG, "Packages moved to device id " + deviceId + " are " 900 + Arrays.toString(client.mPackageNames)); 901 } 902 client.mDeviceId = deviceId; 903 } 904 } 905 } 906 } 907 908 @VisibleForTesting notifyProxyOfRunningAppsChange(Set<Integer> allRunningUids)909 void notifyProxyOfRunningAppsChange(Set<Integer> allRunningUids) { 910 if (DEBUG) { 911 Slog.v(LOG_TAG, "notifyProxyOfRunningAppsChange: " + allRunningUids); 912 } 913 synchronized (mLock) { 914 if (mProxyA11yServiceConnections.size() == 0) { 915 return; 916 } 917 final VirtualDeviceManagerInternal localVdm = getLocalVdm(); 918 if (localVdm == null) { 919 return; 920 } 921 final ArraySet<Integer> deviceIdsToUpdate = new ArraySet<>(); 922 for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) { 923 final ProxyAccessibilityServiceConnection proxy = 924 mProxyA11yServiceConnections.valueAt(i); 925 if (proxy != null) { 926 final int proxyDeviceId = proxy.getDeviceId(); 927 for (Integer uid : allRunningUids) { 928 if (localVdm.getDeviceIdsForUid(uid).contains(proxyDeviceId)) { 929 deviceIdsToUpdate.add(proxyDeviceId); 930 } 931 } 932 } 933 } 934 for (Integer proxyDeviceId : deviceIdsToUpdate) { 935 onProxyChanged(proxyDeviceId, true); 936 } 937 } 938 } 939 940 /** 941 * Clears all proxy caches. 942 */ clearCacheLocked()943 public void clearCacheLocked() { 944 for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) { 945 final ProxyAccessibilityServiceConnection proxy = 946 mProxyA11yServiceConnections.valueAt(i); 947 proxy.notifyClearAccessibilityNodeInfoCache(); 948 } 949 } 950 951 /** 952 * Sets the input filter for enabling and disabling features for proxy displays. 953 */ setAccessibilityInputFilter(AccessibilityInputFilter filter)954 public void setAccessibilityInputFilter(AccessibilityInputFilter filter) { 955 if (DEBUG) { 956 Slog.v(LOG_TAG, "Set proxy input filter to " + filter); 957 } 958 mA11yInputFilter = filter; 959 } 960 getLocalVdm()961 private VirtualDeviceManagerInternal getLocalVdm() { 962 if (mLocalVdm == null) { 963 mLocalVdm = LocalServices.getService(VirtualDeviceManagerInternal.class); 964 } 965 return mLocalVdm; 966 } 967 968 @VisibleForTesting setLocalVirtualDeviceManager(VirtualDeviceManagerInternal localVdm)969 void setLocalVirtualDeviceManager(VirtualDeviceManagerInternal localVdm) { 970 mLocalVdm = localVdm; 971 } 972 973 /** 974 * Prints information belonging to each display that is controlled by an 975 * AccessibilityDisplayProxy. 976 */ dump(FileDescriptor fd, PrintWriter pw, String[] args)977 void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 978 synchronized (mLock) { 979 pw.println(); 980 pw.println("Proxy manager state:"); 981 pw.println(" Number of proxy connections: " + mProxyA11yServiceConnections.size()); 982 pw.println(" Registered proxy connections:"); 983 final RemoteCallbackList<IAccessibilityManagerClient> userClients = 984 mSystemSupport.getCurrentUserClientsLocked(); 985 final RemoteCallbackList<IAccessibilityManagerClient> globalClients = 986 mSystemSupport.getGlobalClientsLocked(); 987 for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) { 988 final ProxyAccessibilityServiceConnection proxy = 989 mProxyA11yServiceConnections.valueAt(i); 990 if (proxy != null) { 991 proxy.dump(fd, pw, args); 992 } 993 pw.println(); 994 pw.println(" User clients for proxy's virtual device id"); 995 printClientsForDeviceId(pw, userClients, proxy.getDeviceId()); 996 pw.println(); 997 pw.println(" Global clients for proxy's virtual device id"); 998 printClientsForDeviceId(pw, globalClients, proxy.getDeviceId()); 999 1000 } 1001 } 1002 } 1003 printClientsForDeviceId(PrintWriter pw, RemoteCallbackList<IAccessibilityManagerClient> clients, int deviceId)1004 private void printClientsForDeviceId(PrintWriter pw, 1005 RemoteCallbackList<IAccessibilityManagerClient> clients, int deviceId) { 1006 if (clients != null) { 1007 for (int j = 0; j < clients.getRegisteredCallbackCount(); j++) { 1008 final AccessibilityManagerService.Client client = 1009 (AccessibilityManagerService.Client) 1010 clients.getRegisteredCallbackCookie(j); 1011 if (client.mDeviceId == deviceId) { 1012 pw.println(" " + Arrays.toString(client.mPackageNames) + "\n"); 1013 } 1014 } 1015 } 1016 } 1017 } 1018