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