Merge "Support separating A11yManagers by device id" into udc-dev

This commit is contained in:
Sally Yuen
2023-04-03 16:39:57 +00:00
committed by Android (Google) Code Review
5 changed files with 900 additions and 160 deletions

View File

@@ -271,6 +271,14 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
*/ */
void onClientChangeLocked(boolean serviceInfoChanged); void onClientChangeLocked(boolean serviceInfoChanged);
/**
* Called back to notify the system the proxy client for a device has changed.
*
* Changes include if the proxy is unregistered, if its service info list has changed, or if
* its focus appearance has changed.
*/
void onProxyChanged(int deviceId);
int getCurrentUserIdLocked(); int getCurrentUserIdLocked();
Pair<float[], MagnificationSpec> getWindowTransformationMatrixAndMagnificationSpec( Pair<float[], MagnificationSpec> getWindowTransformationMatrixAndMagnificationSpec(
@@ -315,8 +323,6 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
void attachAccessibilityOverlayToDisplay(int displayId, SurfaceControl sc); void attachAccessibilityOverlayToDisplay(int displayId, SurfaceControl sc);
void setCurrentUserFocusAppearance(int strokeWidth, int color);
} }
public AbstractAccessibilityServiceConnection(Context context, ComponentName componentName, public AbstractAccessibilityServiceConnection(Context context, ComponentName componentName,

View File

@@ -25,6 +25,9 @@ import static android.accessibilityservice.AccessibilityTrace.FLAGS_PACKAGE_BROA
import static android.accessibilityservice.AccessibilityTrace.FLAGS_USER_BROADCAST_RECEIVER; import static android.accessibilityservice.AccessibilityTrace.FLAGS_USER_BROADCAST_RECEIVER;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION; import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL; import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
import static android.companion.virtual.VirtualDeviceManager.ACTION_VIRTUAL_DEVICE_REMOVED;
import static android.companion.virtual.VirtualDeviceManager.EXTRA_VIRTUAL_DEVICE_ID;
import static android.content.Context.DEVICE_ID_DEFAULT;
import static android.provider.Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED; import static android.provider.Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED;
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON; import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY; import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
@@ -189,7 +192,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
AccessibilityUserState.ServiceInfoChangeListener, AccessibilityUserState.ServiceInfoChangeListener,
AccessibilityWindowManager.AccessibilityEventSender, AccessibilityWindowManager.AccessibilityEventSender,
AccessibilitySecurityPolicy.AccessibilityUserManager, AccessibilitySecurityPolicy.AccessibilityUserManager,
SystemActionPerformer.SystemActionsChangedListener { SystemActionPerformer.SystemActionsChangedListener, ProxyManager.SystemSupport{
private static final boolean DEBUG = false; private static final boolean DEBUG = false;
@@ -471,7 +474,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
new MagnificationScaleProvider(mContext)); new MagnificationScaleProvider(mContext));
mMagnificationProcessor = new MagnificationProcessor(mMagnificationController); mMagnificationProcessor = new MagnificationProcessor(mMagnificationController);
mCaptioningManagerImpl = new CaptioningManagerImpl(mContext); mCaptioningManagerImpl = new CaptioningManagerImpl(mContext);
mProxyManager = new ProxyManager(mLock, mA11yWindowManager, mContext); mProxyManager = new ProxyManager(mLock, mA11yWindowManager, mContext, mMainHandler,
mUiAutomationManager, this);
mFlashNotificationsController = new FlashNotificationsController(mContext); mFlashNotificationsController = new FlashNotificationsController(mContext);
init(); init();
} }
@@ -862,6 +866,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}; };
mContext.registerReceiverAsUser(receiver, UserHandle.ALL, filter, null, mMainHandler, mContext.registerReceiverAsUser(receiver, UserHandle.ALL, filter, null, mMainHandler,
Context.RECEIVER_EXPORTED); Context.RECEIVER_EXPORTED);
final BroadcastReceiver virtualDeviceReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final int deviceId = intent.getIntExtra(
EXTRA_VIRTUAL_DEVICE_ID, DEVICE_ID_DEFAULT);
mProxyManager.clearConnections(deviceId);
}
};
final IntentFilter virtualDeviceFilter = new IntentFilter(ACTION_VIRTUAL_DEVICE_REMOVED);
mContext.registerReceiver(virtualDeviceReceiver, virtualDeviceFilter,
Context.RECEIVER_NOT_EXPORTED);
} }
/** /**
@@ -940,21 +957,42 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
final int resolvedUserId = mSecurityPolicy final int resolvedUserId = mSecurityPolicy
.resolveCallingUserIdEnforcingPermissionsLocked(userId); .resolveCallingUserIdEnforcingPermissionsLocked(userId);
AccessibilityUserState userState = getUserStateLocked(resolvedUserId);
// Support a process moving from the default device to a single virtual
// device.
final int deviceId = mProxyManager.getFirstDeviceIdForUidLocked(
Binder.getCallingUid());
Client client = new Client(callback, Binder.getCallingUid(), userState, deviceId);
// If the client is from a process that runs across users such as // If the client is from a process that runs across users such as
// the system UI or the system we add it to the global state that // the system UI or the system we add it to the global state that
// is shared across users. // is shared across users.
AccessibilityUserState userState = getUserStateLocked(resolvedUserId);
Client client = new Client(callback, Binder.getCallingUid(), userState);
if (mSecurityPolicy.isCallerInteractingAcrossUsers(userId)) { if (mSecurityPolicy.isCallerInteractingAcrossUsers(userId)) {
if (mProxyManager.isProxyedDeviceId(deviceId)) {
if (DEBUG) {
Slog.v(LOG_TAG, "Added global client for proxy-ed pid: "
+ Binder.getCallingPid() + " for device id " + deviceId
+ " with package names " + Arrays.toString(client.mPackageNames));
}
return IntPair.of(mProxyManager.getStateLocked(deviceId,
mUiAutomationManager.isUiAutomationRunningLocked()),
client.mLastSentRelevantEventTypes);
}
mGlobalClients.register(callback, client); mGlobalClients.register(callback, client);
if (DEBUG) { if (DEBUG) {
Slog.i(LOG_TAG, "Added global client for pid:" + Binder.getCallingPid()); Slog.i(LOG_TAG, "Added global client for pid:" + Binder.getCallingPid());
} }
return IntPair.of(
combineUserStateAndProxyState(getClientStateLocked(userState),
mProxyManager.getStateLocked()),
client.mLastSentRelevantEventTypes);
} else { } else {
// If the display belongs to a proxy connections
if (mProxyManager.isProxyedDeviceId(deviceId)) {
if (DEBUG) {
Slog.v(LOG_TAG, "Added user client for proxy-ed pid: "
+ Binder.getCallingPid() + " for device id " + deviceId
+ " with package names " + Arrays.toString(client.mPackageNames));
}
return IntPair.of(mProxyManager.getStateLocked(deviceId,
mUiAutomationManager.isUiAutomationRunningLocked()),
client.mLastSentRelevantEventTypes);
}
userState.mUserClients.register(callback, client); userState.mUserClients.register(callback, client);
// If this client is not for the current user we do not // If this client is not for the current user we do not
// return a state since it is not for the foreground user. // return a state since it is not for the foreground user.
@@ -963,12 +1001,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
Slog.i(LOG_TAG, "Added user client for pid:" + Binder.getCallingPid() Slog.i(LOG_TAG, "Added user client for pid:" + Binder.getCallingPid()
+ " and userId:" + mCurrentUserId); + " and userId:" + mCurrentUserId);
} }
return IntPair.of(
(resolvedUserId == mCurrentUserId) ? combineUserStateAndProxyState(
getClientStateLocked(userState), mProxyManager.getStateLocked())
: 0,
client.mLastSentRelevantEventTypes);
} }
return IntPair.of(
(resolvedUserId == mCurrentUserId) ? getClientStateLocked(userState) : 0,
client.mLastSentRelevantEventTypes);
} }
} }
@@ -1094,7 +1130,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
} }
private void dispatchAccessibilityEventLocked(AccessibilityEvent event) { private void dispatchAccessibilityEventLocked(AccessibilityEvent event) {
if (mProxyManager.isProxyed(event.getDisplayId())) { if (mProxyManager.isProxyedDisplay(event.getDisplayId())) {
mProxyManager.sendAccessibilityEventLocked(event); mProxyManager.sendAccessibilityEventLocked(event);
} else { } else {
notifyAccessibilityServicesDelayedLocked(event, false); notifyAccessibilityServicesDelayedLocked(event, false);
@@ -1160,6 +1196,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
final int resolvedUserId; final int resolvedUserId;
final List<AccessibilityServiceInfo> serviceInfos; final List<AccessibilityServiceInfo> serviceInfos;
synchronized (mLock) { synchronized (mLock) {
final int deviceId = mProxyManager.getFirstDeviceIdForUidLocked(
Binder.getCallingUid());
if (mProxyManager.isProxyedDeviceId(deviceId)) {
return mProxyManager.getInstalledAndEnabledServiceInfosLocked(
AccessibilityServiceInfo.FEEDBACK_ALL_MASK, deviceId);
}
// We treat calls from a profile as if made by its parent as profiles // We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below // share the accessibility state of the parent. The call below
// performs the current profile parent resolution. // performs the current profile parent resolution.
@@ -1195,6 +1237,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
} }
synchronized (mLock) { synchronized (mLock) {
final int deviceId = mProxyManager.getFirstDeviceIdForUidLocked(
Binder.getCallingUid());
if (mProxyManager.isProxyedDeviceId(deviceId)) {
return mProxyManager.getInstalledAndEnabledServiceInfosLocked(feedbackType,
deviceId);
}
// We treat calls from a profile as if made by its parent as profiles // We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below // share the accessibility state of the parent. The call below
// performs the current profile parent resolution. // performs the current profile parent resolution.
@@ -1239,10 +1287,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (resolvedUserId != mCurrentUserId) { if (resolvedUserId != mCurrentUserId) {
return; return;
} }
final int deviceId = mProxyManager.getFirstDeviceIdForUidLocked(
Binder.getCallingUid());
if (mProxyManager.isProxyedDeviceId(deviceId)) {
interfacesToInterrupt = new ArrayList<>();
mProxyManager.addServiceInterfacesLocked(interfacesToInterrupt, deviceId);
} else {
List<AccessibilityServiceConnection> services = List<AccessibilityServiceConnection> services =
getUserStateLocked(resolvedUserId).mBoundServices; getUserStateLocked(resolvedUserId).mBoundServices;
int numServices = services.size() + mProxyManager.getNumProxysLocked(); interfacesToInterrupt = new ArrayList<>(services.size());
interfacesToInterrupt = new ArrayList<>(numServices);
for (int i = 0; i < services.size(); i++) { for (int i = 0; i < services.size(); i++) {
AccessibilityServiceConnection service = services.get(i); AccessibilityServiceConnection service = services.get(i);
IBinder a11yServiceBinder = service.mService; IBinder a11yServiceBinder = service.mService;
@@ -1251,7 +1305,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
interfacesToInterrupt.add(a11yServiceInterface); interfacesToInterrupt.add(a11yServiceInterface);
} }
} }
mProxyManager.addServiceInterfacesLocked(interfacesToInterrupt); }
} }
for (int i = 0, count = interfacesToInterrupt.size(); i < count; i++) { for (int i = 0, count = interfacesToInterrupt.size(); i < count; i++) {
try { try {
@@ -1827,7 +1881,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return result; return result;
} }
private void notifyClearAccessibilityCacheLocked() { @Override
public void notifyClearAccessibilityCacheLocked() {
AccessibilityUserState state = getCurrentUserStateLocked(); AccessibilityUserState state = getCurrentUserStateLocked();
for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
AccessibilityServiceConnection service = state.mBoundServices.get(i); AccessibilityServiceConnection service = state.mBoundServices.get(i);
@@ -2031,18 +2086,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mMainHandler.post(() -> { mMainHandler.post(() -> {
broadcastToClients(userState, ignoreRemoteException(client -> { broadcastToClients(userState, ignoreRemoteException(client -> {
int relevantEventTypes; int relevantEventTypes;
boolean changed = false;
synchronized (mLock) { synchronized (mLock) {
relevantEventTypes = computeRelevantEventTypesLocked(userState, client); relevantEventTypes = computeRelevantEventTypesLocked(userState, client);
if (!mProxyManager.isProxyedDeviceId(client.mDeviceId)) {
if (client.mLastSentRelevantEventTypes != relevantEventTypes) { if (client.mLastSentRelevantEventTypes != relevantEventTypes) {
client.mLastSentRelevantEventTypes = relevantEventTypes; client.mLastSentRelevantEventTypes = relevantEventTypes;
changed = true;
}
}
if (changed) {
client.mCallback.setRelevantEventTypes(relevantEventTypes); client.mCallback.setRelevantEventTypes(relevantEventTypes);
} }
}
}
})); }));
}); });
} }
@@ -2062,7 +2114,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mUiAutomationManager.getServiceInfo(), client) mUiAutomationManager.getServiceInfo(), client)
? mUiAutomationManager.getRelevantEventTypes() ? mUiAutomationManager.getRelevantEventTypes()
: 0; : 0;
relevantEventTypes |= mProxyManager.getRelevantEventTypesLocked();
return relevantEventTypes; return relevantEventTypes;
} }
@@ -2116,7 +2167,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
} }
} }
private static boolean isClientInPackageAllowlist( static boolean isClientInPackageAllowlist(
@Nullable AccessibilityServiceInfo serviceInfo, Client client) { @Nullable AccessibilityServiceInfo serviceInfo, Client client) {
if (serviceInfo == null) return false; if (serviceInfo == null) return false;
@@ -2309,24 +2360,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
updateAccessibilityEnabledSettingLocked(userState); updateAccessibilityEnabledSettingLocked(userState);
} }
private int combineUserStateAndProxyState(int userState, int proxyState) { void scheduleUpdateClientsIfNeededLocked(AccessibilityUserState userState) {
return userState | proxyState; scheduleUpdateClientsIfNeededLocked(userState, false);
} }
void scheduleUpdateClientsIfNeededLocked(AccessibilityUserState userState) { void scheduleUpdateClientsIfNeededLocked(AccessibilityUserState userState,
boolean forceUpdate) {
final int clientState = getClientStateLocked(userState); final int clientState = getClientStateLocked(userState);
final int proxyState = mProxyManager.getStateLocked(); if (((userState.getLastSentClientStateLocked() != clientState || forceUpdate))
if ((userState.getLastSentClientStateLocked() != clientState
|| mProxyManager.getLastSentStateLocked() != proxyState)
&& (mGlobalClients.getRegisteredCallbackCount() > 0 && (mGlobalClients.getRegisteredCallbackCount() > 0
|| userState.mUserClients.getRegisteredCallbackCount() > 0)) { || userState.mUserClients.getRegisteredCallbackCount() > 0)) {
userState.setLastSentClientStateLocked(clientState); userState.setLastSentClientStateLocked(clientState);
mProxyManager.setLastStateLocked(proxyState);
// Send both the user and proxy state to the app for now.
// TODO(b/250929565): Send proxy state to proxy clients
mMainHandler.sendMessage(obtainMessage( mMainHandler.sendMessage(obtainMessage(
AccessibilityManagerService::sendStateToAllClients, AccessibilityManagerService::sendStateToAllClients,
this, combineUserStateAndProxyState(clientState, proxyState), this, clientState,
userState.mUserId)); userState.mUserId));
} }
} }
@@ -2346,8 +2393,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mTraceManager.logTrace(LOG_TAG + ".sendStateToClients", mTraceManager.logTrace(LOG_TAG + ".sendStateToClients",
FLAGS_ACCESSIBILITY_MANAGER_CLIENT, "clientState=" + clientState); FLAGS_ACCESSIBILITY_MANAGER_CLIENT, "clientState=" + clientState);
} }
clients.broadcast(ignoreRemoteException( clients.broadcastForEachCookie(ignoreRemoteException(
client -> client.setState(clientState))); client -> {
Client managerClient = ((Client) client);
if (!mProxyManager.isProxyedDeviceId(managerClient.mDeviceId)) {
managerClient.mCallback.setState(clientState);
}
}));
} }
private void scheduleNotifyClientsOfServicesStateChangeLocked( private void scheduleNotifyClientsOfServicesStateChangeLocked(
@@ -2370,8 +2422,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mTraceManager.logTrace(LOG_TAG + ".notifyClientsOfServicesStateChange", mTraceManager.logTrace(LOG_TAG + ".notifyClientsOfServicesStateChange",
FLAGS_ACCESSIBILITY_MANAGER_CLIENT, "uiTimeout=" + uiTimeout); FLAGS_ACCESSIBILITY_MANAGER_CLIENT, "uiTimeout=" + uiTimeout);
} }
clients.broadcast(ignoreRemoteException(
client -> client.notifyServicesStateChanged(uiTimeout))); clients.broadcastForEachCookie(ignoreRemoteException(
client -> {
Client managerClient = ((Client) client);
if (!mProxyManager.isProxyedDeviceId(managerClient.mDeviceId)) {
managerClient.mCallback.notifyServicesStateChanged(uiTimeout);
}
}));
} }
private void scheduleUpdateInputFilter(AccessibilityUserState userState) { private void scheduleUpdateInputFilter(AccessibilityUserState userState) {
@@ -2444,7 +2502,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
} }
inputFilter = mInputFilter; inputFilter = mInputFilter;
setInputFilter = true; setInputFilter = true;
mProxyManager.setAccessibilityInputFilter(mInputFilter);
} }
mInputFilter.setUserAndEnabledFeatures(userState.mUserId, flags); mInputFilter.setUserAndEnabledFeatures(userState.mUserId, flags);
mInputFilter.setCombinedGenericMotionEventSources( mInputFilter.setCombinedGenericMotionEventSources(
@@ -2477,6 +2534,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
"inputFilter=" + inputFilter); "inputFilter=" + inputFilter);
} }
mWindowManagerService.setInputFilter(inputFilter); mWindowManagerService.setInputFilter(inputFilter);
mProxyManager.setAccessibilityInputFilter(inputFilter);
} }
} }
@@ -2541,6 +2599,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
* @param userState the new user state * @param userState the new user state
*/ */
private void onUserStateChangedLocked(AccessibilityUserState userState) { private void onUserStateChangedLocked(AccessibilityUserState userState) {
onUserStateChangedLocked(userState, false);
}
/**
* Called when any property of the user state has changed.
*
* @param userState the new user state
* @param forceUpdate whether to force an update of the app Clients.
*/
private void onUserStateChangedLocked(AccessibilityUserState userState, boolean forceUpdate) {
if (DEBUG) {
Slog.v(LOG_TAG, "onUserStateChangedLocked for user " + userState.mUserId + " with "
+ "forceUpdate: " + forceUpdate);
}
// TODO: Remove this hack // TODO: Remove this hack
mInitialized = true; mInitialized = true;
updateLegacyCapabilitiesLocked(userState); updateLegacyCapabilitiesLocked(userState);
@@ -2553,7 +2625,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
scheduleUpdateFingerprintGestureHandling(userState); scheduleUpdateFingerprintGestureHandling(userState);
scheduleUpdateInputFilter(userState); scheduleUpdateInputFilter(userState);
updateRelevantEventsLocked(userState); updateRelevantEventsLocked(userState);
scheduleUpdateClientsIfNeededLocked(userState); scheduleUpdateClientsIfNeededLocked(userState, forceUpdate);
updateAccessibilityShortcutKeyTargetsLocked(userState); updateAccessibilityShortcutKeyTargetsLocked(userState);
updateAccessibilityButtonTargetsLocked(userState); updateAccessibilityButtonTargetsLocked(userState);
// Update the capabilities before the mode because we will check the current mode is // Update the capabilities before the mode because we will check the current mode is
@@ -2600,7 +2672,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (display != null) { if (display != null) {
if (observingWindows) { if (observingWindows) {
mA11yWindowManager.startTrackingWindows(display.getDisplayId(), mA11yWindowManager.startTrackingWindows(display.getDisplayId(),
mProxyManager.isProxyed(display.getDisplayId())); mProxyManager.isProxyedDisplay(display.getDisplayId()));
} else { } else {
mA11yWindowManager.stopTrackingWindows(display.getDisplayId()); mA11yWindowManager.stopTrackingWindows(display.getDisplayId());
} }
@@ -2867,6 +2939,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mContext.getContentResolver(), mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS, 0, Settings.Secure.ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS, 0,
userState.mUserId); userState.mUserId);
mProxyManager.updateTimeoutsIfNeeded(nonInteractiveUiTimeout, interactiveUiTimeout);
if (nonInteractiveUiTimeout != userState.getUserNonInteractiveUiTimeoutLocked() if (nonInteractiveUiTimeout != userState.getUserNonInteractiveUiTimeoutLocked()
|| interactiveUiTimeout != userState.getUserInteractiveUiTimeoutLocked()) { || interactiveUiTimeout != userState.getUserInteractiveUiTimeoutLocked()) {
userState.setUserNonInteractiveUiTimeoutLocked(nonInteractiveUiTimeout); userState.setUserNonInteractiveUiTimeoutLocked(nonInteractiveUiTimeout);
@@ -3632,10 +3706,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
} }
synchronized(mLock) { synchronized(mLock) {
final int deviceId = mProxyManager.getFirstDeviceIdForUidLocked(
Binder.getCallingUid());
if (mProxyManager.isProxyedDeviceId(deviceId)) {
return mProxyManager.getRecommendedTimeoutMillisLocked(deviceId);
} else {
final AccessibilityUserState userState = getCurrentUserStateLocked(); final AccessibilityUserState userState = getCurrentUserStateLocked();
return getRecommendedTimeoutMillisLocked(userState); return getRecommendedTimeoutMillisLocked(userState);
} }
} }
}
private long getRecommendedTimeoutMillisLocked(AccessibilityUserState userState) { private long getRecommendedTimeoutMillisLocked(AccessibilityUserState userState) {
return IntPair.of(userState.getInteractiveUiTimeoutLocked(), return IntPair.of(userState.getInteractiveUiTimeoutLocked(),
@@ -3712,6 +3792,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mTraceManager.logTrace(LOG_TAG + ".getFocusStrokeWidth", FLAGS_ACCESSIBILITY_MANAGER); mTraceManager.logTrace(LOG_TAG + ".getFocusStrokeWidth", FLAGS_ACCESSIBILITY_MANAGER);
} }
synchronized (mLock) { synchronized (mLock) {
final int deviceId = mProxyManager.getFirstDeviceIdForUidLocked(
Binder.getCallingUid());
if (mProxyManager.isProxyedDeviceId(deviceId)) {
return mProxyManager.getFocusStrokeWidthLocked(deviceId);
}
final AccessibilityUserState userState = getCurrentUserStateLocked(); final AccessibilityUserState userState = getCurrentUserStateLocked();
return userState.getFocusStrokeWidthLocked(); return userState.getFocusStrokeWidthLocked();
@@ -3728,6 +3813,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mTraceManager.logTrace(LOG_TAG + ".getFocusColor", FLAGS_ACCESSIBILITY_MANAGER); mTraceManager.logTrace(LOG_TAG + ".getFocusColor", FLAGS_ACCESSIBILITY_MANAGER);
} }
synchronized (mLock) { synchronized (mLock) {
final int deviceId = mProxyManager.getFirstDeviceIdForUidLocked(
Binder.getCallingUid());
if (mProxyManager.isProxyedDeviceId(deviceId)) {
return mProxyManager.getFocusColorLocked(deviceId);
}
final AccessibilityUserState userState = getCurrentUserStateLocked(); final AccessibilityUserState userState = getCurrentUserStateLocked();
return userState.getFocusColorLocked(); return userState.getFocusColorLocked();
@@ -3814,7 +3904,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
throw new IllegalArgumentException("The display " + displayId + " does not exist or is" throw new IllegalArgumentException("The display " + displayId + " does not exist or is"
+ " not tracked by accessibility."); + " not tracked by accessibility.");
} }
if (mProxyManager.isProxyed(displayId)) { if (mProxyManager.isProxyedDisplay(displayId)) {
throw new IllegalArgumentException("The display " + displayId + " is already being" throw new IllegalArgumentException("The display " + displayId + " is already being"
+ " proxy-ed"); + " proxy-ed");
} }
@@ -3847,7 +3937,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
} }
boolean isDisplayProxyed(int displayId) { boolean isDisplayProxyed(int displayId) {
return mProxyManager.isProxyed(displayId); return mProxyManager.isProxyedDisplay(displayId);
} }
@Override @Override
@@ -3995,13 +4085,71 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override @Override
public void onClientChangeLocked(boolean serviceInfoChanged) { public void onClientChangeLocked(boolean serviceInfoChanged) {
onClientChangeLocked(serviceInfoChanged, false);
}
/**
* Called when the state of a service or proxy has changed
* @param serviceInfoChanged if the service info has changed
* @param forceUpdate whether to force an update of state for app clients
*/
public void onClientChangeLocked(boolean serviceInfoChanged, boolean forceUpdate) {
AccessibilityUserState userState = getUserStateLocked(mCurrentUserId); AccessibilityUserState userState = getUserStateLocked(mCurrentUserId);
onUserStateChangedLocked(userState); onUserStateChangedLocked(userState, forceUpdate);
if (serviceInfoChanged) { if (serviceInfoChanged) {
scheduleNotifyClientsOfServicesStateChangeLocked(userState); scheduleNotifyClientsOfServicesStateChangeLocked(userState);
} }
} }
@Override
public void onProxyChanged(int deviceId) {
mProxyManager.onProxyChanged(deviceId);
}
/**
* Removes the device from tracking. This will reset any AccessibilityManagerClients to be
* associated with the default user id.
*/
@Override
public void removeDeviceIdLocked(int deviceId) {
resetClientsLocked(deviceId, getCurrentUserStateLocked().mUserClients);
resetClientsLocked(deviceId, mGlobalClients);
// Force an update of A11yManagers if the state was previously a proxy state and needs to be
// returned to the default device state.
onClientChangeLocked(true, true);
}
private void resetClientsLocked(int deviceId,
RemoteCallbackList<IAccessibilityManagerClient> clients) {
if (clients == null || clients.getRegisteredCallbackCount() == 0) {
return;
}
synchronized (mLock) {
for (int i = 0; i < clients.getRegisteredCallbackCount(); i++) {
final Client appClient = ((Client) clients.getRegisteredCallbackCookie(i));
if (appClient.mDeviceId == deviceId) {
appClient.mDeviceId = DEVICE_ID_DEFAULT;
}
}
}
}
@Override
public void updateWindowsForAccessibilityCallbackLocked() {
updateWindowsForAccessibilityCallbackLocked(getUserStateLocked(mCurrentUserId));
}
@Override
public RemoteCallbackList<IAccessibilityManagerClient> getGlobalClientsLocked() {
return mGlobalClients;
}
@Override
public RemoteCallbackList<IAccessibilityManagerClient> getCurrentUserClientsLocked() {
return getCurrentUserState().mUserClients;
}
@Override @Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback, FileDescriptor err, String[] args, ShellCallback callback,
@@ -4313,16 +4461,25 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
final IAccessibilityManagerClient mCallback; final IAccessibilityManagerClient mCallback;
final String[] mPackageNames; final String[] mPackageNames;
int mLastSentRelevantEventTypes; int mLastSentRelevantEventTypes;
int mUid;
int mDeviceId = DEVICE_ID_DEFAULT;
private Client(IAccessibilityManagerClient callback, int clientUid, private Client(IAccessibilityManagerClient callback, int clientUid,
AccessibilityUserState userState) { AccessibilityUserState userState, int deviceId) {
mCallback = callback; mCallback = callback;
mPackageNames = mPackageManager.getPackagesForUid(clientUid); mPackageNames = mPackageManager.getPackagesForUid(clientUid);
mUid = clientUid;
mDeviceId = deviceId;
synchronized (mLock) { synchronized (mLock) {
if (mProxyManager.isProxyedDeviceId(deviceId)) {
mLastSentRelevantEventTypes =
mProxyManager.computeRelevantEventTypesLocked(this);
} else {
mLastSentRelevantEventTypes = computeRelevantEventTypesLocked(userState, this); mLastSentRelevantEventTypes = computeRelevantEventTypesLocked(userState, this);
} }
} }
} }
}
private final class AccessibilityContentObserver extends ContentObserver { private final class AccessibilityContentObserver extends ContentObserver {
@@ -4805,8 +4962,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
} }
mMainHandler.post(() -> { mMainHandler.post(() -> {
broadcastToClients(userState, ignoreRemoteException(client -> { broadcastToClients(userState, ignoreRemoteException(client -> {
if (!mProxyManager.isProxyedDeviceId(client.mDeviceId)) {
client.mCallback.setFocusAppearance(userState.getFocusStrokeWidthLocked(), client.mCallback.setFocusAppearance(userState.getFocusStrokeWidthLocked(),
userState.getFocusColorLocked()); userState.getFocusColorLocked());
}
})); }));
}); });
@@ -5041,11 +5200,4 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
transaction.apply(); transaction.apply();
transaction.close(); transaction.close();
} }
@Override
public void setCurrentUserFocusAppearance(int strokeWidth, int color) {
synchronized (mLock) {
getCurrentUserStateLocked().setFocusAppearanceLocked(strokeWidth, color);
}
}
} }

View File

@@ -67,6 +67,7 @@ import java.util.Set;
public class ProxyAccessibilityServiceConnection extends AccessibilityServiceConnection { public class ProxyAccessibilityServiceConnection extends AccessibilityServiceConnection {
private static final String LOG_TAG = "ProxyAccessibilityServiceConnection"; private static final String LOG_TAG = "ProxyAccessibilityServiceConnection";
private int mDeviceId;
private int mDisplayId; private int mDisplayId;
private List<AccessibilityServiceInfo> mInstalledAndEnabledServices; private List<AccessibilityServiceInfo> mInstalledAndEnabledServices;
@@ -75,6 +76,9 @@ public class ProxyAccessibilityServiceConnection extends AccessibilityServiceCon
/** The color of the focus rectangle */ /** The color of the focus rectangle */
private int mFocusColor; private int mFocusColor;
private int mInteractiveTimeout;
private int mNonInteractiveTimeout;
ProxyAccessibilityServiceConnection( ProxyAccessibilityServiceConnection(
Context context, Context context,
ComponentName componentName, ComponentName componentName,
@@ -83,7 +87,7 @@ public class ProxyAccessibilityServiceConnection extends AccessibilityServiceCon
AccessibilitySecurityPolicy securityPolicy, AccessibilitySecurityPolicy securityPolicy,
SystemSupport systemSupport, AccessibilityTrace trace, SystemSupport systemSupport, AccessibilityTrace trace,
WindowManagerInternal windowManagerInternal, WindowManagerInternal windowManagerInternal,
AccessibilityWindowManager awm, int displayId) { AccessibilityWindowManager awm, int displayId, int deviceId) {
super(/* userState= */null, context, componentName, accessibilityServiceInfo, id, super(/* userState= */null, context, componentName, accessibilityServiceInfo, id,
mainHandler, lock, securityPolicy, systemSupport, trace, windowManagerInternal, mainHandler, lock, securityPolicy, systemSupport, trace, windowManagerInternal,
/* systemActionPerformer= */ null, awm, /* activityTaskManagerService= */ null); /* systemActionPerformer= */ null, awm, /* activityTaskManagerService= */ null);
@@ -93,6 +97,14 @@ public class ProxyAccessibilityServiceConnection extends AccessibilityServiceCon
R.dimen.accessibility_focus_highlight_stroke_width); R.dimen.accessibility_focus_highlight_stroke_width);
mFocusColor = mContext.getResources().getColor( mFocusColor = mContext.getResources().getColor(
R.color.accessibility_focus_highlight_color); R.color.accessibility_focus_highlight_color);
mDeviceId = deviceId;
}
int getDisplayId() {
return mDisplayId;
}
int getDeviceId() {
return mDeviceId;
} }
/** /**
@@ -155,6 +167,8 @@ public class ProxyAccessibilityServiceConnection extends AccessibilityServiceCon
proxyInfo.setAccessibilityTool(isAccessibilityTool); proxyInfo.setAccessibilityTool(isAccessibilityTool);
proxyInfo.setInteractiveUiTimeoutMillis(interactiveUiTimeout); proxyInfo.setInteractiveUiTimeoutMillis(interactiveUiTimeout);
proxyInfo.setNonInteractiveUiTimeoutMillis(nonInteractiveUiTimeout); proxyInfo.setNonInteractiveUiTimeoutMillis(nonInteractiveUiTimeout);
mInteractiveTimeout = interactiveUiTimeout;
mNonInteractiveTimeout = nonInteractiveUiTimeout;
// If any one service info doesn't set package names, i.e. if it's interested in all // If any one service info doesn't set package names, i.e. if it's interested in all
// apps, the proxy shouldn't filter by package name even if some infos specify this. // apps, the proxy shouldn't filter by package name even if some infos specify this.
@@ -167,7 +181,7 @@ public class ProxyAccessibilityServiceConnection extends AccessibilityServiceCon
// Update connection with mAccessibilityServiceInfo values. // Update connection with mAccessibilityServiceInfo values.
setDynamicallyConfigurableProperties(proxyInfo); setDynamicallyConfigurableProperties(proxyInfo);
// Notify manager service. // Notify manager service.
mSystemSupport.onClientChangeLocked(true); mSystemSupport.onProxyChanged(mDeviceId);
} }
} finally { } finally {
Binder.restoreCallingIdentity(identity); Binder.restoreCallingIdentity(identity);
@@ -235,12 +249,7 @@ public class ProxyAccessibilityServiceConnection extends AccessibilityServiceCon
mFocusStrokeWidth = strokeWidth; mFocusStrokeWidth = strokeWidth;
mFocusColor = color; mFocusColor = color;
// Sets the appearance data in the A11yUserState for now, since the A11yManagers are not mSystemSupport.onProxyChanged(mDeviceId);
// separated.
// TODO(254545943): Separate proxy and non-proxy states so the focus appearance on the
// phone is not affected by the appearance of a proxy-ed app.
mSystemSupport.setCurrentUserFocusAppearance(mFocusStrokeWidth, mFocusColor);
mSystemSupport.onClientChangeLocked(false);
} }
} }
@@ -572,17 +581,51 @@ public class ProxyAccessibilityServiceConnection extends AccessibilityServiceCon
throw new UnsupportedOperationException("setAnimationScale is not supported"); throw new UnsupportedOperationException("setAnimationScale is not supported");
} }
public int getInteractiveTimeout() {
return mInteractiveTimeout;
}
public int getNonInteractiveTimeout() {
return mNonInteractiveTimeout;
}
/**
* Returns true if a timeout was updated.
*/
public boolean updateTimeouts(int nonInteractiveUiTimeout, int interactiveUiTimeout) {
final int newInteractiveUiTimeout = interactiveUiTimeout != 0
? interactiveUiTimeout
: mAccessibilityServiceInfo.getInteractiveUiTimeoutMillis();
final int newNonInteractiveUiTimeout = nonInteractiveUiTimeout != 0
? nonInteractiveUiTimeout
: mAccessibilityServiceInfo.getNonInteractiveUiTimeoutMillis();
boolean updated = false;
if (mInteractiveTimeout != newInteractiveUiTimeout) {
mInteractiveTimeout = newInteractiveUiTimeout;
updated = true;
}
if (mNonInteractiveTimeout != newNonInteractiveUiTimeout) {
mNonInteractiveTimeout = newNonInteractiveUiTimeout;
updated = true;
}
return updated;
}
@Override @Override
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return; if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
synchronized (mLock) { synchronized (mLock) {
pw.append("Proxy[displayId=" + mDisplayId); pw.append("Proxy[displayId=" + mDisplayId);
pw.append(", deviceId=" + mDeviceId);
pw.append(", feedbackType" pw.append(", feedbackType"
+ AccessibilityServiceInfo.feedbackTypeToString(mFeedbackType)); + AccessibilityServiceInfo.feedbackTypeToString(mFeedbackType));
pw.append(", capabilities=" + mAccessibilityServiceInfo.getCapabilities()); pw.append(", capabilities=" + mAccessibilityServiceInfo.getCapabilities());
pw.append(", eventTypes=" pw.append(", eventTypes="
+ AccessibilityEvent.eventTypeToString(mEventTypes)); + AccessibilityEvent.eventTypeToString(mEventTypes));
pw.append(", notificationTimeout=" + mNotificationTimeout); pw.append(", notificationTimeout=" + mNotificationTimeout);
pw.append(", nonInteractiveUiTimeout=").append(String.valueOf(mNonInteractiveTimeout));
pw.append(", interactiveUiTimeout=").append(String.valueOf(mInteractiveTimeout));
pw.append(", focusStrokeWidth=").append(String.valueOf(mFocusStrokeWidth)); pw.append(", focusStrokeWidth=").append(String.valueOf(mFocusStrokeWidth));
pw.append(", focusColor=").append(String.valueOf(mFocusColor)); pw.append(", focusColor=").append(String.valueOf(mFocusColor));
pw.append(", installedAndEnabledServiceCount=").append(String.valueOf( pw.append(", installedAndEnabledServiceCount=").append(String.valueOf(

View File

@@ -14,26 +14,45 @@
* limitations under the License. * limitations under the License.
*/ */
package com.android.server.accessibility; package com.android.server.accessibility;
import static android.content.Context.DEVICE_ID_DEFAULT;
import static android.content.Context.DEVICE_ID_INVALID;
import static com.android.internal.util.FunctionalUtils.ignoreRemoteException;
import android.accessibilityservice.AccessibilityServiceInfo; import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityTrace; import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.IAccessibilityServiceClient; import android.accessibilityservice.IAccessibilityServiceClient;
import android.annotation.NonNull;
import android.companion.virtual.VirtualDeviceManager;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager;
import android.os.Handler; import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException; import android.os.RemoteException;
import android.util.IntArray;
import android.util.Slog; import android.util.Slog;
import android.util.SparseArray; import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.Display; import android.view.Display;
import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManagerClient;
import com.android.internal.util.IntPair;
import com.android.server.LocalServices;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.wm.WindowManagerInternal; import com.android.server.wm.WindowManagerInternal;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
/** /**
* Manages proxy connections. * Manages proxy connections.
@@ -53,26 +72,72 @@ public class ProxyManager {
static final String PROXY_COMPONENT_PACKAGE_NAME = "ProxyPackage"; static final String PROXY_COMPONENT_PACKAGE_NAME = "ProxyPackage";
static final String PROXY_COMPONENT_CLASS_NAME = "ProxyClass"; static final String PROXY_COMPONENT_CLASS_NAME = "ProxyClass";
// AMS#mLock
private final Object mLock; private final Object mLock;
private final Context mContext; private final Context mContext;
private final Handler mMainHandler;
// Used to determine if we should notify AccessibilityManager clients of updates. private final UiAutomationManager mUiAutomationManager;
// TODO(254545943): Separate this so each display id has its own state. Currently there is no
// way to identify from AccessibilityManager which proxy state should be returned.
private int mLastState = -1;
private SparseArray<ProxyAccessibilityServiceConnection> mProxyA11yServiceConnections = // Device Id -> state. Used to determine if we should notify AccessibilityManager clients of
// updates.
private final SparseIntArray mLastStates = new SparseIntArray();
// Each display id entry in a SparseArray represents a proxy a11y user.
private final SparseArray<ProxyAccessibilityServiceConnection> mProxyA11yServiceConnections =
new SparseArray<>(); new SparseArray<>();
private AccessibilityWindowManager mA11yWindowManager; private final AccessibilityWindowManager mA11yWindowManager;
private AccessibilityInputFilter mA11yInputFilter; private AccessibilityInputFilter mA11yInputFilter;
ProxyManager(Object lock, AccessibilityWindowManager awm, Context context) { private VirtualDeviceManagerInternal mLocalVdm;
private final SystemSupport mSystemSupport;
/**
* Callbacks into AccessibilityManagerService.
*/
public interface SystemSupport {
/**
* Removes the device id from tracking.
*/
void removeDeviceIdLocked(int deviceId);
/**
* Updates the windows tracking for the current user.
*/
void updateWindowsForAccessibilityCallbackLocked();
/**
* Clears all caches.
*/
void notifyClearAccessibilityCacheLocked();
/**
* Gets the clients for all users.
*/
@NonNull
RemoteCallbackList<IAccessibilityManagerClient> getGlobalClientsLocked();
/**
* Gets the clients for the current user.
*/
@NonNull
RemoteCallbackList<IAccessibilityManagerClient> getCurrentUserClientsLocked();
}
ProxyManager(Object lock, AccessibilityWindowManager awm,
Context context, Handler mainHandler, UiAutomationManager uiAutomationManager,
SystemSupport systemSupport) {
mLock = lock; mLock = lock;
mA11yWindowManager = awm; mA11yWindowManager = awm;
mContext = context; mContext = context;
mMainHandler = mainHandler;
mUiAutomationManager = uiAutomationManager;
mSystemSupport = systemSupport;
mLocalVdm = LocalServices.getService(VirtualDeviceManagerInternal.class);
} }
/** /**
@@ -89,6 +154,12 @@ public class ProxyManager {
Slog.v(LOG_TAG, "Register proxy for display id: " + displayId); Slog.v(LOG_TAG, "Register proxy for display id: " + displayId);
} }
VirtualDeviceManager vdm = mContext.getSystemService(VirtualDeviceManager.class);
if (vdm == null) {
return;
}
final int deviceId = vdm.getDeviceIdForDisplayId(displayId);
// Set a default AccessibilityServiceInfo that is used before the proxy's info is // Set a default AccessibilityServiceInfo that is used before the proxy's info is
// populated. A proxy has the touch exploration and window capabilities. // populated. A proxy has the touch exploration and window capabilities.
AccessibilityServiceInfo info = new AccessibilityServiceInfo(); AccessibilityServiceInfo info = new AccessibilityServiceInfo();
@@ -101,7 +172,7 @@ public class ProxyManager {
new ProxyAccessibilityServiceConnection(context, info.getComponentName(), info, new ProxyAccessibilityServiceConnection(context, info.getComponentName(), info,
id, mainHandler, mLock, securityPolicy, systemSupport, trace, id, mainHandler, mLock, securityPolicy, systemSupport, trace,
windowManagerInternal, windowManagerInternal,
mA11yWindowManager, displayId); mA11yWindowManager, displayId, deviceId);
synchronized (mLock) { synchronized (mLock) {
mProxyA11yServiceConnections.put(displayId, connection); mProxyA11yServiceConnections.put(displayId, connection);
@@ -113,20 +184,16 @@ public class ProxyManager {
@Override @Override
public void binderDied() { public void binderDied() {
client.asBinder().unlinkToDeath(this, 0); client.asBinder().unlinkToDeath(this, 0);
clearConnection(displayId); clearConnectionAndUpdateState(displayId);
} }
}; };
client.asBinder().linkToDeath(deathRecipient, 0); client.asBinder().linkToDeath(deathRecipient, 0);
// Notify apps that the service state has changed. mMainHandler.post(() -> {
// A11yManager#A11yServicesStateChangeListener
synchronized (mLock) {
connection.mSystemSupport.onClientChangeLocked(true);
}
if (mA11yInputFilter != null) { if (mA11yInputFilter != null) {
mA11yInputFilter.disableFeaturesForDisplayIfInstalled(displayId); mA11yInputFilter.disableFeaturesForDisplayIfInstalled(displayId);
} }
});
connection.initializeServiceInterface(client); connection.initializeServiceInterface(client);
} }
@@ -134,38 +201,101 @@ public class ProxyManager {
* Unregister the proxy based on display id. * Unregister the proxy based on display id.
*/ */
public boolean unregisterProxy(int displayId) { public boolean unregisterProxy(int displayId) {
return clearConnection(displayId); return clearConnectionAndUpdateState(displayId);
} }
private boolean clearConnection(int displayId) { /**
boolean removed = false; * Clears all proxy connections belonging to {@code deviceId}.
*/
public void clearConnections(int deviceId) {
final IntArray displaysToClear = new IntArray();
synchronized (mLock) {
for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) {
final ProxyAccessibilityServiceConnection proxy =
mProxyA11yServiceConnections.valueAt(i);
if (proxy != null && proxy.getDeviceId() == deviceId) {
displaysToClear.add(proxy.getDisplayId());
}
}
}
for (int i = 0; i < displaysToClear.size(); i++) {
clearConnectionAndUpdateState(displaysToClear.get(i));
}
}
/**
* Removes the system connection of an AccessibilityDisplayProxy.
*
* This will:
* <ul>
* <li> Reset Clients to belong to the default device if appropriate.
* <li> Stop identifying the display's a11y windows as belonging to a proxy.
* <li> Re-enable any input filters for the display.
* <li> Notify AMS that a proxy has been removed.
* </ul>
*
* @param displayId the display id of the connection to be cleared.
* @return whether the proxy was removed.
*/
private boolean clearConnectionAndUpdateState(int displayId) {
boolean removedFromConnections = false;
int deviceId = DEVICE_ID_INVALID;
synchronized (mLock) { synchronized (mLock) {
if (mProxyA11yServiceConnections.contains(displayId)) { if (mProxyA11yServiceConnections.contains(displayId)) {
deviceId = mProxyA11yServiceConnections.get(displayId).getDeviceId();
mProxyA11yServiceConnections.remove(displayId); mProxyA11yServiceConnections.remove(displayId);
removed = true; removedFromConnections = true;
}
}
if (removedFromConnections) {
updateStateForRemovedDisplay(displayId, deviceId);
}
if (DEBUG) { if (DEBUG) {
Slog.v(LOG_TAG, "Unregister proxy for display id " + displayId); Slog.v(LOG_TAG, "Unregistered proxy for display id " + displayId + ": "
+ removedFromConnections);
} }
return removedFromConnections;
} }
}
if (removed) { /**
* When the connection is removed from tracking in ProxyManager, propagate changes to other a11y
* system components like the input filter and IAccessibilityManagerClients.
*/
public void updateStateForRemovedDisplay(int displayId, int deviceId) {
mA11yWindowManager.stopTrackingDisplayProxy(displayId); mA11yWindowManager.stopTrackingDisplayProxy(displayId);
// A11yInputFilter isn't thread-safe, so post on the system thread.
mMainHandler.post(
() -> {
if (mA11yInputFilter != null) { if (mA11yInputFilter != null) {
final DisplayManager displayManager = (DisplayManager) final DisplayManager displayManager = (DisplayManager)
mContext.getSystemService(Context.DISPLAY_SERVICE); mContext.getSystemService(Context.DISPLAY_SERVICE);
final Display proxyDisplay = displayManager.getDisplay(displayId); final Display proxyDisplay = displayManager.getDisplay(displayId);
if (proxyDisplay != null) { if (proxyDisplay != null) {
// A11yInputFilter isn't thread-safe, so post on the system thread.
mA11yInputFilter.enableFeaturesForDisplayIfInstalled(proxyDisplay); mA11yInputFilter.enableFeaturesForDisplayIfInstalled(proxyDisplay);
} }
} }
});
// If there isn't an existing proxy for the device id, reset clients. Resetting
// will usually happen, since in most cases there will only be one proxy for a
// device.
if (!isProxyedDeviceId(deviceId)) {
synchronized (mLock) {
mSystemSupport.removeDeviceIdLocked(deviceId);
mLastStates.delete(deviceId);
}
} else {
// Update with the states of the remaining proxies.
onProxyChanged(deviceId);
} }
return removed;
} }
/** /**
* Checks if a display id is being proxy-ed. * Returns {@code true} if {@code displayId} is being proxy-ed.
*/ */
public boolean isProxyed(int displayId) { public boolean isProxyedDisplay(int displayId) {
synchronized (mLock) { synchronized (mLock) {
final boolean tracked = mProxyA11yServiceConnections.contains(displayId); final boolean tracked = mProxyA11yServiceConnections.contains(displayId);
if (DEBUG) { if (DEBUG) {
@@ -175,6 +305,23 @@ public class ProxyManager {
} }
} }
/**
* Returns {@code true} if {@code deviceId} is being proxy-ed.
*/
public boolean isProxyedDeviceId(int deviceId) {
if (deviceId == DEVICE_ID_DEFAULT && deviceId == DEVICE_ID_INVALID) {
return false;
}
boolean isTrackingDeviceId;
synchronized (mLock) {
isTrackingDeviceId = getFirstProxyForDeviceIdLocked(deviceId) != null;
}
if (DEBUG) {
Slog.v(LOG_TAG, "Tracking device " + deviceId + " : " + isTrackingDeviceId);
}
return isTrackingDeviceId;
}
/** /**
* Sends AccessibilityEvents to a proxy given the event's displayId. * Sends AccessibilityEvents to a proxy given the event's displayId.
*/ */
@@ -213,15 +360,37 @@ public class ProxyManager {
/** /**
* If there is at least one proxy, accessibility is enabled. * If there is at least one proxy, accessibility is enabled.
*/ */
public int getStateLocked() { public int getStateLocked(int deviceId, boolean automationRunning) {
int clientState = 0; int clientState = 0;
final boolean a11yEnabled = mProxyA11yServiceConnections.size() > 0; if (automationRunning) {
if (a11yEnabled) {
clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED; clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
} }
for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) { for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) {
final ProxyAccessibilityServiceConnection proxy = final ProxyAccessibilityServiceConnection proxy =
mProxyA11yServiceConnections.valueAt(i); mProxyA11yServiceConnections.valueAt(i);
if (proxy != null && proxy.getDeviceId() == deviceId) {
// Combine proxy states.
clientState |= getStateForDisplayIdLocked(proxy);
}
}
if (DEBUG) {
Slog.v(LOG_TAG, "For device id " + deviceId + " a11y is enabled: "
+ ((clientState & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0));
Slog.v(LOG_TAG, "For device id " + deviceId + " touch exploration is enabled: "
+ ((clientState & AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED)
!= 0));
}
return clientState;
}
/**
* If there is at least one proxy, accessibility is enabled.
*/
public int getStateForDisplayIdLocked(ProxyAccessibilityServiceConnection proxy) {
int clientState = 0;
if (proxy != null) {
clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
if (proxy.mRequestTouchExplorationMode) { if (proxy.mRequestTouchExplorationMode) {
clientState |= AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED; clientState |= AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED;
} }
@@ -235,57 +404,85 @@ public class ProxyManager {
!= 0)); != 0));
} }
return clientState; return clientState;
// TODO(b/254545943): When A11yManager is separated, include support for other properties.
} }
/** /**
* Gets the last state. * Gets the last state for a device.
*/ */
public int getLastSentStateLocked() { public int getLastSentStateLocked(int deviceId) {
return mLastState; return mLastStates.get(deviceId, 0);
} }
/** /**
* Sets the last state. * Sets the last state for a device.
*/ */
public void setLastStateLocked(int proxyState) { public void setLastStateLocked(int deviceId, int proxyState) {
mLastState = proxyState; mLastStates.put(deviceId, proxyState);
} }
/** /**
* Returns the relevant event types of every proxy. * Updates the relevant event types of the app clients that are shown on a display owned by the
* TODO(254545943): When A11yManager is separated, return based on the A11yManager display. * specified device.
*
* A client belongs to a device id, so event types (and other state) is determined by the device
* id. In most cases, a device owns a single display. But if multiple displays may belong to one
* Virtual Device, the app clients will get the aggregated event types for all proxy-ed displays
* belonging to a VirtualDevice.
*/ */
public int getRelevantEventTypesLocked() { public void updateRelevantEventTypesLocked(int deviceId) {
if (!isProxyedDeviceId(deviceId)) {
return;
}
mMainHandler.post(() -> {
synchronized (mLock) {
broadcastToClientsLocked(ignoreRemoteException(client -> {
int relevantEventTypes;
if (client.mDeviceId == deviceId) {
relevantEventTypes = computeRelevantEventTypesLocked(client);
if (client.mLastSentRelevantEventTypes != relevantEventTypes) {
client.mLastSentRelevantEventTypes = relevantEventTypes;
client.mCallback.setRelevantEventTypes(relevantEventTypes);
}
}
}));
}
});
}
/**
* Returns the relevant event types for a Client.
*/
int computeRelevantEventTypesLocked(AccessibilityManagerService.Client client) {
int relevantEventTypes = 0; int relevantEventTypes = 0;
for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) { for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) {
ProxyAccessibilityServiceConnection proxy = final ProxyAccessibilityServiceConnection proxy =
mProxyA11yServiceConnections.valueAt(i); mProxyA11yServiceConnections.valueAt(i);
if (proxy != null && proxy.getDeviceId() == client.mDeviceId) {
relevantEventTypes |= proxy.getRelevantEventTypes(); relevantEventTypes |= proxy.getRelevantEventTypes();
relevantEventTypes |= AccessibilityManagerService.isClientInPackageAllowlist(
mUiAutomationManager.getServiceInfo(), client)
? mUiAutomationManager.getRelevantEventTypes()
: 0;
}
} }
if (DEBUG) { if (DEBUG) {
Slog.v(LOG_TAG, "Relevant event types for all proxies: " Slog.v(LOG_TAG, "Relevant event types for device id " + client.mDeviceId
+ AccessibilityEvent.eventTypeToString(relevantEventTypes)); + ": " + AccessibilityEvent.eventTypeToString(relevantEventTypes));
} }
return relevantEventTypes; return relevantEventTypes;
} }
/**
* Gets the number of current proxy connections.
* @return
*/
public int getNumProxysLocked() {
return mProxyA11yServiceConnections.size();
}
/** /**
* Adds the service interfaces to a list. * Adds the service interfaces to a list.
* @param interfaces * @param interfaces the list to add to.
* @param deviceId the device id of the interested app client.
*/ */
public void addServiceInterfacesLocked(List<IAccessibilityServiceClient> interfaces) { public void addServiceInterfacesLocked(@NonNull List<IAccessibilityServiceClient> interfaces,
int deviceId) {
for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) { for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) {
final ProxyAccessibilityServiceConnection proxy = final ProxyAccessibilityServiceConnection proxy =
mProxyA11yServiceConnections.valueAt(i); mProxyA11yServiceConnections.valueAt(i);
if (proxy != null && proxy.getDeviceId() == deviceId) {
final IBinder proxyBinder = proxy.mService; final IBinder proxyBinder = proxy.mService;
final IAccessibilityServiceClient proxyInterface = proxy.mServiceInterface; final IAccessibilityServiceClient proxyInterface = proxy.mServiceInterface;
if ((proxyBinder != null) && (proxyInterface != null)) { if ((proxyBinder != null) && (proxyInterface != null)) {
@@ -293,6 +490,313 @@ public class ProxyManager {
} }
} }
} }
}
/**
* Gets the list of installed and enabled services for a device id.
*
* Note: Multiple display proxies may belong to the same device.
*/
public List<AccessibilityServiceInfo> getInstalledAndEnabledServiceInfosLocked(int feedbackType,
int deviceId) {
List<AccessibilityServiceInfo> serviceInfos = new ArrayList<>();
for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) {
final ProxyAccessibilityServiceConnection proxy =
mProxyA11yServiceConnections.valueAt(i);
if (proxy != null && proxy.getDeviceId() == deviceId) {
// Return all proxy infos for ALL mask.
if (feedbackType == AccessibilityServiceInfo.FEEDBACK_ALL_MASK) {
serviceInfos.addAll(proxy.getInstalledAndEnabledServices());
} else if ((proxy.mFeedbackType & feedbackType) != 0) {
List<AccessibilityServiceInfo> proxyInfos =
proxy.getInstalledAndEnabledServices();
// Iterate through each info in the proxy.
for (AccessibilityServiceInfo info : proxyInfos) {
if ((info.feedbackType & feedbackType) != 0) {
serviceInfos.add(info);
}
}
}
}
}
return serviceInfos;
}
/**
* Handles proxy changes.
*
* <p>
* Changes include if the proxy is unregistered, its service info list has
* changed, or its focus appearance has changed.
* <p>
* Some responses may include updating app clients. A client belongs to a device id, so state is
* determined by the device id. In most cases, a device owns a single display. But if multiple
* displays belong to one Virtual Device, the app clients will get a difference in
* behavior depending on what is being updated.
*
* The following state methods are updated for AccessibilityManager clients belonging to a
* proxied device:
* <ul>
* <li> A11yManager#setRelevantEventTypes - The combined event types of all proxies belonging to
* a device id.
* <li> A11yManager#setState - The combined states of all proxies belonging to a device id.
* <li> A11yManager#notifyServicesStateChanged(timeout) - The highest of all proxies belonging
* to a device id.
* <li> A11yManager#setFocusAppearance - The appearance of the most recently updated display id
* belonging to the device.
* </ul>
* This is similar to onUserStateChangeLocked and onClientChangeLocked, but does not require an
* A11yUserState and only checks proxy-relevant settings.
*/
public void onProxyChanged(int deviceId) {
if (DEBUG) {
Slog.v(LOG_TAG, "onProxyChanged called for deviceId: " + deviceId);
}
//The following state updates are excluded:
// - Input-related state
// - Primary-device / hardware-specific state
synchronized (mLock) {
// A proxy may be registered after the client has been initialized in #addClient.
// For example, a user does not turn on accessibility until after the app has launched.
// Or the process was started with a default id context and should shift to a device.
// Update device ids of the clients if necessary.
updateDeviceIdsIfNeededLocked(deviceId);
// Start tracking of all displays if necessary.
mSystemSupport.updateWindowsForAccessibilityCallbackLocked();
// Calls A11yManager#setRelevantEventTypes (test these)
updateRelevantEventTypesLocked(deviceId);
// Calls A11yManager#setState
scheduleUpdateProxyClientsIfNeededLocked(deviceId);
//Calls A11yManager#notifyServicesStateChanged(timeout)
scheduleNotifyProxyClientsOfServicesStateChangeLocked(deviceId);
// Calls A11yManager#setFocusAppearance
updateFocusAppearanceLocked(deviceId);
mSystemSupport.notifyClearAccessibilityCacheLocked();
}
}
/**
* Updates the states of the app AccessibilityManagers.
*/
public void scheduleUpdateProxyClientsIfNeededLocked(int deviceId) {
final int proxyState = getStateLocked(deviceId,
mUiAutomationManager.isUiAutomationRunningLocked());
if (DEBUG) {
Slog.v(LOG_TAG, "State for device id " + deviceId + " is " + proxyState);
Slog.v(LOG_TAG, "Last state for device id " + deviceId + " is "
+ getLastSentStateLocked(deviceId));
}
if ((getLastSentStateLocked(deviceId)) != proxyState) {
setLastStateLocked(deviceId, proxyState);
mMainHandler.post(() -> {
synchronized (mLock) {
broadcastToClientsLocked(ignoreRemoteException(client -> {
if (client.mDeviceId == deviceId) {
client.mCallback.setState(proxyState);
}
}));
}
});
}
}
/**
* Notifies AccessibilityManager of services state changes, which includes changes to the
* list of service infos and timeouts.
*
* @see AccessibilityManager.AccessibilityServicesStateChangeListener
*/
public void scheduleNotifyProxyClientsOfServicesStateChangeLocked(int deviceId) {
if (DEBUG) {
Slog.v(LOG_TAG, "Notify services state change at device id " + deviceId);
}
mMainHandler.post(()-> {
broadcastToClientsLocked(ignoreRemoteException(client -> {
if (client.mDeviceId == deviceId) {
synchronized (mLock) {
client.mCallback.notifyServicesStateChanged(
getRecommendedTimeoutMillisLocked(deviceId));
}
}
}));
});
}
/**
* Updates the focus appearance of AccessibilityManagerClients.
*/
public void updateFocusAppearanceLocked(int deviceId) {
if (DEBUG) {
Slog.v(LOG_TAG, "Update proxy focus appearance at device id " + deviceId);
}
// Reasonably assume that all proxies belonging to a virtual device should have the
// same focus appearance, and if they should be different these should belong to different
// virtual devices.
final ProxyAccessibilityServiceConnection proxy = getFirstProxyForDeviceIdLocked(deviceId);
if (proxy != null) {
mMainHandler.post(()-> {
broadcastToClientsLocked(ignoreRemoteException(client -> {
if (client.mDeviceId == proxy.getDeviceId()) {
client.mCallback.setFocusAppearance(
proxy.getFocusStrokeWidthLocked(),
proxy.getFocusColorLocked());
}
}));
});
}
}
private ProxyAccessibilityServiceConnection getFirstProxyForDeviceIdLocked(int deviceId) {
for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) {
final ProxyAccessibilityServiceConnection proxy =
mProxyA11yServiceConnections.valueAt(i);
if (proxy != null && proxy.getDeviceId() == deviceId) {
return proxy;
}
}
return null;
}
private void broadcastToClientsLocked(
@NonNull Consumer<AccessibilityManagerService.Client> clientAction) {
final RemoteCallbackList<IAccessibilityManagerClient> userClients =
mSystemSupport.getCurrentUserClientsLocked();
final RemoteCallbackList<IAccessibilityManagerClient> globalClients =
mSystemSupport.getGlobalClientsLocked();
userClients.broadcastForEachCookie(clientAction);
globalClients.broadcastForEachCookie(clientAction);
}
/**
* Updates the timeout and notifies app clients.
*
* For real users, timeouts are tracked in A11yUserState. For proxies, timeouts are in the
* service connection. The value in user state is preferred, but if this value is 0 the service
* info value is used.
*
* This follows the pattern in readUserRecommendedUiTimeoutSettingsLocked.
*
* TODO(b/250929565): ProxyUserState or similar should hold the timeouts
*/
public void updateTimeoutsIfNeeded(int nonInteractiveUiTimeout, int interactiveUiTimeout) {
synchronized (mLock) {
for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) {
final ProxyAccessibilityServiceConnection proxy =
mProxyA11yServiceConnections.valueAt(i);
if (proxy != null) {
if (proxy.updateTimeouts(nonInteractiveUiTimeout, interactiveUiTimeout)) {
scheduleNotifyProxyClientsOfServicesStateChangeLocked(proxy.getDeviceId());
}
}
}
}
}
/**
* Gets the recommended timeout belonging to a Virtual Device.
*
* This is the highest of all display proxies belonging to the virtual device.
*/
public long getRecommendedTimeoutMillisLocked(int deviceId) {
int combinedInteractiveTimeout = 0;
int combinedNonInteractiveTimeout = 0;
for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) {
final ProxyAccessibilityServiceConnection proxy =
mProxyA11yServiceConnections.valueAt(i);
if (proxy != null && proxy.getDeviceId() == deviceId) {
final int proxyInteractiveUiTimeout =
(proxy != null) ? proxy.getInteractiveTimeout() : 0;
final int nonInteractiveUiTimeout =
(proxy != null) ? proxy.getNonInteractiveTimeout() : 0;
combinedInteractiveTimeout = Math.max(proxyInteractiveUiTimeout,
combinedInteractiveTimeout);
combinedNonInteractiveTimeout = Math.max(nonInteractiveUiTimeout,
combinedNonInteractiveTimeout);
}
}
return IntPair.of(combinedInteractiveTimeout, combinedNonInteractiveTimeout);
}
/**
* Gets the first focus stroke width belonging to the device.
*/
public int getFocusStrokeWidthLocked(int deviceId) {
final ProxyAccessibilityServiceConnection proxy = getFirstProxyForDeviceIdLocked(deviceId);
if (proxy != null) {
return proxy.getFocusStrokeWidthLocked();
}
return 0;
}
/**
* Gets the first focus color belonging to the device.
*/
public int getFocusColorLocked(int deviceId) {
final ProxyAccessibilityServiceConnection proxy = getFirstProxyForDeviceIdLocked(deviceId);
if (proxy != null) {
return proxy.getFocusColorLocked();
}
return 0;
}
/**
* Returns the first device id given a UID.
* @param callingUid the UID to check.
* @return the first matching device id, or DEVICE_ID_INVALID.
*/
public int getFirstDeviceIdForUidLocked(int callingUid) {
int firstDeviceId = DEVICE_ID_INVALID;
final VirtualDeviceManagerInternal localVdm = getLocalVdm();
if (localVdm == null) {
return firstDeviceId;
}
final Set<Integer> deviceIds = localVdm.getDeviceIdsForUid(callingUid);
for (Integer uidDeviceId : deviceIds) {
if (uidDeviceId != DEVICE_ID_DEFAULT && uidDeviceId != DEVICE_ID_INVALID) {
firstDeviceId = uidDeviceId;
break;
}
}
return firstDeviceId;
}
/**
* Sets a Client device id if the app uid belongs to the virtual device.
*/
public void updateDeviceIdsIfNeededLocked(int deviceId) {
final RemoteCallbackList<IAccessibilityManagerClient> userClients =
mSystemSupport.getCurrentUserClientsLocked();
final RemoteCallbackList<IAccessibilityManagerClient> globalClients =
mSystemSupport.getGlobalClientsLocked();
updateDeviceIdsIfNeededLocked(deviceId, userClients);
updateDeviceIdsIfNeededLocked(deviceId, globalClients);
}
/**
* Updates the device ids of IAccessibilityManagerClients if needed.
*/
public void updateDeviceIdsIfNeededLocked(int deviceId,
@NonNull RemoteCallbackList<IAccessibilityManagerClient> clients) {
final VirtualDeviceManagerInternal localVdm = getLocalVdm();
if (localVdm == null) {
return;
}
for (int i = 0; i < clients.getRegisteredCallbackCount(); i++) {
final AccessibilityManagerService.Client client =
((AccessibilityManagerService.Client) clients.getRegisteredCallbackCookie(i));
if (deviceId != DEVICE_ID_DEFAULT && deviceId != DEVICE_ID_INVALID
&& localVdm.getDeviceIdsForUid(client.mUid).contains(deviceId)) {
if (DEBUG) {
Slog.v(LOG_TAG, "Packages moved to device id " + deviceId + " are "
+ Arrays.toString(client.mPackageNames));
}
client.mDeviceId = deviceId;
}
}
}
/** /**
* Clears all proxy caches. * Clears all proxy caches.
@@ -306,9 +810,18 @@ public class ProxyManager {
} }
void setAccessibilityInputFilter(AccessibilityInputFilter filter) { void setAccessibilityInputFilter(AccessibilityInputFilter filter) {
if (DEBUG) {
Slog.v(LOG_TAG, "Set proxy input filter to " + filter);
}
mA11yInputFilter = filter; mA11yInputFilter = filter;
} }
VirtualDeviceManagerInternal getLocalVdm() {
if (mLocalVdm == null) {
mLocalVdm = LocalServices.getService(VirtualDeviceManagerInternal.class);
}
return mLocalVdm;
}
/** /**
* Prints information belonging to each display that is controlled by an * Prints information belonging to each display that is controlled by an
@@ -320,12 +833,37 @@ public class ProxyManager {
pw.println("Proxy manager state:"); pw.println("Proxy manager state:");
pw.println(" Number of proxy connections: " + mProxyA11yServiceConnections.size()); pw.println(" Number of proxy connections: " + mProxyA11yServiceConnections.size());
pw.println(" Registered proxy connections:"); pw.println(" Registered proxy connections:");
final RemoteCallbackList<IAccessibilityManagerClient> userClients =
mSystemSupport.getCurrentUserClientsLocked();
final RemoteCallbackList<IAccessibilityManagerClient> globalClients =
mSystemSupport.getGlobalClientsLocked();
for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) { for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) {
final ProxyAccessibilityServiceConnection proxy = final ProxyAccessibilityServiceConnection proxy =
mProxyA11yServiceConnections.valueAt(i); mProxyA11yServiceConnections.valueAt(i);
if (proxy != null) { if (proxy != null) {
proxy.dump(fd, pw, args); proxy.dump(fd, pw, args);
} }
pw.println();
pw.println(" User clients for proxy's virtual device id");
printClientsForDeviceId(pw, userClients, proxy.getDeviceId());
pw.println();
pw.println(" Global clients for proxy's virtual device id");
printClientsForDeviceId(pw, globalClients, proxy.getDeviceId());
}
}
}
private void printClientsForDeviceId(PrintWriter pw,
RemoteCallbackList<IAccessibilityManagerClient> clients, int deviceId) {
if (clients != null) {
for (int j = 0; j < clients.getRegisteredCallbackCount(); j++) {
final AccessibilityManagerService.Client client =
(AccessibilityManagerService.Client)
clients.getRegisteredCallbackCookie(j);
if (client.mDeviceId == deviceId) {
pw.println(" " + Arrays.toString(client.mPackageNames) + "\n");
}
} }
} }
} }

View File

@@ -50,6 +50,7 @@ import java.util.List;
public class ProxyAccessibilityServiceConnectionTest { public class ProxyAccessibilityServiceConnectionTest {
private static final int DISPLAY_ID = 1000; private static final int DISPLAY_ID = 1000;
private static final int DEVICE_ID = 2000;
private static final int CONNECTION_ID = 1000; private static final int CONNECTION_ID = 1000;
private static final ComponentName COMPONENT_NAME = new ComponentName( private static final ComponentName COMPONENT_NAME = new ComponentName(
"com.android.server.accessibility", ".ProxyAccessibilityServiceConnectionTest"); "com.android.server.accessibility", ".ProxyAccessibilityServiceConnectionTest");
@@ -90,7 +91,7 @@ public class ProxyAccessibilityServiceConnectionTest {
mAccessibilityServiceInfo, CONNECTION_ID , new Handler( mAccessibilityServiceInfo, CONNECTION_ID , new Handler(
getInstrumentation().getContext().getMainLooper()), getInstrumentation().getContext().getMainLooper()),
mMockLock, mMockSecurityPolicy, mMockSystemSupport, mMockA11yTrace, mMockLock, mMockSecurityPolicy, mMockSystemSupport, mMockA11yTrace,
mMockWindowManagerInternal, mMockA11yWindowManager, DISPLAY_ID); mMockWindowManagerInternal, mMockA11yWindowManager, DISPLAY_ID, DEVICE_ID);
} }
@Test @Test
@@ -101,7 +102,7 @@ public class ProxyAccessibilityServiceConnectionTest {
mProxyConnection.setInstalledAndEnabledServices(infos); mProxyConnection.setInstalledAndEnabledServices(infos);
verify(mMockSystemSupport).onClientChangeLocked(true); verify(mMockSystemSupport).onProxyChanged(DEVICE_ID);
} }
@Test @Test