Merge "Support separating A11yManagers by device id" into udc-dev am: 1e7c22abdb
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21643019 Change-Id: Iefab33a072f2106c34c65023051cc842c8e54ba5 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -271,6 +271,14 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
|
||||
*/
|
||||
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();
|
||||
|
||||
Pair<float[], MagnificationSpec> getWindowTransformationMatrixAndMagnificationSpec(
|
||||
@@ -315,8 +323,6 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
|
||||
|
||||
void attachAccessibilityOverlayToDisplay(int displayId, SurfaceControl sc);
|
||||
|
||||
void setCurrentUserFocusAppearance(int strokeWidth, int color);
|
||||
|
||||
}
|
||||
|
||||
public AbstractAccessibilityServiceConnection(Context context, ComponentName componentName,
|
||||
|
||||
@@ -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_WINDOW_MAGNIFICATION_CONNECTION;
|
||||
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.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
|
||||
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
|
||||
@@ -189,7 +192,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
AccessibilityUserState.ServiceInfoChangeListener,
|
||||
AccessibilityWindowManager.AccessibilityEventSender,
|
||||
AccessibilitySecurityPolicy.AccessibilityUserManager,
|
||||
SystemActionPerformer.SystemActionsChangedListener {
|
||||
SystemActionPerformer.SystemActionsChangedListener, ProxyManager.SystemSupport{
|
||||
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
@@ -471,7 +474,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
new MagnificationScaleProvider(mContext));
|
||||
mMagnificationProcessor = new MagnificationProcessor(mMagnificationController);
|
||||
mCaptioningManagerImpl = new CaptioningManagerImpl(mContext);
|
||||
mProxyManager = new ProxyManager(mLock, mA11yWindowManager, mContext);
|
||||
mProxyManager = new ProxyManager(mLock, mA11yWindowManager, mContext, mMainHandler,
|
||||
mUiAutomationManager, this);
|
||||
mFlashNotificationsController = new FlashNotificationsController(mContext);
|
||||
init();
|
||||
}
|
||||
@@ -862,6 +866,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
};
|
||||
mContext.registerReceiverAsUser(receiver, UserHandle.ALL, filter, null, mMainHandler,
|
||||
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
|
||||
.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
|
||||
// the system UI or the system we add it to the global state that
|
||||
// is shared across users.
|
||||
AccessibilityUserState userState = getUserStateLocked(resolvedUserId);
|
||||
Client client = new Client(callback, Binder.getCallingUid(), userState);
|
||||
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);
|
||||
if (DEBUG) {
|
||||
Slog.i(LOG_TAG, "Added global client for pid:" + Binder.getCallingPid());
|
||||
}
|
||||
return IntPair.of(
|
||||
combineUserStateAndProxyState(getClientStateLocked(userState),
|
||||
mProxyManager.getStateLocked()),
|
||||
client.mLastSentRelevantEventTypes);
|
||||
} 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);
|
||||
// If this client is not for the current user we do not
|
||||
// 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()
|
||||
+ " 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) {
|
||||
if (mProxyManager.isProxyed(event.getDisplayId())) {
|
||||
if (mProxyManager.isProxyedDisplay(event.getDisplayId())) {
|
||||
mProxyManager.sendAccessibilityEventLocked(event);
|
||||
} else {
|
||||
notifyAccessibilityServicesDelayedLocked(event, false);
|
||||
@@ -1160,6 +1196,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
final int resolvedUserId;
|
||||
final List<AccessibilityServiceInfo> serviceInfos;
|
||||
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
|
||||
// share the accessibility state of the parent. The call below
|
||||
// performs the current profile parent resolution.
|
||||
@@ -1195,6 +1237,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
}
|
||||
|
||||
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
|
||||
// share the accessibility state of the parent. The call below
|
||||
// performs the current profile parent resolution.
|
||||
@@ -1239,19 +1287,25 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
if (resolvedUserId != mCurrentUserId) {
|
||||
return;
|
||||
}
|
||||
List<AccessibilityServiceConnection> services =
|
||||
getUserStateLocked(resolvedUserId).mBoundServices;
|
||||
int numServices = services.size() + mProxyManager.getNumProxysLocked();
|
||||
interfacesToInterrupt = new ArrayList<>(numServices);
|
||||
for (int i = 0; i < services.size(); i++) {
|
||||
AccessibilityServiceConnection service = services.get(i);
|
||||
IBinder a11yServiceBinder = service.mService;
|
||||
IAccessibilityServiceClient a11yServiceInterface = service.mServiceInterface;
|
||||
if ((a11yServiceBinder != null) && (a11yServiceInterface != null)) {
|
||||
interfacesToInterrupt.add(a11yServiceInterface);
|
||||
|
||||
final int deviceId = mProxyManager.getFirstDeviceIdForUidLocked(
|
||||
Binder.getCallingUid());
|
||||
if (mProxyManager.isProxyedDeviceId(deviceId)) {
|
||||
interfacesToInterrupt = new ArrayList<>();
|
||||
mProxyManager.addServiceInterfacesLocked(interfacesToInterrupt, deviceId);
|
||||
} else {
|
||||
List<AccessibilityServiceConnection> services =
|
||||
getUserStateLocked(resolvedUserId).mBoundServices;
|
||||
interfacesToInterrupt = new ArrayList<>(services.size());
|
||||
for (int i = 0; i < services.size(); i++) {
|
||||
AccessibilityServiceConnection service = services.get(i);
|
||||
IBinder a11yServiceBinder = service.mService;
|
||||
IAccessibilityServiceClient a11yServiceInterface = service.mServiceInterface;
|
||||
if ((a11yServiceBinder != null) && (a11yServiceInterface != null)) {
|
||||
interfacesToInterrupt.add(a11yServiceInterface);
|
||||
}
|
||||
}
|
||||
}
|
||||
mProxyManager.addServiceInterfacesLocked(interfacesToInterrupt);
|
||||
}
|
||||
for (int i = 0, count = interfacesToInterrupt.size(); i < count; i++) {
|
||||
try {
|
||||
@@ -1827,7 +1881,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
return result;
|
||||
}
|
||||
|
||||
private void notifyClearAccessibilityCacheLocked() {
|
||||
@Override
|
||||
public void notifyClearAccessibilityCacheLocked() {
|
||||
AccessibilityUserState state = getCurrentUserStateLocked();
|
||||
for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
|
||||
AccessibilityServiceConnection service = state.mBoundServices.get(i);
|
||||
@@ -2031,18 +2086,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
mMainHandler.post(() -> {
|
||||
broadcastToClients(userState, ignoreRemoteException(client -> {
|
||||
int relevantEventTypes;
|
||||
boolean changed = false;
|
||||
synchronized (mLock) {
|
||||
relevantEventTypes = computeRelevantEventTypesLocked(userState, client);
|
||||
|
||||
if (client.mLastSentRelevantEventTypes != relevantEventTypes) {
|
||||
client.mLastSentRelevantEventTypes = relevantEventTypes;
|
||||
changed = true;
|
||||
if (!mProxyManager.isProxyedDeviceId(client.mDeviceId)) {
|
||||
if (client.mLastSentRelevantEventTypes != relevantEventTypes) {
|
||||
client.mLastSentRelevantEventTypes = relevantEventTypes;
|
||||
client.mCallback.setRelevantEventTypes(relevantEventTypes);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
client.mCallback.setRelevantEventTypes(relevantEventTypes);
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
@@ -2062,7 +2114,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
mUiAutomationManager.getServiceInfo(), client)
|
||||
? mUiAutomationManager.getRelevantEventTypes()
|
||||
: 0;
|
||||
relevantEventTypes |= mProxyManager.getRelevantEventTypesLocked();
|
||||
return relevantEventTypes;
|
||||
}
|
||||
|
||||
@@ -2116,7 +2167,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isClientInPackageAllowlist(
|
||||
static boolean isClientInPackageAllowlist(
|
||||
@Nullable AccessibilityServiceInfo serviceInfo, Client client) {
|
||||
if (serviceInfo == null) return false;
|
||||
|
||||
@@ -2309,24 +2360,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
updateAccessibilityEnabledSettingLocked(userState);
|
||||
}
|
||||
|
||||
private int combineUserStateAndProxyState(int userState, int proxyState) {
|
||||
return userState | proxyState;
|
||||
void scheduleUpdateClientsIfNeededLocked(AccessibilityUserState userState) {
|
||||
scheduleUpdateClientsIfNeededLocked(userState, false);
|
||||
}
|
||||
|
||||
void scheduleUpdateClientsIfNeededLocked(AccessibilityUserState userState) {
|
||||
void scheduleUpdateClientsIfNeededLocked(AccessibilityUserState userState,
|
||||
boolean forceUpdate) {
|
||||
final int clientState = getClientStateLocked(userState);
|
||||
final int proxyState = mProxyManager.getStateLocked();
|
||||
if ((userState.getLastSentClientStateLocked() != clientState
|
||||
|| mProxyManager.getLastSentStateLocked() != proxyState)
|
||||
if (((userState.getLastSentClientStateLocked() != clientState || forceUpdate))
|
||||
&& (mGlobalClients.getRegisteredCallbackCount() > 0
|
||||
|| userState.mUserClients.getRegisteredCallbackCount() > 0)) {
|
||||
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(
|
||||
AccessibilityManagerService::sendStateToAllClients,
|
||||
this, combineUserStateAndProxyState(clientState, proxyState),
|
||||
this, clientState,
|
||||
userState.mUserId));
|
||||
}
|
||||
}
|
||||
@@ -2346,8 +2393,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
mTraceManager.logTrace(LOG_TAG + ".sendStateToClients",
|
||||
FLAGS_ACCESSIBILITY_MANAGER_CLIENT, "clientState=" + clientState);
|
||||
}
|
||||
clients.broadcast(ignoreRemoteException(
|
||||
client -> client.setState(clientState)));
|
||||
clients.broadcastForEachCookie(ignoreRemoteException(
|
||||
client -> {
|
||||
Client managerClient = ((Client) client);
|
||||
if (!mProxyManager.isProxyedDeviceId(managerClient.mDeviceId)) {
|
||||
managerClient.mCallback.setState(clientState);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private void scheduleNotifyClientsOfServicesStateChangeLocked(
|
||||
@@ -2370,8 +2422,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
mTraceManager.logTrace(LOG_TAG + ".notifyClientsOfServicesStateChange",
|
||||
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) {
|
||||
@@ -2444,7 +2502,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
}
|
||||
inputFilter = mInputFilter;
|
||||
setInputFilter = true;
|
||||
mProxyManager.setAccessibilityInputFilter(mInputFilter);
|
||||
}
|
||||
mInputFilter.setUserAndEnabledFeatures(userState.mUserId, flags);
|
||||
mInputFilter.setCombinedGenericMotionEventSources(
|
||||
@@ -2477,6 +2534,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
"inputFilter=" + inputFilter);
|
||||
}
|
||||
mWindowManagerService.setInputFilter(inputFilter);
|
||||
mProxyManager.setAccessibilityInputFilter(inputFilter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2541,6 +2599,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
* @param userState the new user state
|
||||
*/
|
||||
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
|
||||
mInitialized = true;
|
||||
updateLegacyCapabilitiesLocked(userState);
|
||||
@@ -2553,7 +2625,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
scheduleUpdateFingerprintGestureHandling(userState);
|
||||
scheduleUpdateInputFilter(userState);
|
||||
updateRelevantEventsLocked(userState);
|
||||
scheduleUpdateClientsIfNeededLocked(userState);
|
||||
scheduleUpdateClientsIfNeededLocked(userState, forceUpdate);
|
||||
updateAccessibilityShortcutKeyTargetsLocked(userState);
|
||||
updateAccessibilityButtonTargetsLocked(userState);
|
||||
// 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 (observingWindows) {
|
||||
mA11yWindowManager.startTrackingWindows(display.getDisplayId(),
|
||||
mProxyManager.isProxyed(display.getDisplayId()));
|
||||
mProxyManager.isProxyedDisplay(display.getDisplayId()));
|
||||
} else {
|
||||
mA11yWindowManager.stopTrackingWindows(display.getDisplayId());
|
||||
}
|
||||
@@ -2867,6 +2939,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
mContext.getContentResolver(),
|
||||
Settings.Secure.ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS, 0,
|
||||
userState.mUserId);
|
||||
|
||||
mProxyManager.updateTimeoutsIfNeeded(nonInteractiveUiTimeout, interactiveUiTimeout);
|
||||
if (nonInteractiveUiTimeout != userState.getUserNonInteractiveUiTimeoutLocked()
|
||||
|| interactiveUiTimeout != userState.getUserInteractiveUiTimeoutLocked()) {
|
||||
userState.setUserNonInteractiveUiTimeoutLocked(nonInteractiveUiTimeout);
|
||||
@@ -3632,8 +3706,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
}
|
||||
|
||||
synchronized(mLock) {
|
||||
final AccessibilityUserState userState = getCurrentUserStateLocked();
|
||||
return getRecommendedTimeoutMillisLocked(userState);
|
||||
final int deviceId = mProxyManager.getFirstDeviceIdForUidLocked(
|
||||
Binder.getCallingUid());
|
||||
if (mProxyManager.isProxyedDeviceId(deviceId)) {
|
||||
return mProxyManager.getRecommendedTimeoutMillisLocked(deviceId);
|
||||
} else {
|
||||
final AccessibilityUserState userState = getCurrentUserStateLocked();
|
||||
return getRecommendedTimeoutMillisLocked(userState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3712,6 +3792,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
mTraceManager.logTrace(LOG_TAG + ".getFocusStrokeWidth", FLAGS_ACCESSIBILITY_MANAGER);
|
||||
}
|
||||
synchronized (mLock) {
|
||||
final int deviceId = mProxyManager.getFirstDeviceIdForUidLocked(
|
||||
Binder.getCallingUid());
|
||||
if (mProxyManager.isProxyedDeviceId(deviceId)) {
|
||||
return mProxyManager.getFocusStrokeWidthLocked(deviceId);
|
||||
}
|
||||
final AccessibilityUserState userState = getCurrentUserStateLocked();
|
||||
|
||||
return userState.getFocusStrokeWidthLocked();
|
||||
@@ -3728,6 +3813,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
mTraceManager.logTrace(LOG_TAG + ".getFocusColor", FLAGS_ACCESSIBILITY_MANAGER);
|
||||
}
|
||||
synchronized (mLock) {
|
||||
final int deviceId = mProxyManager.getFirstDeviceIdForUidLocked(
|
||||
Binder.getCallingUid());
|
||||
if (mProxyManager.isProxyedDeviceId(deviceId)) {
|
||||
return mProxyManager.getFocusColorLocked(deviceId);
|
||||
}
|
||||
final AccessibilityUserState userState = getCurrentUserStateLocked();
|
||||
|
||||
return userState.getFocusColorLocked();
|
||||
@@ -3814,9 +3904,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
throw new IllegalArgumentException("The display " + displayId + " does not exist or is"
|
||||
+ " not tracked by accessibility.");
|
||||
}
|
||||
if (mProxyManager.isProxyed(displayId)) {
|
||||
if (mProxyManager.isProxyedDisplay(displayId)) {
|
||||
throw new IllegalArgumentException("The display " + displayId + " is already being"
|
||||
+ "proxy-ed");
|
||||
+ " proxy-ed");
|
||||
}
|
||||
|
||||
final long identity = Binder.clearCallingIdentity();
|
||||
@@ -3847,7 +3937,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
}
|
||||
|
||||
boolean isDisplayProxyed(int displayId) {
|
||||
return mProxyManager.isProxyed(displayId);
|
||||
return mProxyManager.isProxyedDisplay(displayId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -3995,13 +4085,71 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
|
||||
@Override
|
||||
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);
|
||||
onUserStateChangedLocked(userState);
|
||||
onUserStateChangedLocked(userState, forceUpdate);
|
||||
if (serviceInfoChanged) {
|
||||
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
|
||||
public void onShellCommand(FileDescriptor in, FileDescriptor out,
|
||||
FileDescriptor err, String[] args, ShellCallback callback,
|
||||
@@ -4313,13 +4461,22 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
final IAccessibilityManagerClient mCallback;
|
||||
final String[] mPackageNames;
|
||||
int mLastSentRelevantEventTypes;
|
||||
int mUid;
|
||||
int mDeviceId = DEVICE_ID_DEFAULT;
|
||||
|
||||
private Client(IAccessibilityManagerClient callback, int clientUid,
|
||||
AccessibilityUserState userState) {
|
||||
AccessibilityUserState userState, int deviceId) {
|
||||
mCallback = callback;
|
||||
mPackageNames = mPackageManager.getPackagesForUid(clientUid);
|
||||
mUid = clientUid;
|
||||
mDeviceId = deviceId;
|
||||
synchronized (mLock) {
|
||||
mLastSentRelevantEventTypes = computeRelevantEventTypesLocked(userState, this);
|
||||
if (mProxyManager.isProxyedDeviceId(deviceId)) {
|
||||
mLastSentRelevantEventTypes =
|
||||
mProxyManager.computeRelevantEventTypesLocked(this);
|
||||
} else {
|
||||
mLastSentRelevantEventTypes = computeRelevantEventTypesLocked(userState, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4805,8 +4962,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
}
|
||||
mMainHandler.post(() -> {
|
||||
broadcastToClients(userState, ignoreRemoteException(client -> {
|
||||
client.mCallback.setFocusAppearance(userState.getFocusStrokeWidthLocked(),
|
||||
userState.getFocusColorLocked());
|
||||
if (!mProxyManager.isProxyedDeviceId(client.mDeviceId)) {
|
||||
client.mCallback.setFocusAppearance(userState.getFocusStrokeWidthLocked(),
|
||||
userState.getFocusColorLocked());
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -5041,11 +5200,4 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
transaction.apply();
|
||||
transaction.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCurrentUserFocusAppearance(int strokeWidth, int color) {
|
||||
synchronized (mLock) {
|
||||
getCurrentUserStateLocked().setFocusAppearanceLocked(strokeWidth, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ import java.util.Set;
|
||||
public class ProxyAccessibilityServiceConnection extends AccessibilityServiceConnection {
|
||||
private static final String LOG_TAG = "ProxyAccessibilityServiceConnection";
|
||||
|
||||
private int mDeviceId;
|
||||
private int mDisplayId;
|
||||
private List<AccessibilityServiceInfo> mInstalledAndEnabledServices;
|
||||
|
||||
@@ -75,6 +76,9 @@ public class ProxyAccessibilityServiceConnection extends AccessibilityServiceCon
|
||||
/** The color of the focus rectangle */
|
||||
private int mFocusColor;
|
||||
|
||||
private int mInteractiveTimeout;
|
||||
private int mNonInteractiveTimeout;
|
||||
|
||||
ProxyAccessibilityServiceConnection(
|
||||
Context context,
|
||||
ComponentName componentName,
|
||||
@@ -83,7 +87,7 @@ public class ProxyAccessibilityServiceConnection extends AccessibilityServiceCon
|
||||
AccessibilitySecurityPolicy securityPolicy,
|
||||
SystemSupport systemSupport, AccessibilityTrace trace,
|
||||
WindowManagerInternal windowManagerInternal,
|
||||
AccessibilityWindowManager awm, int displayId) {
|
||||
AccessibilityWindowManager awm, int displayId, int deviceId) {
|
||||
super(/* userState= */null, context, componentName, accessibilityServiceInfo, id,
|
||||
mainHandler, lock, securityPolicy, systemSupport, trace, windowManagerInternal,
|
||||
/* systemActionPerformer= */ null, awm, /* activityTaskManagerService= */ null);
|
||||
@@ -93,6 +97,14 @@ public class ProxyAccessibilityServiceConnection extends AccessibilityServiceCon
|
||||
R.dimen.accessibility_focus_highlight_stroke_width);
|
||||
mFocusColor = mContext.getResources().getColor(
|
||||
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.setInteractiveUiTimeoutMillis(interactiveUiTimeout);
|
||||
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
|
||||
// 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.
|
||||
setDynamicallyConfigurableProperties(proxyInfo);
|
||||
// Notify manager service.
|
||||
mSystemSupport.onClientChangeLocked(true);
|
||||
mSystemSupport.onProxyChanged(mDeviceId);
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
@@ -235,12 +249,7 @@ public class ProxyAccessibilityServiceConnection extends AccessibilityServiceCon
|
||||
|
||||
mFocusStrokeWidth = strokeWidth;
|
||||
mFocusColor = color;
|
||||
// Sets the appearance data in the A11yUserState for now, since the A11yManagers are not
|
||||
// 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);
|
||||
mSystemSupport.onProxyChanged(mDeviceId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -572,17 +581,51 @@ public class ProxyAccessibilityServiceConnection extends AccessibilityServiceCon
|
||||
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
|
||||
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
|
||||
if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
|
||||
synchronized (mLock) {
|
||||
pw.append("Proxy[displayId=" + mDisplayId);
|
||||
pw.append(", deviceId=" + mDeviceId);
|
||||
pw.append(", feedbackType"
|
||||
+ AccessibilityServiceInfo.feedbackTypeToString(mFeedbackType));
|
||||
pw.append(", capabilities=" + mAccessibilityServiceInfo.getCapabilities());
|
||||
pw.append(", eventTypes="
|
||||
+ AccessibilityEvent.eventTypeToString(mEventTypes));
|
||||
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(", focusColor=").append(String.valueOf(mFocusColor));
|
||||
pw.append(", installedAndEnabledServiceCount=").append(String.valueOf(
|
||||
|
||||
@@ -14,26 +14,45 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
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.AccessibilityTrace;
|
||||
import android.accessibilityservice.IAccessibilityServiceClient;
|
||||
import android.annotation.NonNull;
|
||||
import android.companion.virtual.VirtualDeviceManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteCallbackList;
|
||||
import android.os.RemoteException;
|
||||
import android.util.IntArray;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.util.SparseIntArray;
|
||||
import android.view.Display;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
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 java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Manages proxy connections.
|
||||
@@ -53,26 +72,72 @@ public class ProxyManager {
|
||||
static final String PROXY_COMPONENT_PACKAGE_NAME = "ProxyPackage";
|
||||
static final String PROXY_COMPONENT_CLASS_NAME = "ProxyClass";
|
||||
|
||||
// AMS#mLock
|
||||
private final Object mLock;
|
||||
|
||||
private final Context mContext;
|
||||
private final Handler mMainHandler;
|
||||
|
||||
// Used to determine if we should notify AccessibilityManager clients of updates.
|
||||
// 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 final UiAutomationManager mUiAutomationManager;
|
||||
|
||||
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<>();
|
||||
|
||||
private AccessibilityWindowManager mA11yWindowManager;
|
||||
private final AccessibilityWindowManager mA11yWindowManager;
|
||||
|
||||
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;
|
||||
mA11yWindowManager = awm;
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
// populated. A proxy has the touch exploration and window capabilities.
|
||||
AccessibilityServiceInfo info = new AccessibilityServiceInfo();
|
||||
@@ -101,7 +172,7 @@ public class ProxyManager {
|
||||
new ProxyAccessibilityServiceConnection(context, info.getComponentName(), info,
|
||||
id, mainHandler, mLock, securityPolicy, systemSupport, trace,
|
||||
windowManagerInternal,
|
||||
mA11yWindowManager, displayId);
|
||||
mA11yWindowManager, displayId, deviceId);
|
||||
|
||||
synchronized (mLock) {
|
||||
mProxyA11yServiceConnections.put(displayId, connection);
|
||||
@@ -113,20 +184,16 @@ public class ProxyManager {
|
||||
@Override
|
||||
public void binderDied() {
|
||||
client.asBinder().unlinkToDeath(this, 0);
|
||||
clearConnection(displayId);
|
||||
clearConnectionAndUpdateState(displayId);
|
||||
}
|
||||
};
|
||||
client.asBinder().linkToDeath(deathRecipient, 0);
|
||||
|
||||
// Notify apps that the service state has changed.
|
||||
// A11yManager#A11yServicesStateChangeListener
|
||||
synchronized (mLock) {
|
||||
connection.mSystemSupport.onClientChangeLocked(true);
|
||||
}
|
||||
|
||||
if (mA11yInputFilter != null) {
|
||||
mA11yInputFilter.disableFeaturesForDisplayIfInstalled(displayId);
|
||||
}
|
||||
mMainHandler.post(() -> {
|
||||
if (mA11yInputFilter != null) {
|
||||
mA11yInputFilter.disableFeaturesForDisplayIfInstalled(displayId);
|
||||
}
|
||||
});
|
||||
connection.initializeServiceInterface(client);
|
||||
}
|
||||
|
||||
@@ -134,38 +201,101 @@ public class ProxyManager {
|
||||
* Unregister the proxy based on display id.
|
||||
*/
|
||||
public boolean unregisterProxy(int displayId) {
|
||||
return clearConnection(displayId);
|
||||
}
|
||||
|
||||
private boolean clearConnection(int displayId) {
|
||||
boolean removed = false;
|
||||
synchronized (mLock) {
|
||||
if (mProxyA11yServiceConnections.contains(displayId)) {
|
||||
mProxyA11yServiceConnections.remove(displayId);
|
||||
removed = true;
|
||||
if (DEBUG) {
|
||||
Slog.v(LOG_TAG, "Unregister proxy for display id " + displayId);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (removed) {
|
||||
mA11yWindowManager.stopTrackingDisplayProxy(displayId);
|
||||
if (mA11yInputFilter != null) {
|
||||
final DisplayManager displayManager = (DisplayManager)
|
||||
mContext.getSystemService(Context.DISPLAY_SERVICE);
|
||||
final Display proxyDisplay = displayManager.getDisplay(displayId);
|
||||
if (proxyDisplay != null) {
|
||||
mA11yInputFilter.enableFeaturesForDisplayIfInstalled(proxyDisplay);
|
||||
}
|
||||
}
|
||||
}
|
||||
return removed;
|
||||
return clearConnectionAndUpdateState(displayId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a display id is being proxy-ed.
|
||||
* Clears all proxy connections belonging to {@code deviceId}.
|
||||
*/
|
||||
public boolean isProxyed(int displayId) {
|
||||
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) {
|
||||
if (mProxyA11yServiceConnections.contains(displayId)) {
|
||||
deviceId = mProxyA11yServiceConnections.get(displayId).getDeviceId();
|
||||
mProxyA11yServiceConnections.remove(displayId);
|
||||
removedFromConnections = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (removedFromConnections) {
|
||||
updateStateForRemovedDisplay(displayId, deviceId);
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
Slog.v(LOG_TAG, "Unregistered proxy for display id " + displayId + ": "
|
||||
+ removedFromConnections);
|
||||
}
|
||||
return removedFromConnections;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
// A11yInputFilter isn't thread-safe, so post on the system thread.
|
||||
mMainHandler.post(
|
||||
() -> {
|
||||
if (mA11yInputFilter != null) {
|
||||
final DisplayManager displayManager = (DisplayManager)
|
||||
mContext.getSystemService(Context.DISPLAY_SERVICE);
|
||||
final Display proxyDisplay = displayManager.getDisplay(displayId);
|
||||
if (proxyDisplay != null) {
|
||||
// A11yInputFilter isn't thread-safe, so post on the system thread.
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if {@code displayId} is being proxy-ed.
|
||||
*/
|
||||
public boolean isProxyedDisplay(int displayId) {
|
||||
synchronized (mLock) {
|
||||
final boolean tracked = mProxyA11yServiceConnections.contains(displayId);
|
||||
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.
|
||||
*/
|
||||
@@ -213,15 +360,37 @@ public class ProxyManager {
|
||||
/**
|
||||
* If there is at least one proxy, accessibility is enabled.
|
||||
*/
|
||||
public int getStateLocked() {
|
||||
public int getStateLocked(int deviceId, boolean automationRunning) {
|
||||
int clientState = 0;
|
||||
final boolean a11yEnabled = mProxyA11yServiceConnections.size() > 0;
|
||||
if (a11yEnabled) {
|
||||
if (automationRunning) {
|
||||
clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
|
||||
}
|
||||
for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) {
|
||||
final ProxyAccessibilityServiceConnection proxy =
|
||||
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) {
|
||||
clientState |= AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED;
|
||||
}
|
||||
@@ -235,61 +404,396 @@ public class ProxyManager {
|
||||
!= 0));
|
||||
}
|
||||
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() {
|
||||
return mLastState;
|
||||
public int getLastSentStateLocked(int deviceId) {
|
||||
return mLastStates.get(deviceId, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the last state.
|
||||
* Sets the last state for a device.
|
||||
*/
|
||||
public void setLastStateLocked(int proxyState) {
|
||||
mLastState = proxyState;
|
||||
public void setLastStateLocked(int deviceId, int proxyState) {
|
||||
mLastStates.put(deviceId, proxyState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the relevant event types of every proxy.
|
||||
* TODO(254545943): When A11yManager is separated, return based on the A11yManager display.
|
||||
* Updates the relevant event types of the app clients that are shown on a display owned by the
|
||||
* 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;
|
||||
for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) {
|
||||
ProxyAccessibilityServiceConnection proxy =
|
||||
final ProxyAccessibilityServiceConnection proxy =
|
||||
mProxyA11yServiceConnections.valueAt(i);
|
||||
relevantEventTypes |= proxy.getRelevantEventTypes();
|
||||
if (proxy != null && proxy.getDeviceId() == client.mDeviceId) {
|
||||
relevantEventTypes |= proxy.getRelevantEventTypes();
|
||||
relevantEventTypes |= AccessibilityManagerService.isClientInPackageAllowlist(
|
||||
mUiAutomationManager.getServiceInfo(), client)
|
||||
? mUiAutomationManager.getRelevantEventTypes()
|
||||
: 0;
|
||||
}
|
||||
}
|
||||
if (DEBUG) {
|
||||
Slog.v(LOG_TAG, "Relevant event types for all proxies: "
|
||||
+ AccessibilityEvent.eventTypeToString(relevantEventTypes));
|
||||
Slog.v(LOG_TAG, "Relevant event types for device id " + client.mDeviceId
|
||||
+ ": " + AccessibilityEvent.eventTypeToString(relevantEventTypes));
|
||||
}
|
||||
return relevantEventTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of current proxy connections.
|
||||
* @return
|
||||
*/
|
||||
public int getNumProxysLocked() {
|
||||
return mProxyA11yServiceConnections.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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++) {
|
||||
final ProxyAccessibilityServiceConnection proxy =
|
||||
mProxyA11yServiceConnections.valueAt(i);
|
||||
final IBinder proxyBinder = proxy.mService;
|
||||
final IAccessibilityServiceClient proxyInterface = proxy.mServiceInterface;
|
||||
if ((proxyBinder != null) && (proxyInterface != null)) {
|
||||
interfaces.add(proxyInterface);
|
||||
if (proxy != null && proxy.getDeviceId() == deviceId) {
|
||||
final IBinder proxyBinder = proxy.mService;
|
||||
final IAccessibilityServiceClient proxyInterface = proxy.mServiceInterface;
|
||||
if ((proxyBinder != null) && (proxyInterface != null)) {
|
||||
interfaces.add(proxyInterface);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -306,9 +810,18 @@ public class ProxyManager {
|
||||
}
|
||||
|
||||
void setAccessibilityInputFilter(AccessibilityInputFilter filter) {
|
||||
if (DEBUG) {
|
||||
Slog.v(LOG_TAG, "Set proxy input filter to " + 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
|
||||
@@ -320,13 +833,38 @@ public class ProxyManager {
|
||||
pw.println("Proxy manager state:");
|
||||
pw.println(" Number of proxy connections: " + mProxyA11yServiceConnections.size());
|
||||
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++) {
|
||||
final ProxyAccessibilityServiceConnection proxy =
|
||||
mProxyA11yServiceConnections.valueAt(i);
|
||||
if (proxy != null) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ import java.util.List;
|
||||
|
||||
public class ProxyAccessibilityServiceConnectionTest {
|
||||
private static final int DISPLAY_ID = 1000;
|
||||
private static final int DEVICE_ID = 2000;
|
||||
private static final int CONNECTION_ID = 1000;
|
||||
private static final ComponentName COMPONENT_NAME = new ComponentName(
|
||||
"com.android.server.accessibility", ".ProxyAccessibilityServiceConnectionTest");
|
||||
@@ -90,7 +91,7 @@ public class ProxyAccessibilityServiceConnectionTest {
|
||||
mAccessibilityServiceInfo, CONNECTION_ID , new Handler(
|
||||
getInstrumentation().getContext().getMainLooper()),
|
||||
mMockLock, mMockSecurityPolicy, mMockSystemSupport, mMockA11yTrace,
|
||||
mMockWindowManagerInternal, mMockA11yWindowManager, DISPLAY_ID);
|
||||
mMockWindowManagerInternal, mMockA11yWindowManager, DISPLAY_ID, DEVICE_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -101,7 +102,7 @@ public class ProxyAccessibilityServiceConnectionTest {
|
||||
|
||||
mProxyConnection.setInstalledAndEnabledServices(infos);
|
||||
|
||||
verify(mMockSystemSupport).onClientChangeLocked(true);
|
||||
verify(mMockSystemSupport).onProxyChanged(DEVICE_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user