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