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