diff options
6 files changed, 1115 insertions, 629 deletions
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 91269c7ab37e..680b2c94c783 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -16,13 +16,6 @@ package com.android.server.accessibility; -import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO; -import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE; -import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN; -import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN; -import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD; -import static android.accessibilityservice.AccessibilityService.SHOW_MODE_MASK; - import static com.android.internal.util.FunctionalUtils.ignoreRemoteException; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; @@ -50,8 +43,6 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.database.ContentObserver; -import android.graphics.Point; -import android.graphics.Rect; import android.graphics.Region; import android.hardware.display.DisplayManager; import android.hardware.fingerprint.IFingerprintService; @@ -119,7 +110,6 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -136,6 +126,7 @@ import java.util.function.Consumer; */ public class AccessibilityManagerService extends IAccessibilityManager.Stub implements AbstractAccessibilityServiceConnection.SystemSupport, + AccessibilityUserState.ServiceInfoChangeListener, AccessibilityWindowManager.AccessibilityEventSender, AccessibilitySecurityPolicy.AccessibilityUserManager { @@ -178,12 +169,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private final SimpleStringSplitter mStringColonSplitter = new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR); - private final Rect mTempRect = new Rect(); - - private final Rect mTempRect1 = new Rect(); - - private final Point mTempPoint = new Point(); - private final PackageManager mPackageManager; private final PowerManager mPowerManager; @@ -226,7 +211,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private final RemoteCallbackList<IAccessibilityManagerClient> mGlobalClients = new RemoteCallbackList<>(); - private final SparseArray<UserState> mUserStates = new SparseArray<>(); + private final SparseArray<AccessibilityUserState> mUserStates = new SparseArray<>(); private final UiAutomationManager mUiAutomationManager = new UiAutomationManager(mLock); @@ -237,7 +222,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private boolean mIsAccessibilityButtonShown; - private UserState getCurrentUserStateLocked() { + private AccessibilityUserState getCurrentUserStateLocked() { return getUserStateLocked(mCurrentUserId); } @@ -293,6 +278,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return mIsAccessibilityButtonShown; } + @Override + public void onServiceInfoChangedLocked(AccessibilityUserState userState) { + scheduleNotifyClientsOfServicesStateChangeLocked(userState); + } + @Nullable public FingerprintGestureDispatcher getFingerprintGestureDispatcher() { return mFingerprintGestureDispatcher; @@ -307,40 +297,40 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - private UserState getUserState(int userId) { + private AccessibilityUserState getUserState(int userId) { synchronized (mLock) { return getUserStateLocked(userId); } } - private UserState getUserStateLocked(int userId) { - UserState state = mUserStates.get(userId); + @NonNull + private AccessibilityUserState getUserStateLocked(int userId) { + AccessibilityUserState state = mUserStates.get(userId); if (state == null) { - state = new UserState(userId); + state = new AccessibilityUserState(userId, mContext, this); mUserStates.put(userId, state); } return state; } boolean getBindInstantServiceAllowed(int userId) { - final UserState userState = getUserState(userId); - if (userState == null) return false; - return userState.getBindInstantServiceAllowed(); + synchronized (mLock) { + final AccessibilityUserState userState = getUserStateLocked(userId); + return userState.getBindInstantServiceAllowedLocked(); + } } void setBindInstantServiceAllowed(int userId, boolean allowed) { - UserState userState; + mContext.enforceCallingOrSelfPermission( + Manifest.permission.MANAGE_BIND_INSTANT_SERVICE, + "setBindInstantServiceAllowed"); synchronized (mLock) { - userState = getUserState(userId); - if (userState == null) { - if (!allowed) { - return; - } - userState = new UserState(userId); - mUserStates.put(userId, userState); + final AccessibilityUserState userState = getUserStateLocked(userId); + if (allowed != userState.getBindInstantServiceAllowedLocked()) { + userState.setBindInstantServiceAllowedLocked(allowed); + onUserStateChangedLocked(userState); } } - userState.setBindInstantServiceAllowed(allowed); } private void registerBroadcastReceivers() { @@ -354,7 +344,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return; } // We will update when the automation service dies. - UserState userState = getCurrentUserStateLocked(); + AccessibilityUserState userState = getCurrentUserStateLocked(); // We have to reload the installed services since some services may // have different attributes, resolve info (does not support equals), // etc. Remove them then to force reload. @@ -376,8 +366,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (userId != mCurrentUserId) { return; } - UserState userState = getUserStateLocked(userId); - boolean reboundAService = userState.mBindingServices.removeIf( + AccessibilityUserState userState = getUserStateLocked(userId); + boolean reboundAService = userState.getBindingServicesLocked().removeIf( component -> component != null && component.getPackageName().equals(packageName)); if (reboundAService) { @@ -395,14 +385,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (userId != mCurrentUserId) { return; } - UserState userState = getUserStateLocked(userId); + AccessibilityUserState userState = getUserStateLocked(userId); Iterator<ComponentName> it = userState.mEnabledServices.iterator(); while (it.hasNext()) { ComponentName comp = it.next(); String compPkg = comp.getPackageName(); if (compPkg.equals(packageName)) { it.remove(); - userState.mBindingServices.remove(comp); + userState.getBindingServicesLocked().remove(comp); // Update the enabled services setting. persistComponentNamesToSettingLocked( Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, @@ -430,7 +420,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (userId != mCurrentUserId) { return false; } - UserState userState = getUserStateLocked(userId); + AccessibilityUserState userState = getUserStateLocked(userId); Iterator<ComponentName> it = userState.mEnabledServices.iterator(); while (it.hasNext()) { ComponentName comp = it.next(); @@ -441,7 +431,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return true; } it.remove(); - userState.mBindingServices.remove(comp); + userState.getBindingServicesLocked().remove(comp); persistComponentNamesToSettingLocked( Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, userState.mEnabledServices, userId); @@ -478,7 +468,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } else if (Intent.ACTION_USER_PRESENT.equals(action)) { // We will update when the automation service dies. synchronized (mLock) { - UserState userState = getCurrentUserStateLocked(); + AccessibilityUserState userState = getCurrentUserStateLocked(); if (readConfigurationForUserStateLocked(userState)) { onUserStateChangedLocked(userState); } @@ -509,7 +499,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // 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. - UserState userState = getUserStateLocked(resolvedUserId); + AccessibilityUserState userState = getUserStateLocked(resolvedUserId); Client client = new Client(callback, Binder.getCallingUid(), userState); if (mSecurityPolicy.isCallerInteractingAcrossUsers(userId)) { mGlobalClients.register(callback, client); @@ -517,7 +507,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub Slog.i(LOG_TAG, "Added global client for pid:" + Binder.getCallingPid()); } return IntPair.of( - userState.getClientState(), + getClientStateLocked(userState), client.mLastSentRelevantEventTypes); } else { userState.mUserClients.register(callback, client); @@ -529,7 +519,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub + " and userId:" + mCurrentUserId); } return IntPair.of( - (resolvedUserId == mCurrentUserId) ? userState.getClientState() : 0, + (resolvedUserId == mCurrentUserId) ? getClientStateLocked(userState) : 0, client.mLastSentRelevantEventTypes); } } @@ -644,7 +634,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub .resolveCallingUserIdEnforcingPermissionsLocked(userId); // The automation service can suppress other services. - final UserState userState = getUserStateLocked(resolvedUserId); + final AccessibilityUserState userState = getUserStateLocked(resolvedUserId); if (mUiAutomationManager.suppressingAccessibilityServicesLocked()) { return Collections.emptyList(); } @@ -754,15 +744,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } synchronized (mLock) { // Set the temporary state. - UserState userState = getCurrentUserStateLocked(); + AccessibilityUserState userState = getCurrentUserStateLocked(); - userState.mIsTouchExplorationEnabled = touchExplorationEnabled; - userState.mIsDisplayMagnificationEnabled = false; - userState.mIsNavBarMagnificationEnabled = false; - userState.mIsAutoclickEnabled = false; + userState.setTouchExplorationEnabledLocked(touchExplorationEnabled); + userState.setDisplayMagnificationEnabledLocked(false); + userState.setNavBarMagnificationEnabledLocked(false); + userState.setAutoclickEnabledLocked(false); userState.mEnabledServices.clear(); userState.mEnabledServices.add(service); - userState.mBindingServices.clear(); + userState.getBindingServicesLocked().clear(); userState.mTouchExplorationGrantedServices.clear(); userState.mTouchExplorationGrantedServices.add(service); @@ -943,7 +933,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } // Disconnect from services for the old user. - UserState oldUserState = getCurrentUserStateLocked(); + AccessibilityUserState oldUserState = getCurrentUserStateLocked(); oldUserState.onSwitchToAnotherUserLocked(); // Disable the local managers for the old user. @@ -960,7 +950,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // The user changed. mCurrentUserId = userId; - UserState userState = getCurrentUserStateLocked(); + AccessibilityUserState userState = getCurrentUserStateLocked(); readConfigurationForUserStateLocked(userState); // Even if reading did not yield change, we have to update @@ -979,8 +969,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private void announceNewUserIfNeeded() { synchronized (mLock) { - UserState userState = getCurrentUserStateLocked(); - if (userState.isHandlingAccessibilityEvents()) { + AccessibilityUserState userState = getCurrentUserStateLocked(); + if (userState.isHandlingAccessibilityEventsLocked()) { UserManager userManager = (UserManager) mContext.getSystemService( Context.USER_SERVICE); String message = mContext.getString(R.string.user_switched, @@ -997,7 +987,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub synchronized (mLock) { int parentUserId = mSecurityPolicy.resolveProfileParentLocked(userId); if (parentUserId == mCurrentUserId) { - UserState userState = getUserStateLocked(mCurrentUserId); + AccessibilityUserState userState = getUserStateLocked(mCurrentUserId); onUserStateChangedLocked(userState); } } @@ -1015,7 +1005,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub readComponentNamesFromStringLocked(oldSetting, mTempComponentNameSet, false); readComponentNamesFromStringLocked(newSetting, mTempComponentNameSet, true); - UserState userState = getUserStateLocked(UserHandle.USER_SYSTEM); + AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM); userState.mEnabledServices.clear(); userState.mEnabledServices.addAll(mTempComponentNameSet); persistComponentNamesToSettingLocked( @@ -1025,6 +1015,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub onUserStateChangedLocked(userState); } + private int getClientStateLocked(AccessibilityUserState userState) { + return userState.getClientStateLocked(mUiAutomationManager.isUiAutomationRunningLocked()); + } + private InteractionBridge getInteractionBridge() { synchronized (mLock) { if (mInteractionBridge == null) { @@ -1044,7 +1038,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // gestures to avoid user frustration when different // behavior is observed from different combinations of // enabled accessibility services. - UserState state = getCurrentUserStateLocked(); + AccessibilityUserState state = getCurrentUserStateLocked(); for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { AccessibilityServiceConnection service = state.mBoundServices.get(i); if (service.mRequestTouchExplorationMode && service.mIsDefault == isDefault) { @@ -1056,7 +1050,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } private void notifyClearAccessibilityCacheLocked() { - UserState state = getCurrentUserStateLocked(); + AccessibilityUserState state = getCurrentUserStateLocked(); for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { AccessibilityServiceConnection service = state.mBoundServices.get(i); service.notifyClearAccessibilityNodeInfoCache(); @@ -1065,25 +1059,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private void notifyMagnificationChangedLocked(int displayId, @NonNull Region region, float scale, float centerX, float centerY) { - final UserState state = getCurrentUserStateLocked(); + final AccessibilityUserState state = getCurrentUserStateLocked(); for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { final AccessibilityServiceConnection service = state.mBoundServices.get(i); service.notifyMagnificationChangedLocked(displayId, region, scale, centerX, centerY); } } - private void notifySoftKeyboardShowModeChangedLocked(int showMode) { - final UserState state = getCurrentUserStateLocked(); - for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { - final AccessibilityServiceConnection service = state.mBoundServices.get(i); - service.notifySoftKeyboardShowModeChangedLocked(showMode); - } - } - private void notifyAccessibilityButtonClickedLocked(int displayId) { - final UserState state = getCurrentUserStateLocked(); + final AccessibilityUserState state = getCurrentUserStateLocked(); - int potentialTargets = state.mIsNavBarMagnificationEnabled ? 1 : 0; + int potentialTargets = state.isNavBarMagnificationEnabledLocked() ? 1 : 0; for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { final AccessibilityServiceConnection service = state.mBoundServices.get(i); if (service.mRequestAccessibilityButton) { @@ -1095,7 +1081,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return; } if (potentialTargets == 1) { - if (state.mIsNavBarMagnificationEnabled) { + if (state.isNavBarMagnificationEnabledLocked()) { mMainHandler.sendMessage(obtainMessage( AccessibilityManagerService::sendAccessibilityButtonToInputFilter, this, displayId)); @@ -1110,13 +1096,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } } else { - if (state.mServiceAssignedToAccessibilityButton == null - && !state.mIsNavBarMagnificationAssignedToAccessibilityButton) { + if (state.getServiceAssignedToAccessibilityButtonLocked() == null + && !state.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) { mMainHandler.sendMessage(obtainMessage( AccessibilityManagerService::showAccessibilityButtonTargetSelection, this, displayId)); - } else if (state.mIsNavBarMagnificationEnabled - && state.mIsNavBarMagnificationAssignedToAccessibilityButton) { + } else if (state.isNavBarMagnificationEnabledLocked() + && state.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) { mMainHandler.sendMessage(obtainMessage( AccessibilityManagerService::sendAccessibilityButtonToInputFilter, this, displayId)); @@ -1125,7 +1111,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { final AccessibilityServiceConnection service = state.mBoundServices.get(i); if (service.mRequestAccessibilityButton && (service.mComponentName.equals( - state.mServiceAssignedToAccessibilityButton))) { + state.getServiceAssignedToAccessibilityButtonLocked()))) { service.notifyAccessibilityButtonClickedLocked(displayId); return; } @@ -1154,7 +1140,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } private void notifyAccessibilityButtonVisibilityChangedLocked(boolean available) { - final UserState state = getCurrentUserStateLocked(); + final AccessibilityUserState state = getCurrentUserStateLocked(); mIsAccessibilityButtonShown = available; for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { final AccessibilityServiceConnection clientConnection = state.mBoundServices.get(i); @@ -1165,7 +1151,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - private boolean readInstalledAccessibilityServiceLocked(UserState userState) { + private boolean readInstalledAccessibilityServiceLocked(AccessibilityUserState userState) { mTempAccessibilityServiceInfoList.clear(); int flags = PackageManager.GET_SERVICES @@ -1174,7 +1160,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub | PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE; - if (userState.getBindInstantServiceAllowed()) { + if (userState.getBindInstantServiceAllowedLocked()) { flags |= PackageManager.MATCH_INSTANT; } @@ -1209,7 +1195,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return false; } - private boolean readInstalledAccessibilityShortcutLocked(UserState userState) { + private boolean readInstalledAccessibilityShortcutLocked(AccessibilityUserState userState) { final List<AccessibilityShortcutInfo> shortcutInfos = AccessibilityManager .getInstance(mContext).getInstalledAccessibilityShortcutListAsUser( mContext, mCurrentUserId); @@ -1221,7 +1207,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return false; } - private boolean readEnabledAccessibilityServicesLocked(UserState userState) { + private boolean readEnabledAccessibilityServicesLocked(AccessibilityUserState userState) { mTempComponentNameSet.clear(); readComponentNamesFromSettingLocked(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, userState.mUserId, mTempComponentNameSet); @@ -1236,7 +1222,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } private boolean readTouchExplorationGrantedAccessibilityServicesLocked( - UserState userState) { + AccessibilityUserState userState) { mTempComponentNameSet.clear(); readComponentNamesFromSettingLocked( Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, @@ -1261,7 +1247,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private void notifyAccessibilityServicesDelayedLocked(AccessibilityEvent event, boolean isDefault) { try { - UserState state = getCurrentUserStateLocked(); + AccessibilityUserState state = getCurrentUserStateLocked(); for (int i = 0, count = state.mBoundServices.size(); i < count; i++) { AccessibilityServiceConnection service = state.mBoundServices.get(i); @@ -1276,7 +1262,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - private void updateRelevantEventsLocked(UserState userState) { + private void updateRelevantEventsLocked(AccessibilityUserState userState) { mMainHandler.post(() -> { broadcastToClients(userState, ignoreRemoteException(client -> { int relevantEventTypes; @@ -1296,7 +1282,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub }); } - private int computeRelevantEventTypesLocked(UserState userState, Client client) { + private int computeRelevantEventTypesLocked(AccessibilityUserState userState, Client client) { int relevantEventTypes = 0; int serviceCount = userState.mBoundServices.size(); @@ -1342,20 +1328,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } private void broadcastToClients( - UserState userState, Consumer<Client> clientAction) { + AccessibilityUserState userState, Consumer<Client> clientAction) { mGlobalClients.broadcastForEachCookie(clientAction); userState.mUserClients.broadcastForEachCookie(clientAction); } - private void unbindAllServicesLocked(UserState userState) { - List<AccessibilityServiceConnection> services = userState.mBoundServices; - for (int count = services.size(); count > 0; count--) { - // When the service is unbound, it disappears from the list, so there's no need to - // keep track of the index - services.get(0).unbindLocked(); - } - } - /** * Populates a set with the {@link ComponentName}s stored in a colon * separated value setting for a given user. @@ -1422,7 +1399,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - private void updateServicesLocked(UserState userState) { + private void updateServicesLocked(AccessibilityUserState userState) { Map<ComponentName, AccessibilityServiceConnection> componentNameToServiceMap = userState.mComponentNameToServiceMap; boolean isUnlockingOrUnlocked = LocalServices.getService(UserManagerInternal.class) @@ -1442,7 +1419,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } // Wait for the binding if it is in process. - if (userState.mBindingServices.contains(componentName)) { + if (userState.getBindingServicesLocked().contains(componentName)) { continue; } if (userState.mEnabledServices.contains(componentName) @@ -1478,15 +1455,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (audioManager != null) { audioManager.setAccessibilityServiceUids(mTempIntArray); } - updateAccessibilityEnabledSetting(userState); + updateAccessibilityEnabledSettingLocked(userState); } - private void scheduleUpdateClientsIfNeededLocked(UserState userState) { - final int clientState = userState.getClientState(); - if (userState.mLastSentClientState != clientState + private void scheduleUpdateClientsIfNeededLocked(AccessibilityUserState userState) { + final int clientState = getClientStateLocked(userState); + if (userState.getLastSentClientStateLocked() != clientState && (mGlobalClients.getRegisteredCallbackCount() > 0 || userState.mUserClients.getRegisteredCallbackCount() > 0)) { - userState.mLastSentClientState = clientState; + userState.setLastSentClientStateLocked(clientState); mMainHandler.sendMessage(obtainMessage( AccessibilityManagerService::sendStateToAllClients, this, clientState, userState.mUserId)); @@ -1508,7 +1485,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub client -> client.setState(clientState))); } - private void scheduleNotifyClientsOfServicesStateChangeLocked(UserState userState) { + private void scheduleNotifyClientsOfServicesStateChangeLocked( + AccessibilityUserState userState) { updateRecommendedUiTimeoutLocked(userState); mMainHandler.sendMessage(obtainMessage( AccessibilityManagerService::sendServicesStateChanged, @@ -1527,44 +1505,45 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub client -> client.notifyServicesStateChanged(uiTimeout))); } - private void scheduleUpdateInputFilter(UserState userState) { + private void scheduleUpdateInputFilter(AccessibilityUserState userState) { mMainHandler.sendMessage(obtainMessage( AccessibilityManagerService::updateInputFilter, this, userState)); } - private void scheduleUpdateFingerprintGestureHandling(UserState userState) { + private void scheduleUpdateFingerprintGestureHandling(AccessibilityUserState userState) { mMainHandler.sendMessage(obtainMessage( AccessibilityManagerService::updateFingerprintGestureHandling, this, userState)); } - private void updateInputFilter(UserState userState) { + private void updateInputFilter(AccessibilityUserState userState) { if (mUiAutomationManager.suppressingAccessibilityServicesLocked()) return; boolean setInputFilter = false; AccessibilityInputFilter inputFilter = null; synchronized (mLock) { int flags = 0; - if (userState.mIsDisplayMagnificationEnabled) { + if (userState.isDisplayMagnificationEnabledLocked()) { flags |= AccessibilityInputFilter.FLAG_FEATURE_SCREEN_MAGNIFIER; } - if (userState.mIsNavBarMagnificationEnabled) { + if (userState.isNavBarMagnificationEnabledLocked()) { flags |= AccessibilityInputFilter.FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER; } if (userHasMagnificationServicesLocked(userState)) { flags |= AccessibilityInputFilter.FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER; } // Touch exploration without accessibility makes no sense. - if (userState.isHandlingAccessibilityEvents() && userState.mIsTouchExplorationEnabled) { + if (userState.isHandlingAccessibilityEventsLocked() + && userState.isTouchExplorationEnabledLocked()) { flags |= AccessibilityInputFilter.FLAG_FEATURE_TOUCH_EXPLORATION; } - if (userState.mIsFilterKeyEventsEnabled) { + if (userState.isFilterKeyEventsEnabledLocked()) { flags |= AccessibilityInputFilter.FLAG_FEATURE_FILTER_KEY_EVENTS; } - if (userState.mIsAutoclickEnabled) { + if (userState.isAutoclickEnabledLocked()) { flags |= AccessibilityInputFilter.FLAG_FEATURE_AUTOCLICK; } - if (userState.mIsPerformGesturesEnabled) { + if (userState.isPerformGesturesEnabledLocked()) { flags |= AccessibilityInputFilter.FLAG_FEATURE_INJECT_MOTION_EVENTS; } if (flags != 0) { @@ -1597,8 +1576,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub String label = service.getServiceInfo().getResolveInfo() .loadLabel(mContext.getPackageManager()).toString(); - final UserState userState = getCurrentUserStateLocked(); - if (userState.mIsTouchExplorationEnabled) { + final AccessibilityUserState userState = getCurrentUserStateLocked(); + if (userState.isTouchExplorationEnabledLocked()) { return; } if (mEnableTouchExplorationDialog != null @@ -1608,36 +1587,36 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mEnableTouchExplorationDialog = new AlertDialog.Builder(mContext) .setIconAttribute(android.R.attr.alertDialogIcon) .setPositiveButton(android.R.string.ok, new OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - // The user allowed the service to toggle touch exploration. - userState.mTouchExplorationGrantedServices.add(service.mComponentName); - persistComponentNamesToSettingLocked( - Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, - userState.mTouchExplorationGrantedServices, userState.mUserId); - // Enable touch exploration. - userState.mIsTouchExplorationEnabled = true; - final long identity = Binder.clearCallingIdentity(); - try { - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1, - userState.mUserId); - } finally { - Binder.restoreCallingIdentity(identity); - } - onUserStateChangedLocked(userState); - } - }) - .setNegativeButton(android.R.string.cancel, new OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - } - }) - .setTitle(R.string.enable_explore_by_touch_warning_title) - .setMessage(mContext.getString( - R.string.enable_explore_by_touch_warning_message, label)) - .create(); + @Override + public void onClick(DialogInterface dialog, int which) { + // The user allowed the service to toggle touch exploration. + userState.mTouchExplorationGrantedServices.add(service.mComponentName); + persistComponentNamesToSettingLocked( + Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, + userState.mTouchExplorationGrantedServices, userState.mUserId); + // Enable touch exploration. + userState.setTouchExplorationEnabledLocked(true); + final long identity = Binder.clearCallingIdentity(); + try { + Settings.Secure.putIntForUser(mContext.getContentResolver(), + Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1, + userState.mUserId); + } finally { + Binder.restoreCallingIdentity(identity); + } + onUserStateChangedLocked(userState); + } + }) + .setNegativeButton(android.R.string.cancel, new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }) + .setTitle(R.string.enable_explore_by_touch_warning_title) + .setMessage(mContext.getString( + R.string.enable_explore_by_touch_warning_message, label)) + .create(); mEnableTouchExplorationDialog.getWindow().setType( WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); mEnableTouchExplorationDialog.getWindow().getAttributes().privateFlags @@ -1652,7 +1631,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * * @param userState the new user state */ - private void onUserStateChangedLocked(UserState userState) { + private void onUserStateChangedLocked(AccessibilityUserState userState) { // TODO: Remove this hack mInitialized = true; updateLegacyCapabilitiesLocked(userState); @@ -1670,7 +1649,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub updateAccessibilityButtonTargetsLocked(userState); } - private void updateWindowsForAccessibilityCallbackLocked(UserState userState) { + private void updateWindowsForAccessibilityCallbackLocked(AccessibilityUserState userState) { // We observe windows for accessibility only if there is at least // one bound service that can retrieve window content that specified // it is interested in accessing such windows. For services that are @@ -1702,7 +1681,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - private void updateLegacyCapabilitiesLocked(UserState userState) { + private void updateLegacyCapabilitiesLocked(AccessibilityUserState userState) { // Up to JB-MR1 we had a white list with services that can enable touch // exploration. When a service is first started we show a dialog to the // use to get a permission to white list the service. @@ -1724,20 +1703,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - private void updatePerformGesturesLocked(UserState userState) { + private void updatePerformGesturesLocked(AccessibilityUserState userState) { final int serviceCount = userState.mBoundServices.size(); for (int i = 0; i < serviceCount; i++) { AccessibilityServiceConnection service = userState.mBoundServices.get(i); if ((service.getCapabilities() & AccessibilityServiceInfo.CAPABILITY_CAN_PERFORM_GESTURES) != 0) { - userState.mIsPerformGesturesEnabled = true; + userState.setPerformGesturesEnabledLocked(true); return; } } - userState.mIsPerformGesturesEnabled = false; + userState.setPerformGesturesEnabledLocked(false); } - private void updateFilterKeyEventsLocked(UserState userState) { + private void updateFilterKeyEventsLocked(AccessibilityUserState userState) { final int serviceCount = userState.mBoundServices.size(); for (int i = 0; i < serviceCount; i++) { AccessibilityServiceConnection service = userState.mBoundServices.get(i); @@ -1745,14 +1724,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub && (service.getCapabilities() & AccessibilityServiceInfo .CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS) != 0) { - userState.mIsFilterKeyEventsEnabled = true; + userState.setFilterKeyEventsEnabledLocked(true); return; } } - userState.mIsFilterKeyEventsEnabled = false; + userState.setFilterKeyEventsEnabledLocked(false); } - private boolean readConfigurationForUserStateLocked(UserState userState) { + private boolean readConfigurationForUserStateLocked(AccessibilityUserState userState) { boolean somethingChanged = readInstalledAccessibilityServiceLocked(userState); somethingChanged |= readInstalledAccessibilityShortcutLocked(userState); somethingChanged |= readEnabledAccessibilityServicesLocked(userState); @@ -1767,10 +1746,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return somethingChanged; } - private void updateAccessibilityEnabledSetting(UserState userState) { + private void updateAccessibilityEnabledSettingLocked(AccessibilityUserState userState) { final long identity = Binder.clearCallingIdentity(); final boolean isA11yEnabled = mUiAutomationManager.isUiAutomationRunningLocked() - || userState.isHandlingAccessibilityEvents(); + || userState.isHandlingAccessibilityEventsLocked(); try { Settings.Secure.putIntForUser(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED, @@ -1781,18 +1760,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - private boolean readTouchExplorationEnabledSettingLocked(UserState userState) { + private boolean readTouchExplorationEnabledSettingLocked(AccessibilityUserState userState) { final boolean touchExplorationEnabled = Settings.Secure.getIntForUser( mContext.getContentResolver(), Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0, userState.mUserId) == 1; - if (touchExplorationEnabled != userState.mIsTouchExplorationEnabled) { - userState.mIsTouchExplorationEnabled = touchExplorationEnabled; + if (touchExplorationEnabled != userState.isTouchExplorationEnabledLocked()) { + userState.setTouchExplorationEnabledLocked(touchExplorationEnabled); return true; } return false; } - private boolean readMagnificationEnabledSettingsLocked(UserState userState) { + private boolean readMagnificationEnabledSettingsLocked(AccessibilityUserState userState) { final boolean displayMagnificationEnabled = Settings.Secure.getIntForUser( mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, @@ -1801,40 +1780,40 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, 0, userState.mUserId) == 1; - if ((displayMagnificationEnabled != userState.mIsDisplayMagnificationEnabled) - || (navBarMagnificationEnabled != userState.mIsNavBarMagnificationEnabled)) { - userState.mIsDisplayMagnificationEnabled = displayMagnificationEnabled; - userState.mIsNavBarMagnificationEnabled = navBarMagnificationEnabled; + if ((displayMagnificationEnabled != userState.isDisplayMagnificationEnabledLocked()) + || (navBarMagnificationEnabled != userState.isNavBarMagnificationEnabledLocked())) { + userState.setDisplayMagnificationEnabledLocked(displayMagnificationEnabled); + userState.setNavBarMagnificationEnabledLocked(navBarMagnificationEnabled); return true; } return false; } - private boolean readAutoclickEnabledSettingLocked(UserState userState) { + private boolean readAutoclickEnabledSettingLocked(AccessibilityUserState userState) { final boolean autoclickEnabled = Settings.Secure.getIntForUser( mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED, 0, userState.mUserId) == 1; - if (autoclickEnabled != userState.mIsAutoclickEnabled) { - userState.mIsAutoclickEnabled = autoclickEnabled; + if (autoclickEnabled != userState.isAutoclickEnabledLocked()) { + userState.setAutoclickEnabledLocked(autoclickEnabled); return true; } return false; } - private boolean readHighTextContrastEnabledSettingLocked(UserState userState) { + private boolean readHighTextContrastEnabledSettingLocked(AccessibilityUserState userState) { final boolean highTextContrastEnabled = Settings.Secure.getIntForUser( mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, 0, userState.mUserId) == 1; - if (highTextContrastEnabled != userState.mIsTextHighContrastEnabled) { - userState.mIsTextHighContrastEnabled = highTextContrastEnabled; + if (highTextContrastEnabled != userState.isTextHighContrastEnabledLocked()) { + userState.setTextHighContrastEnabledLocked(highTextContrastEnabled); return true; } return false; } - private void updateTouchExplorationLocked(UserState userState) { + private void updateTouchExplorationLocked(AccessibilityUserState userState) { boolean enabled = mUiAutomationManager.isTouchExplorationEnabledLocked(); final int serviceCount = userState.mBoundServices.size(); for (int i = 0; i < serviceCount; i++) { @@ -1844,8 +1823,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub break; } } - if (enabled != userState.mIsTouchExplorationEnabled) { - userState.mIsTouchExplorationEnabled = enabled; + if (enabled != userState.isTouchExplorationEnabledLocked()) { + userState.setTouchExplorationEnabledLocked(enabled); final long identity = Binder.clearCallingIdentity(); try { Settings.Secure.putIntForUser(mContext.getContentResolver(), @@ -1857,60 +1836,61 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - private boolean readAccessibilityShortcutSettingLocked(UserState userState) { + private boolean readAccessibilityShortcutSettingLocked(AccessibilityUserState userState) { String componentNameToEnableString = AccessibilityShortcutController .getTargetServiceComponentNameString(mContext, userState.mUserId); if ((componentNameToEnableString == null) || componentNameToEnableString.isEmpty()) { - if (userState.mServiceToEnableWithShortcut == null) { + if (userState.getServiceToEnableWithShortcutLocked() == null) { return false; } - userState.mServiceToEnableWithShortcut = null; + userState.setServiceToEnableWithShortcutLocked(null); return true; } ComponentName componentNameToEnable = ComponentName.unflattenFromString(componentNameToEnableString); if ((componentNameToEnable != null) - && componentNameToEnable.equals(userState.mServiceToEnableWithShortcut)) { + && componentNameToEnable.equals(userState.getServiceToEnableWithShortcutLocked())) { return false; } - userState.mServiceToEnableWithShortcut = componentNameToEnable; + userState.setServiceToEnableWithShortcutLocked(componentNameToEnable); scheduleNotifyClientsOfServicesStateChangeLocked(userState); return true; } - private boolean readAccessibilityButtonSettingsLocked(UserState userState) { + private boolean readAccessibilityButtonSettingsLocked(AccessibilityUserState userState) { String componentId = Settings.Secure.getStringForUser(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, userState.mUserId); if (TextUtils.isEmpty(componentId)) { - if ((userState.mServiceAssignedToAccessibilityButton == null) - && !userState.mIsNavBarMagnificationAssignedToAccessibilityButton) { + if ((userState.getServiceAssignedToAccessibilityButtonLocked() == null) + && !userState.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) { return false; } - userState.mServiceAssignedToAccessibilityButton = null; - userState.mIsNavBarMagnificationAssignedToAccessibilityButton = false; + userState.setServiceAssignedToAccessibilityButtonLocked(null); + userState.setNavBarMagnificationAssignedToAccessibilityButtonLocked(false); return true; } if (componentId.equals(MagnificationController.class.getName())) { - if (userState.mIsNavBarMagnificationAssignedToAccessibilityButton) { + if (userState.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) { return false; } - userState.mServiceAssignedToAccessibilityButton = null; - userState.mIsNavBarMagnificationAssignedToAccessibilityButton = true; + userState.setServiceAssignedToAccessibilityButtonLocked(null); + userState.setNavBarMagnificationAssignedToAccessibilityButtonLocked(true); return true; } ComponentName componentName = ComponentName.unflattenFromString(componentId); - if (Objects.equals(componentName, userState.mServiceAssignedToAccessibilityButton)) { + if (Objects.equals(componentName, + userState.getServiceAssignedToAccessibilityButtonLocked())) { return false; } - userState.mServiceAssignedToAccessibilityButton = componentName; - userState.mIsNavBarMagnificationAssignedToAccessibilityButton = false; + userState.setServiceAssignedToAccessibilityButtonLocked(componentName); + userState.setNavBarMagnificationAssignedToAccessibilityButtonLocked(false); return true; } - private boolean readUserRecommendedUiTimeoutSettingsLocked(UserState userState) { + private boolean readUserRecommendedUiTimeoutSettingsLocked(AccessibilityUserState userState) { final int nonInteractiveUiTimeout = Settings.Secure.getIntForUser( mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS, 0, @@ -1919,10 +1899,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS, 0, userState.mUserId); - if (nonInteractiveUiTimeout != userState.mUserNonInteractiveUiTimeout - || interactiveUiTimeout != userState.mUserInteractiveUiTimeout) { - userState.mUserNonInteractiveUiTimeout = nonInteractiveUiTimeout; - userState.mUserInteractiveUiTimeout = interactiveUiTimeout; + if (nonInteractiveUiTimeout != userState.getUserNonInteractiveUiTimeoutLocked() + || interactiveUiTimeout != userState.getUserInteractiveUiTimeoutLocked()) { + userState.setUserNonInteractiveUiTimeoutLocked(nonInteractiveUiTimeout); + userState.setUserInteractiveUiTimeoutLocked(interactiveUiTimeout); scheduleNotifyClientsOfServicesStateChangeLocked(userState); return true; } @@ -1936,22 +1916,22 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * * @param userState */ - private void updateAccessibilityShortcutLocked(UserState userState) { - if (userState.mServiceToEnableWithShortcut == null) { + private void updateAccessibilityShortcutLocked(AccessibilityUserState userState) { + if (userState.getServiceToEnableWithShortcutLocked() == null) { return; } boolean shortcutServiceIsInstalled = AccessibilityShortcutController.getFrameworkShortcutFeaturesMap() - .containsKey(userState.mServiceToEnableWithShortcut); + .containsKey(userState.getServiceToEnableWithShortcutLocked()); for (int i = 0; !shortcutServiceIsInstalled && (i < userState.mInstalledServices.size()); i++) { if (userState.mInstalledServices.get(i).getComponentName() - .equals(userState.mServiceToEnableWithShortcut)) { + .equals(userState.getServiceToEnableWithShortcutLocked())) { shortcutServiceIsInstalled = true; } } if (!shortcutServiceIsInstalled) { - userState.mServiceToEnableWithShortcut = null; + userState.setServiceToEnableWithShortcutLocked(null); final long identity = Binder.clearCallingIdentity(); try { Settings.Secure.putStringForUser(mContext.getContentResolver(), @@ -1967,7 +1947,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } private boolean canRequestAndRequestsTouchExplorationLocked( - AccessibilityServiceConnection service, UserState userState) { + AccessibilityServiceConnection service, AccessibilityUserState userState) { // Service not ready or cannot request the feature - well nothing to do. if (!service.canReceiveEventsLocked() || !service.mRequestTouchExplorationMode) { return false; @@ -1997,7 +1977,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return false; } - private void updateMagnificationLocked(UserState userState) { + private void updateMagnificationLocked(AccessibilityUserState userState) { if (userState.mUserId != mCurrentUserId) { return; } @@ -2012,8 +1992,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // We would skip overlay display because it uses overlay window to simulate secondary // displays in one display. It's not a real display and there's no input events for it. final ArrayList<Display> displays = getValidDisplayList(); - if (userState.mIsDisplayMagnificationEnabled - || userState.mIsNavBarMagnificationEnabled) { + if (userState.isDisplayMagnificationEnabledLocked() + || userState.isNavBarMagnificationEnabledLocked()) { for (int i = 0; i < displays.size(); i++) { final Display display = displays.get(i); getMagnificationController().register(display.getDisplayId()); @@ -2037,7 +2017,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * Returns whether the specified user has any services that are capable of * controlling magnification. */ - private boolean userHasMagnificationServicesLocked(UserState userState) { + private boolean userHasMagnificationServicesLocked(AccessibilityUserState userState) { final List<AccessibilityServiceConnection> services = userState.mBoundServices; for (int i = 0, count = services.size(); i < count; i++) { final AccessibilityServiceConnection service = services.get(i); @@ -2052,7 +2032,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * Returns whether the specified user has any services that are capable of * controlling magnification and are actively listening for magnification updates. */ - private boolean userHasListeningMagnificationServicesLocked(UserState userState, + private boolean userHasListeningMagnificationServicesLocked(AccessibilityUserState userState, int displayId) { final List<AccessibilityServiceConnection> services = userState.mBoundServices; for (int i = 0, count = services.size(); i < count; i++) { @@ -2065,7 +2045,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return false; } - private void updateFingerprintGestureHandling(UserState userState) { + private void updateFingerprintGestureHandling(AccessibilityUserState userState) { final List<AccessibilityServiceConnection> services; synchronized (mLock) { services = userState.mBoundServices; @@ -2097,7 +2077,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - private void updateAccessibilityButtonTargetsLocked(UserState userState) { + private void updateAccessibilityButtonTargetsLocked(AccessibilityUserState userState) { for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) { final AccessibilityServiceConnection service = userState.mBoundServices.get(i); if (service.mRequestAccessibilityButton) { @@ -2107,9 +2087,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - private void updateRecommendedUiTimeoutLocked(UserState userState) { - int newNonInteractiveUiTimeout = userState.mUserNonInteractiveUiTimeout; - int newInteractiveUiTimeout = userState.mUserInteractiveUiTimeout; + private void updateRecommendedUiTimeoutLocked(AccessibilityUserState userState) { + int newNonInteractiveUiTimeout = userState.getUserNonInteractiveUiTimeoutLocked(); + int newInteractiveUiTimeout = userState.getUserInteractiveUiTimeoutLocked(); // read from a11y services if user does not specify value if (newNonInteractiveUiTimeout == 0 || newInteractiveUiTimeout == 0) { int serviceNonInteractiveUiTimeout = 0; @@ -2132,8 +2112,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub newInteractiveUiTimeout = serviceInteractiveUiTimeout; } } - userState.mNonInteractiveUiTimeout = newNonInteractiveUiTimeout; - userState.mInteractiveUiTimeout = newInteractiveUiTimeout; + userState.setNonInteractiveUiTimeoutLocked(newNonInteractiveUiTimeout); + userState.setInteractiveUiTimeoutLocked(newInteractiveUiTimeout); } @GuardedBy("mLock") @@ -2180,8 +2160,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub final Map<ComponentName, ToggleableFrameworkFeatureInfo> frameworkFeatureMap = AccessibilityShortcutController.getFrameworkShortcutFeaturesMap(); synchronized(mLock) { - final UserState userState = getUserStateLocked(mCurrentUserId); - final ComponentName serviceName = userState.mServiceToEnableWithShortcut; + final AccessibilityUserState userState = getUserStateLocked(mCurrentUserId); + final ComponentName serviceName = userState.getServiceToEnableWithShortcutLocked(); if (serviceName == null) { return; } @@ -2218,11 +2198,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub "getAccessibilityShortcutService requires the MANAGE_ACCESSIBILITY permission"); } synchronized(mLock) { - final UserState userState = getUserStateLocked(mCurrentUserId); - if (userState.mServiceToEnableWithShortcut == null) { + final AccessibilityUserState userState = getUserStateLocked(mCurrentUserId); + if (userState.getServiceToEnableWithShortcutLocked() == null) { return null; } - return userState.mServiceToEnableWithShortcut.flattenToString(); + return userState.getServiceToEnableWithShortcutLocked().flattenToString(); } } @@ -2237,7 +2217,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub userId); setting.write(ComponentNameSet.add(setting.read(), componentName)); - UserState userState = getUserStateLocked(userId); + AccessibilityUserState userState = getUserStateLocked(userId); if (userState.mEnabledServices.add(componentName)) { onUserStateChangedLocked(userState); } @@ -2254,7 +2234,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub userId); setting.write(ComponentNameSet.remove(setting.read(), componentName)); - UserState userState = getUserStateLocked(userId); + AccessibilityUserState userState = getUserStateLocked(userId); if (userState.mEnabledServices.remove(componentName)) { onUserStateChangedLocked(userState); } @@ -2322,14 +2302,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public long getRecommendedTimeoutMillis() { synchronized(mLock) { - final UserState userState = getCurrentUserStateLocked(); + final AccessibilityUserState userState = getCurrentUserStateLocked(); return getRecommendedTimeoutMillisLocked(userState); } } - private long getRecommendedTimeoutMillisLocked(UserState userState) { - return IntPair.of(userState.mInteractiveUiTimeout, - userState.mNonInteractiveUiTimeout); + private long getRecommendedTimeoutMillisLocked(AccessibilityUserState userState) { + return IntPair.of(userState.getInteractiveUiTimeoutLocked(), + userState.getNonInteractiveUiTimeoutLocked()); } @Override @@ -2338,78 +2318,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub synchronized (mLock) { pw.println("ACCESSIBILITY MANAGER (dumpsys accessibility)"); pw.println(); + pw.append("currentUserId=").append(String.valueOf(mCurrentUserId)); + pw.println(); final int userCount = mUserStates.size(); for (int i = 0; i < userCount; i++) { - UserState userState = mUserStates.valueAt(i); - pw.append("User state[attributes:{id=" + userState.mUserId); - pw.append(", currentUser=" + (userState.mUserId == mCurrentUserId)); - pw.append(", touchExplorationEnabled=" + userState.mIsTouchExplorationEnabled); - pw.append(", displayMagnificationEnabled=" - + userState.mIsDisplayMagnificationEnabled); - pw.append(", navBarMagnificationEnabled=" - + userState.mIsNavBarMagnificationEnabled); - pw.append(", autoclickEnabled=" + userState.mIsAutoclickEnabled); - pw.append(", nonInteractiveUiTimeout=" + userState.mNonInteractiveUiTimeout); - pw.append(", interactiveUiTimeout=" + userState.mInteractiveUiTimeout); - pw.append(", installedServiceCount=" + userState.mInstalledServices.size()); - if (mUiAutomationManager.isUiAutomationRunningLocked()) { - pw.append(", "); - mUiAutomationManager.dumpUiAutomationService(fd, pw, args); - pw.println(); - } - pw.append("}"); - pw.println(); - pw.append(" Bound services:{"); - final int serviceCount = userState.mBoundServices.size(); - for (int j = 0; j < serviceCount; j++) { - if (j > 0) { - pw.append(", "); - pw.println(); - pw.append(" "); - } - AccessibilityServiceConnection service = userState.mBoundServices.get(j); - service.dump(fd, pw, args); - } - pw.println("}"); - pw.append(" Enabled services:{"); - Iterator<ComponentName> it = userState.mEnabledServices.iterator(); - if (it.hasNext()) { - ComponentName componentName = it.next(); - pw.append(componentName.toShortString()); - while (it.hasNext()) { - componentName = it.next(); - pw.append(", "); - pw.append(componentName.toShortString()); - } - } - pw.println("}"); - pw.append(" Binding services:{"); - it = userState.mBindingServices.iterator(); - if (it.hasNext()) { - ComponentName componentName = it.next(); - pw.append(componentName.toShortString()); - while (it.hasNext()) { - componentName = it.next(); - pw.append(", "); - pw.append(componentName.toShortString()); - } - } - pw.println("}]"); + mUserStates.valueAt(i).dump(fd, pw, args); + } + if (mUiAutomationManager.isUiAutomationRunningLocked()) { + mUiAutomationManager.dumpUiAutomationService(fd, pw, args); pw.println(); } mA11yWindowManager.dump(fd, pw, args); } } - private void putSecureIntForUser(String key, int value, int userid) { - final long identity = Binder.clearCallingIdentity(); - try { - Settings.Secure.putIntForUser(mContext.getContentResolver(), key, value, userid); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - //TODO remove after refactoring KeyEventDispatcherTest final class MainHandler extends Handler { public static final int MSG_SEND_KEY_EVENT_TO_INPUT_FILTER = 8; @@ -2446,7 +2368,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public void onClientChangeLocked(boolean serviceInfoChanged) { - AccessibilityManagerService.UserState userState = getUserStateLocked(mCurrentUserId); + AccessibilityUserState userState = getUserStateLocked(mCurrentUserId); onUserStateChangedLocked(userState); if (serviceInfoChanged) { scheduleNotifyClientsOfServicesStateChangeLocked(userState); @@ -2474,7 +2396,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub info.setCapabilities(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT); info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS; info.flags |= AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS; - final UserState userState; + final AccessibilityUserState userState; synchronized (mLock) { userState = getCurrentUserStateLocked(); } @@ -2593,7 +2515,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (mInputFilter != null) { mInputFilter.onDisplayChanged(); } - UserState userState = getCurrentUserStateLocked(); + AccessibilityUserState userState = getCurrentUserStateLocked(); if (displayId != Display.DEFAULT_DISPLAY) { final List<AccessibilityServiceConnection> services = userState.mBoundServices; for (int i = 0; i < services.size(); i++) { @@ -2618,7 +2540,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (mInputFilter != null) { mInputFilter.onDisplayChanged(); } - UserState userState = getCurrentUserStateLocked(); + AccessibilityUserState userState = getCurrentUserStateLocked(); if (displayId != Display.DEFAULT_DISPLAY) { final List<AccessibilityServiceConnection> services = userState.mBoundServices; for (int i = 0; i < services.size(); i++) { @@ -2658,7 +2580,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub final String[] mPackageNames; int mLastSentRelevantEventTypes; - private Client(IAccessibilityManagerClient callback, int clientUid, UserState userState) { + private Client(IAccessibilityManagerClient callback, int clientUid, + AccessibilityUserState userState) { mCallback = callback; mPackageNames = mPackageManager.getPackagesForUid(clientUid); synchronized (mLock) { @@ -2667,316 +2590,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - public class UserState { - public final int mUserId; - - // Non-transient state. - - public final RemoteCallbackList<IAccessibilityManagerClient> mUserClients = - new RemoteCallbackList<>(); - - // Transient state. - - public final ArrayList<AccessibilityServiceConnection> mBoundServices = new ArrayList<>(); - - public final Map<ComponentName, AccessibilityServiceConnection> mComponentNameToServiceMap = - new HashMap<>(); - - public final List<AccessibilityServiceInfo> mInstalledServices = - new ArrayList<>(); - - public final List<AccessibilityShortcutInfo> mInstalledShortcuts = new ArrayList<>(); - - private final Set<ComponentName> mBindingServices = new HashSet<>(); - - public final Set<ComponentName> mEnabledServices = new HashSet<>(); - - public final Set<ComponentName> mTouchExplorationGrantedServices = - new HashSet<>(); - - public ComponentName mServiceChangingSoftKeyboardMode; - - public ComponentName mServiceToEnableWithShortcut; - - public int mLastSentClientState = -1; - public int mNonInteractiveUiTimeout = 0; - public int mInteractiveUiTimeout = 0; - - private int mSoftKeyboardShowMode = 0; - - public boolean mIsNavBarMagnificationAssignedToAccessibilityButton; - public ComponentName mServiceAssignedToAccessibilityButton; - - public boolean mIsTouchExplorationEnabled; - public boolean mIsTextHighContrastEnabled; - public boolean mIsDisplayMagnificationEnabled; - public boolean mIsNavBarMagnificationEnabled; - public boolean mIsAutoclickEnabled; - public boolean mIsPerformGesturesEnabled; - public boolean mIsFilterKeyEventsEnabled; - public int mUserNonInteractiveUiTimeout; - public int mUserInteractiveUiTimeout; - - private boolean mBindInstantServiceAllowed; - - public UserState(int userId) { - mUserId = userId; - } - - public int getClientState() { - int clientState = 0; - final boolean a11yEnabled = (mUiAutomationManager.isUiAutomationRunningLocked() - || isHandlingAccessibilityEvents()); - if (a11yEnabled) { - clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED; - } - // Touch exploration relies on enabled accessibility. - if (a11yEnabled && mIsTouchExplorationEnabled) { - clientState |= AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED; - } - if (mIsTextHighContrastEnabled) { - clientState |= AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED; - } - return clientState; - } - - public boolean isHandlingAccessibilityEvents() { - return !mBoundServices.isEmpty() || !mBindingServices.isEmpty(); - } - - public void onSwitchToAnotherUserLocked() { - // Unbind all services. - unbindAllServicesLocked(this); - - // Clear service management state. - mBoundServices.clear(); - mBindingServices.clear(); - - // Clear event management state. - mLastSentClientState = -1; - - // clear UI timeout - mNonInteractiveUiTimeout = 0; - mInteractiveUiTimeout = 0; - - // Clear state persisted in settings. - mEnabledServices.clear(); - mTouchExplorationGrantedServices.clear(); - mIsTouchExplorationEnabled = false; - mIsDisplayMagnificationEnabled = false; - mIsNavBarMagnificationEnabled = false; - mServiceAssignedToAccessibilityButton = null; - mIsNavBarMagnificationAssignedToAccessibilityButton = false; - mIsAutoclickEnabled = false; - mUserNonInteractiveUiTimeout = 0; - mUserInteractiveUiTimeout = 0; - } - - public void addServiceLocked(AccessibilityServiceConnection serviceConnection) { - if (!mBoundServices.contains(serviceConnection)) { - serviceConnection.onAdded(); - mBoundServices.add(serviceConnection); - mComponentNameToServiceMap.put(serviceConnection.mComponentName, serviceConnection); - scheduleNotifyClientsOfServicesStateChangeLocked(this); - } - } - - /** - * Removes a service. - * There are three states to a service here: off, bound, and binding. - * This stops tracking the service as bound. - * - * @param serviceConnection The service. - */ - public void removeServiceLocked(AccessibilityServiceConnection serviceConnection) { - mBoundServices.remove(serviceConnection); - serviceConnection.onRemoved(); - if ((mServiceChangingSoftKeyboardMode != null) - && (mServiceChangingSoftKeyboardMode.equals( - serviceConnection.getServiceInfo().getComponentName()))) { - setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null); - } - // It may be possible to bind a service twice, which confuses the map. Rebuild the map - // to make sure we can still reach a service - mComponentNameToServiceMap.clear(); - for (int i = 0; i < mBoundServices.size(); i++) { - AccessibilityServiceConnection boundClient = mBoundServices.get(i); - mComponentNameToServiceMap.put(boundClient.mComponentName, boundClient); - } - scheduleNotifyClientsOfServicesStateChangeLocked(this); - } - - /** - * Make sure a services disconnected but still 'on' state is reflected in UserState - * There are three states to a service here: off, bound, and binding. - * This drops a service from a bound state, to the binding state. - * The binding state describes the situation where a service is on, but not bound. - * - * @param serviceConnection The service. - */ - public void serviceDisconnectedLocked(AccessibilityServiceConnection serviceConnection) { - removeServiceLocked(serviceConnection); - mBindingServices.add(serviceConnection.getComponentName()); - } - - public Set<ComponentName> getBindingServicesLocked() { - return mBindingServices; - } - - /** - * Returns enabled service list. - */ - public Set<ComponentName> getEnabledServicesLocked() { - return mEnabledServices; - } - - public int getSoftKeyboardShowMode() { - return mSoftKeyboardShowMode; - } - - /** - * Set the soft keyboard mode. This mode is a bit odd, as it spans multiple settings. - * The ACCESSIBILITY_SOFT_KEYBOARD_MODE setting can be checked by the rest of the system - * to see if it should suppress showing the IME. The SHOW_IME_WITH_HARD_KEYBOARD setting - * setting can be changed by the user, and prevents the system from suppressing the soft - * keyboard when the hard keyboard is connected. The hard keyboard setting needs to defer - * to the user's preference, if they have supplied one. - * - * @param newMode The new mode - * @param requester The service requesting the change, so we can undo it when the - * service stops. Set to null if something other than a service is forcing - * the change. - * - * @return Whether or not the soft keyboard mode equals the new mode after the call - */ - public boolean setSoftKeyboardModeLocked(int newMode, @Nullable ComponentName requester) { - if ((newMode != SHOW_MODE_AUTO) && (newMode != SHOW_MODE_HIDDEN) - && (newMode != SHOW_MODE_IGNORE_HARD_KEYBOARD)) - { - Slog.w(LOG_TAG, "Invalid soft keyboard mode"); - return false; - } - if (mSoftKeyboardShowMode == newMode) return true; - - if (newMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) { - if (hasUserOverriddenHardKeyboardSettingLocked()) { - // The user has specified a default for this setting - return false; - } - // Save the original value. But don't do this if the value in settings is already - // the new mode. That happens when we start up after a reboot, and we don't want - // to overwrite the value we had from when we first started controlling the setting. - if (getSoftKeyboardValueFromSettings() != SHOW_MODE_IGNORE_HARD_KEYBOARD) { - setOriginalHardKeyboardValue( - Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0); - } - putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 1, mUserId); - } else if (mSoftKeyboardShowMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) { - putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, - getOriginalHardKeyboardValue() ? 1 : 0, mUserId); - } - - saveSoftKeyboardValueToSettings(newMode); - mSoftKeyboardShowMode = newMode; - mServiceChangingSoftKeyboardMode = requester; - notifySoftKeyboardShowModeChangedLocked(mSoftKeyboardShowMode); - return true; - } - - /** - * If the settings are inconsistent with the internal state, make the internal state - * match the settings. - */ - public void reconcileSoftKeyboardModeWithSettingsLocked() { - final ContentResolver cr = mContext.getContentResolver(); - final boolean showWithHardKeyboardSettings = - Settings.Secure.getInt(cr, Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0; - if (mSoftKeyboardShowMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) { - if (!showWithHardKeyboardSettings) { - // The user has overridden the setting. Respect that and prevent further changes - // to this behavior. - setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null); - setUserOverridesHardKeyboardSettingLocked(); - } - } - - // If the setting and the internal state are out of sync, set both to default - if (getSoftKeyboardValueFromSettings() != mSoftKeyboardShowMode) - { - Slog.e(LOG_TAG, - "Show IME setting inconsistent with internal state. Overwriting"); - setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null); - putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, - SHOW_MODE_AUTO, mUserId); - } - } - - private void setUserOverridesHardKeyboardSettingLocked() { - final int softKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0); - putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, - softKeyboardSetting | SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN, - mUserId); - } - - private boolean hasUserOverriddenHardKeyboardSettingLocked() { - final int softKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0); - return (softKeyboardSetting & SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN) - != 0; - } - - private void setOriginalHardKeyboardValue(boolean originalHardKeyboardValue) { - final int oldSoftKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0); - final int newSoftKeyboardSetting = oldSoftKeyboardSetting - & (~SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE) - | ((originalHardKeyboardValue) ? SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE : 0); - putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, - newSoftKeyboardSetting, mUserId); - } - - private void saveSoftKeyboardValueToSettings(int softKeyboardShowMode) { - final int oldSoftKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0); - final int newSoftKeyboardSetting = oldSoftKeyboardSetting & (~SHOW_MODE_MASK) - | softKeyboardShowMode; - putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, - newSoftKeyboardSetting, mUserId); - } - - private int getSoftKeyboardValueFromSettings() { - return Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, - SHOW_MODE_AUTO) & SHOW_MODE_MASK; - } - - private boolean getOriginalHardKeyboardValue() { - return (Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0) - & SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE) != 0; - } - - public boolean getBindInstantServiceAllowed() { - synchronized (mLock) { - return mBindInstantServiceAllowed; - } - } - - public void setBindInstantServiceAllowed(boolean allowed) { - synchronized (mLock) { - mContext.enforceCallingOrSelfPermission( - Manifest.permission.MANAGE_BIND_INSTANT_SERVICE, - "setBindInstantServiceAllowed"); - if (allowed) { - mBindInstantServiceAllowed = allowed; - onUserStateChangedLocked(this); - } - } - } - } - private final class AccessibilityContentObserver extends ContentObserver { private final Uri mTouchExplorationEnabledUri = Settings.Secure.getUriFor( @@ -3057,7 +2670,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub synchronized (mLock) { // Profiles share the accessibility state of the parent. Therefore, // we are checking for changes only the parent settings. - UserState userState = getCurrentUserStateLocked(); + AccessibilityUserState userState = getCurrentUserStateLocked(); if (mTouchExplorationEnabledUri.equals(uri)) { if (readTouchExplorationEnabledSettingLocked(userState)) { diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java index e7f3ccc9f883..d154060d0b73 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java @@ -35,7 +35,6 @@ import android.provider.Settings; import android.util.Slog; import android.view.Display; -import com.android.server.accessibility.AccessibilityManagerService.UserState; import com.android.server.wm.WindowManagerInternal; import java.lang.ref.WeakReference; @@ -52,13 +51,13 @@ import java.util.Set; class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnection { private static final String LOG_TAG = "AccessibilityServiceConnection"; /* - Holding a weak reference so there isn't a loop of references. UserState keeps lists of bound - and binding services. These are freed on user changes, but just in case it somehow gets lost - the weak reference will let the memory get GCed. + Holding a weak reference so there isn't a loop of references. AccessibilityUserState keeps + lists of bound and binding services. These are freed on user changes, but just in case it + somehow gets lost the weak reference will let the memory get GCed. Having the reference be null when being called is a very bad sign, but we check the condition. */ - final WeakReference<UserState> mUserStateWeakReference; + final WeakReference<AccessibilityUserState> mUserStateWeakReference; final Intent mIntent; private final Handler mMainHandler; @@ -66,7 +65,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect private boolean mWasConnectedAndDied; - public AccessibilityServiceConnection(UserState userState, Context context, + AccessibilityServiceConnection(AccessibilityUserState userState, Context context, ComponentName componentName, AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler, Object lock, AccessibilitySecurityPolicy securityPolicy, SystemSupport systemSupport, @@ -74,7 +73,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect SystemActionPerformer systemActionPerfomer, AccessibilityWindowManager awm) { super(context, componentName, accessibilityServiceInfo, id, mainHandler, lock, securityPolicy, systemSupport, windowManagerInternal, systemActionPerfomer, awm); - mUserStateWeakReference = new WeakReference<UserState>(userState); + mUserStateWeakReference = new WeakReference<AccessibilityUserState>(userState); mIntent = new Intent().setComponent(mComponentName); mMainHandler = mainHandler; mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL, @@ -89,13 +88,13 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect } public void bindLocked() { - UserState userState = mUserStateWeakReference.get(); + AccessibilityUserState userState = mUserStateWeakReference.get(); if (userState == null) return; final long identity = Binder.clearCallingIdentity(); try { int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS; - if (userState.getBindInstantServiceAllowed()) { + if (userState.getBindInstantServiceAllowedLocked()) { flags |= Context.BIND_ALLOW_INSTANT; } if (mService == null && mContext.bindServiceAsUser( @@ -109,7 +108,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect public void unbindLocked() { mContext.unbindService(this); - UserState userState = mUserStateWeakReference.get(); + AccessibilityUserState userState = mUserStateWeakReference.get(); if (userState == null) return; userState.removeServiceLocked(this); mSystemSupport.getMagnificationController().resetAllIfNeeded(mId); @@ -123,7 +122,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect @Override public void disableSelf() { synchronized (mLock) { - UserState userState = mUserStateWeakReference.get(); + AccessibilityUserState userState = mUserStateWeakReference.get(); if (userState == null) return; if (userState.getEnabledServicesLocked().remove(mComponentName)) { final long identity = Binder.clearCallingIdentity(); @@ -156,7 +155,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect } } mServiceInterface = IAccessibilityServiceClient.Stub.asInterface(service); - UserState userState = mUserStateWeakReference.get(); + AccessibilityUserState userState = mUserStateWeakReference.get(); if (userState == null) return; userState.addServiceLocked(this); mSystemSupport.onClientChangeLocked(false); @@ -177,7 +176,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect private void initializeService() { IAccessibilityServiceClient serviceInterface = null; synchronized (mLock) { - UserState userState = mUserStateWeakReference.get(); + AccessibilityUserState userState = mUserStateWeakReference.get(); if (userState == null) return; Set<ComponentName> bindingServices = userState.getBindingServicesLocked(); if (bindingServices.contains(mComponentName) || mWasConnectedAndDied) { @@ -240,7 +239,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect if (!hasRightsToCurrentUserLocked()) { return false; } - final UserState userState = mUserStateWeakReference.get(); + final AccessibilityUserState userState = mUserStateWeakReference.get(); if (userState == null) return false; return userState.setSoftKeyboardModeLocked(showMode, mComponentName); } @@ -248,8 +247,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect @Override public int getSoftKeyboardShowMode() { - final UserState userState = mUserStateWeakReference.get(); - return (userState != null) ? userState.getSoftKeyboardShowMode() : 0; + final AccessibilityUserState userState = mUserStateWeakReference.get(); + return (userState != null) ? userState.getSoftKeyboardShowModeLocked() : 0; } @Override @@ -258,7 +257,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect if (!hasRightsToCurrentUserLocked()) { return false; } - UserState userState = mUserStateWeakReference.get(); + AccessibilityUserState userState = mUserStateWeakReference.get(); return (userState != null) && isAccessibilityButtonAvailableLocked(userState); } } @@ -273,7 +272,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect return; } mWasConnectedAndDied = true; - UserState userState = mUserStateWeakReference.get(); + AccessibilityUserState userState = mUserStateWeakReference.get(); if (userState != null) { userState.serviceDisconnectedLocked(this); } @@ -283,7 +282,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect } } - public boolean isAccessibilityButtonAvailableLocked(UserState userState) { + public boolean isAccessibilityButtonAvailableLocked(AccessibilityUserState userState) { // If the service does not request the accessibility button, it isn't available if (!mRequestAccessibilityButton) { return false; @@ -295,8 +294,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect } // If magnification is on and assigned to the accessibility button, services cannot be - if (userState.mIsNavBarMagnificationEnabled - && userState.mIsNavBarMagnificationAssignedToAccessibilityButton) { + if (userState.isNavBarMagnificationEnabledLocked() + && userState.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) { return false; } @@ -314,13 +313,14 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect return true; } else { // With more than one active service, we derive the target from the user's settings - if (userState.mServiceAssignedToAccessibilityButton == null) { + if (userState.getServiceAssignedToAccessibilityButtonLocked() == null) { // If the user has not made an assignment, we treat the button as available to // all services until the user interacts with the button to make an assignment return true; } else { // If an assignment was made, it defines availability - return mComponentName.equals(userState.mServiceAssignedToAccessibilityButton); + return mComponentName.equals( + userState.getServiceAssignedToAccessibilityButtonLocked()); } } } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java new file mode 100644 index 000000000000..ec15a803cae6 --- /dev/null +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java @@ -0,0 +1,571 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.accessibility; + +import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO; +import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE; +import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN; +import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN; +import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD; +import static android.accessibilityservice.AccessibilityService.SHOW_MODE_MASK; + +import android.accessibilityservice.AccessibilityService.SoftKeyboardShowMode; +import android.accessibilityservice.AccessibilityServiceInfo; +import android.accessibilityservice.AccessibilityShortcutInfo; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ComponentName; +import android.content.Context; +import android.os.Binder; +import android.os.RemoteCallbackList; +import android.provider.Settings; +import android.util.Slog; +import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.IAccessibilityManagerClient; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Class that hold states and settings per user and share between + * {@link AccessibilityManagerService} and {@link AccessibilityServiceConnection}. + */ +class AccessibilityUserState { + private static final String LOG_TAG = AccessibilityUserState.class.getSimpleName(); + + final int mUserId; + + // Non-transient state. + + final RemoteCallbackList<IAccessibilityManagerClient> mUserClients = new RemoteCallbackList<>(); + + // Transient state. + + final ArrayList<AccessibilityServiceConnection> mBoundServices = new ArrayList<>(); + + final Map<ComponentName, AccessibilityServiceConnection> mComponentNameToServiceMap = + new HashMap<>(); + + final List<AccessibilityServiceInfo> mInstalledServices = new ArrayList<>(); + + final List<AccessibilityShortcutInfo> mInstalledShortcuts = new ArrayList<>(); + + final Set<ComponentName> mBindingServices = new HashSet<>(); + + final Set<ComponentName> mEnabledServices = new HashSet<>(); + + final Set<ComponentName> mTouchExplorationGrantedServices = new HashSet<>(); + + private final ServiceInfoChangeListener mServiceInfoChangeListener; + + private ComponentName mServiceAssignedToAccessibilityButton; + + private ComponentName mServiceChangingSoftKeyboardMode; + + private ComponentName mServiceToEnableWithShortcut; + + private boolean mBindInstantServiceAllowed; + private boolean mIsAutoclickEnabled; + private boolean mIsDisplayMagnificationEnabled; + private boolean mIsFilterKeyEventsEnabled; + private boolean mIsNavBarMagnificationAssignedToAccessibilityButton; + private boolean mIsNavBarMagnificationEnabled; + private boolean mIsPerformGesturesEnabled; + private boolean mIsTextHighContrastEnabled; + private boolean mIsTouchExplorationEnabled; + private int mUserInteractiveUiTimeout; + private int mUserNonInteractiveUiTimeout; + private int mNonInteractiveUiTimeout = 0; + private int mInteractiveUiTimeout = 0; + private int mLastSentClientState = -1; + + private Context mContext; + + @SoftKeyboardShowMode + private int mSoftKeyboardShowMode = SHOW_MODE_AUTO; + + interface ServiceInfoChangeListener { + void onServiceInfoChangedLocked(AccessibilityUserState userState); + } + + AccessibilityUserState(int userId, @NonNull Context context, + @NonNull ServiceInfoChangeListener serviceInfoChangeListener) { + mUserId = userId; + mContext = context; + mServiceInfoChangeListener = serviceInfoChangeListener; + } + + boolean isHandlingAccessibilityEventsLocked() { + return !mBoundServices.isEmpty() || !mBindingServices.isEmpty(); + } + + void onSwitchToAnotherUserLocked() { + // Unbind all services. + unbindAllServicesLocked(); + + // Clear service management state. + mBoundServices.clear(); + mBindingServices.clear(); + + // Clear event management state. + mLastSentClientState = -1; + + // clear UI timeout + mNonInteractiveUiTimeout = 0; + mInteractiveUiTimeout = 0; + + // Clear state persisted in settings. + mEnabledServices.clear(); + mTouchExplorationGrantedServices.clear(); + mIsTouchExplorationEnabled = false; + mIsDisplayMagnificationEnabled = false; + mIsNavBarMagnificationEnabled = false; + mServiceAssignedToAccessibilityButton = null; + mIsNavBarMagnificationAssignedToAccessibilityButton = false; + mIsAutoclickEnabled = false; + mUserNonInteractiveUiTimeout = 0; + mUserInteractiveUiTimeout = 0; + } + + void addServiceLocked(AccessibilityServiceConnection serviceConnection) { + if (!mBoundServices.contains(serviceConnection)) { + serviceConnection.onAdded(); + mBoundServices.add(serviceConnection); + mComponentNameToServiceMap.put(serviceConnection.getComponentName(), serviceConnection); + mServiceInfoChangeListener.onServiceInfoChangedLocked(this); + } + } + + /** + * Removes a service. + * There are three states to a service here: off, bound, and binding. + * This stops tracking the service as bound. + * + * @param serviceConnection The service. + */ + void removeServiceLocked(AccessibilityServiceConnection serviceConnection) { + mBoundServices.remove(serviceConnection); + serviceConnection.onRemoved(); + if ((mServiceChangingSoftKeyboardMode != null) + && (mServiceChangingSoftKeyboardMode.equals( + serviceConnection.getServiceInfo().getComponentName()))) { + setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null); + } + // It may be possible to bind a service twice, which confuses the map. Rebuild the map + // to make sure we can still reach a service + mComponentNameToServiceMap.clear(); + for (int i = 0; i < mBoundServices.size(); i++) { + AccessibilityServiceConnection boundClient = mBoundServices.get(i); + mComponentNameToServiceMap.put(boundClient.getComponentName(), boundClient); + } + mServiceInfoChangeListener.onServiceInfoChangedLocked(this); + } + + /** + * Make sure a services disconnected but still 'on' state is reflected in AccessibilityUserState + * There are three states to a service here: off, bound, and binding. + * This drops a service from a bound state, to the binding state. + * The binding state describes the situation where a service is on, but not bound. + * + * @param serviceConnection The service. + */ + void serviceDisconnectedLocked(AccessibilityServiceConnection serviceConnection) { + removeServiceLocked(serviceConnection); + mBindingServices.add(serviceConnection.getComponentName()); + } + + /** + * Set the soft keyboard mode. This mode is a bit odd, as it spans multiple settings. + * The ACCESSIBILITY_SOFT_KEYBOARD_MODE setting can be checked by the rest of the system + * to see if it should suppress showing the IME. The SHOW_IME_WITH_HARD_KEYBOARD setting + * setting can be changed by the user, and prevents the system from suppressing the soft + * keyboard when the hard keyboard is connected. The hard keyboard setting needs to defer + * to the user's preference, if they have supplied one. + * + * @param newMode The new mode + * @param requester The service requesting the change, so we can undo it when the + * service stops. Set to null if something other than a service is forcing + * the change. + * + * @return Whether or not the soft keyboard mode equals the new mode after the call + */ + boolean setSoftKeyboardModeLocked(@SoftKeyboardShowMode int newMode, + @Nullable ComponentName requester) { + if ((newMode != SHOW_MODE_AUTO) + && (newMode != SHOW_MODE_HIDDEN) + && (newMode != SHOW_MODE_IGNORE_HARD_KEYBOARD)) { + Slog.w(LOG_TAG, "Invalid soft keyboard mode"); + return false; + } + if (mSoftKeyboardShowMode == newMode) { + return true; + } + + if (newMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) { + if (hasUserOverriddenHardKeyboardSetting()) { + // The user has specified a default for this setting + return false; + } + // Save the original value. But don't do this if the value in settings is already + // the new mode. That happens when we start up after a reboot, and we don't want + // to overwrite the value we had from when we first started controlling the setting. + if (getSoftKeyboardValueFromSettings() != SHOW_MODE_IGNORE_HARD_KEYBOARD) { + setOriginalHardKeyboardValue(getSecureInt( + Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0); + } + putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 1, mUserId); + } else if (mSoftKeyboardShowMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) { + putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, + getOriginalHardKeyboardValue() ? 1 : 0, mUserId); + } + + saveSoftKeyboardValueToSettings(newMode); + mSoftKeyboardShowMode = newMode; + mServiceChangingSoftKeyboardMode = requester; + for (int i = mBoundServices.size() - 1; i >= 0; i--) { + final AccessibilityServiceConnection service = mBoundServices.get(i); + service.notifySoftKeyboardShowModeChangedLocked(mSoftKeyboardShowMode); + } + return true; + } + + @SoftKeyboardShowMode + int getSoftKeyboardShowModeLocked() { + return mSoftKeyboardShowMode; + } + + /** + * If the settings are inconsistent with the internal state, make the internal state + * match the settings. + */ + void reconcileSoftKeyboardModeWithSettingsLocked() { + final boolean showWithHardKeyboardSettings = + getSecureInt(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0; + if (mSoftKeyboardShowMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) { + if (!showWithHardKeyboardSettings) { + // The user has overridden the setting. Respect that and prevent further changes + // to this behavior. + setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null); + setUserOverridesHardKeyboardSetting(); + } + } + + // If the setting and the internal state are out of sync, set both to default + if (getSoftKeyboardValueFromSettings() != mSoftKeyboardShowMode) { + Slog.e(LOG_TAG, "Show IME setting inconsistent with internal state. Overwriting"); + setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null); + putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, + SHOW_MODE_AUTO, mUserId); + } + } + + boolean getBindInstantServiceAllowedLocked() { + return mBindInstantServiceAllowed; + } + + /* Need to have a permission check on callee */ + void setBindInstantServiceAllowedLocked(boolean allowed) { + mBindInstantServiceAllowed = allowed; + } + + Set<ComponentName> getBindingServicesLocked() { + return mBindingServices; + } + + /** + * Returns enabled service list. + */ + Set<ComponentName> getEnabledServicesLocked() { + return mEnabledServices; + } + + List<AccessibilityServiceConnection> getBoundServicesLocked() { + return mBoundServices; + } + + int getClientStateLocked(boolean isUiAutomationRunning) { + int clientState = 0; + final boolean a11yEnabled = isUiAutomationRunning + || isHandlingAccessibilityEventsLocked(); + if (a11yEnabled) { + clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED; + } + // Touch exploration relies on enabled accessibility. + if (a11yEnabled && mIsTouchExplorationEnabled) { + clientState |= AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED; + } + if (mIsTextHighContrastEnabled) { + clientState |= AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED; + } + return clientState; + } + + private void setUserOverridesHardKeyboardSetting() { + final int softKeyboardSetting = getSecureInt( + Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO); + putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, + softKeyboardSetting | SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN, + mUserId); + } + + private boolean hasUserOverriddenHardKeyboardSetting() { + final int softKeyboardSetting = getSecureInt( + Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO); + return (softKeyboardSetting & SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN) + != 0; + } + + private void setOriginalHardKeyboardValue(boolean originalHardKeyboardValue) { + final int oldSoftKeyboardSetting = getSecureInt( + Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO); + final int newSoftKeyboardSetting = oldSoftKeyboardSetting + & (~SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE) + | ((originalHardKeyboardValue) ? SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE : 0); + putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, + newSoftKeyboardSetting, mUserId); + } + + private void saveSoftKeyboardValueToSettings(int softKeyboardShowMode) { + final int oldSoftKeyboardSetting = getSecureInt( + Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO); + final int newSoftKeyboardSetting = oldSoftKeyboardSetting & (~SHOW_MODE_MASK) + | softKeyboardShowMode; + putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, + newSoftKeyboardSetting, mUserId); + } + + private int getSoftKeyboardValueFromSettings() { + return getSecureInt(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO) + & SHOW_MODE_MASK; + } + + private boolean getOriginalHardKeyboardValue() { + return (getSecureInt(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO) + & SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE) != 0; + } + + private void unbindAllServicesLocked() { + final List<AccessibilityServiceConnection> services = mBoundServices; + for (int count = services.size(); count > 0; count--) { + // When the service is unbound, it disappears from the list, so there's no need to + // keep track of the index + services.get(0).unbindLocked(); + } + } + + private int getSecureInt(String key, int def) { + return Settings.Secure.getInt(mContext.getContentResolver(), key, def); + } + + private void putSecureIntForUser(String key, int value, int userId) { + final long identity = Binder.clearCallingIdentity(); + try { + Settings.Secure.putIntForUser(mContext.getContentResolver(), key, value, userId); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.append("User state["); + pw.println(); + pw.append(" attributes:{id=").append(String.valueOf(mUserId)); + pw.append(", touchExplorationEnabled=").append(String.valueOf(mIsTouchExplorationEnabled)); + pw.append(", displayMagnificationEnabled=").append(String.valueOf( + mIsDisplayMagnificationEnabled)); + pw.append(", navBarMagnificationEnabled=").append(String.valueOf( + mIsNavBarMagnificationEnabled)); + pw.append(", autoclickEnabled=").append(String.valueOf(mIsAutoclickEnabled)); + pw.append(", nonInteractiveUiTimeout=").append(String.valueOf(mNonInteractiveUiTimeout)); + pw.append(", interactiveUiTimeout=").append(String.valueOf(mInteractiveUiTimeout)); + pw.append(", installedServiceCount=").append(String.valueOf(mInstalledServices.size())); + pw.append("}"); + pw.println(); + pw.append(" Bound services:{"); + final int serviceCount = mBoundServices.size(); + for (int j = 0; j < serviceCount; j++) { + if (j > 0) { + pw.append(", "); + pw.println(); + pw.append(" "); + } + AccessibilityServiceConnection service = mBoundServices.get(j); + service.dump(fd, pw, args); + } + pw.println("}"); + pw.append(" Enabled services:{"); + Iterator<ComponentName> it = mEnabledServices.iterator(); + if (it.hasNext()) { + ComponentName componentName = it.next(); + pw.append(componentName.toShortString()); + while (it.hasNext()) { + componentName = it.next(); + pw.append(", "); + pw.append(componentName.toShortString()); + } + } + pw.println("}"); + pw.append(" Binding services:{"); + it = mBindingServices.iterator(); + if (it.hasNext()) { + ComponentName componentName = it.next(); + pw.append(componentName.toShortString()); + while (it.hasNext()) { + componentName = it.next(); + pw.append(", "); + pw.append(componentName.toShortString()); + } + } + pw.println("}]"); + } + + public boolean isAutoclickEnabledLocked() { + return mIsAutoclickEnabled; + } + + public void setAutoclickEnabledLocked(boolean enabled) { + mIsAutoclickEnabled = enabled; + } + + public boolean isDisplayMagnificationEnabledLocked() { + return mIsDisplayMagnificationEnabled; + } + + public void setDisplayMagnificationEnabledLocked(boolean enabled) { + mIsDisplayMagnificationEnabled = enabled; + } + + public boolean isFilterKeyEventsEnabledLocked() { + return mIsFilterKeyEventsEnabled; + } + + public void setFilterKeyEventsEnabledLocked(boolean enabled) { + mIsFilterKeyEventsEnabled = enabled; + } + + public int getInteractiveUiTimeoutLocked() { + return mInteractiveUiTimeout; + } + + public void setInteractiveUiTimeoutLocked(int timeout) { + mInteractiveUiTimeout = timeout; + } + + public int getLastSentClientStateLocked() { + return mLastSentClientState; + } + + public void setLastSentClientStateLocked(int state) { + mLastSentClientState = state; + } + + public boolean isNavBarMagnificationAssignedToAccessibilityButtonLocked() { + return mIsNavBarMagnificationAssignedToAccessibilityButton; + } + + public void setNavBarMagnificationAssignedToAccessibilityButtonLocked(boolean assigned) { + mIsNavBarMagnificationAssignedToAccessibilityButton = assigned; + } + + public boolean isNavBarMagnificationEnabledLocked() { + return mIsNavBarMagnificationEnabled; + } + + public void setNavBarMagnificationEnabledLocked(boolean enabled) { + mIsNavBarMagnificationEnabled = enabled; + } + + public int getNonInteractiveUiTimeoutLocked() { + return mNonInteractiveUiTimeout; + } + + public void setNonInteractiveUiTimeoutLocked(int timeout) { + mNonInteractiveUiTimeout = timeout; + } + + public boolean isPerformGesturesEnabledLocked() { + return mIsPerformGesturesEnabled; + } + + public void setPerformGesturesEnabledLocked(boolean enabled) { + mIsPerformGesturesEnabled = enabled; + } + + public ComponentName getServiceAssignedToAccessibilityButtonLocked() { + return mServiceAssignedToAccessibilityButton; + } + + public void setServiceAssignedToAccessibilityButtonLocked(ComponentName componentName) { + mServiceAssignedToAccessibilityButton = componentName; + } + + public ComponentName getServiceChangingSoftKeyboardModeLocked() { + return mServiceChangingSoftKeyboardMode; + } + + public void setServiceChangingSoftKeyboardModeLocked( + ComponentName serviceChangingSoftKeyboardMode) { + mServiceChangingSoftKeyboardMode = serviceChangingSoftKeyboardMode; + } + + public ComponentName getServiceToEnableWithShortcutLocked() { + return mServiceToEnableWithShortcut; + } + + public void setServiceToEnableWithShortcutLocked(ComponentName componentName) { + mServiceToEnableWithShortcut = componentName; + } + + public boolean isTextHighContrastEnabledLocked() { + return mIsTextHighContrastEnabled; + } + + public void setTextHighContrastEnabledLocked(boolean enabled) { + mIsTextHighContrastEnabled = enabled; + } + + public boolean isTouchExplorationEnabledLocked() { + return mIsTouchExplorationEnabled; + } + + public void setTouchExplorationEnabledLocked(boolean enabled) { + mIsTouchExplorationEnabled = enabled; + } + + public int getUserInteractiveUiTimeoutLocked() { + return mUserInteractiveUiTimeout; + } + + public void setUserInteractiveUiTimeoutLocked(int timeout) { + mUserInteractiveUiTimeout = timeout; + } + + public int getUserNonInteractiveUiTimeoutLocked() { + return mUserNonInteractiveUiTimeout; + } + + public void setUserNonInteractiveUiTimeoutLocked(int timeout) { + mUserNonInteractiveUiTimeout = timeout; + } +} diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java index edf82ee30e2d..597d337c2450 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java @@ -41,12 +41,14 @@ import android.content.pm.ServiceInfo; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; +import android.testing.DexmakerShareClassLoaderRule; import android.view.Display; import com.android.server.wm.WindowManagerInternal; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -65,9 +67,14 @@ public class AccessibilityServiceConnectionTest { "com.android.server.accessibility", "AccessibilityServiceConnectionTest"); static final int SERVICE_ID = 42; + // Mock package-private AccessibilityUserState class + @Rule + public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule = + new DexmakerShareClassLoaderRule(); + AccessibilityServiceConnection mConnection; - @Mock AccessibilityManagerService.UserState mMockUserState; + @Mock AccessibilityUserState mMockUserState; @Mock Context mMockContext; @Mock AccessibilityServiceInfo mMockServiceInfo; @Mock ResolveInfo mMockResolveInfo; diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java new file mode 100644 index 000000000000..b642aa4500b7 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.accessibility; + +import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO; +import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE; +import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN; +import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN; +import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD; +import static android.view.accessibility.AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED; +import static android.view.accessibility.AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED; +import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNull; +import static junit.framework.Assert.assertTrue; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.accessibilityservice.AccessibilityServiceInfo; +import android.content.ComponentName; +import android.content.Context; +import android.provider.Settings; +import android.test.mock.MockContentResolver; +import android.testing.DexmakerShareClassLoaderRule; + +import com.android.internal.util.test.FakeSettingsProvider; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** Tests for AccessibilityUserState */ +public class AccessibilityUserStateTest { + + private static final ComponentName COMPONENT_NAME = + new ComponentName("com.android.server.accessibility", "AccessibilityUserStateTest"); + + // Values of setting key SHOW_IME_WITH_HARD_KEYBOARD + private static final int STATE_HIDE_IME = 0; + private static final int STATE_SHOW_IME = 1; + + private static final int USER_ID = 42; + + // Mock package-private class AccessibilityServiceConnection + @Rule public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule = + new DexmakerShareClassLoaderRule(); + + @Mock private AccessibilityServiceInfo mMockServiceInfo; + + @Mock private AccessibilityServiceConnection mMockConnection; + + @Mock private AccessibilityUserState.ServiceInfoChangeListener mMockListener; + + @Mock private Context mContext; + + private MockContentResolver mMockResolver; + + private AccessibilityUserState mUserState; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + FakeSettingsProvider.clearSettingsProvider(); + mMockResolver = new MockContentResolver(); + mMockResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); + when(mContext.getContentResolver()).thenReturn(mMockResolver); + when(mMockServiceInfo.getComponentName()).thenReturn(COMPONENT_NAME); + when(mMockConnection.getServiceInfo()).thenReturn(mMockServiceInfo); + + mUserState = new AccessibilityUserState(USER_ID, mContext, mMockListener); + } + + @After + public void tearDown() { + FakeSettingsProvider.clearSettingsProvider(); + } + + @Test + public void onSwitchToAnotherUser_userStateClearedNonDefaultValues() { + mUserState.getBoundServicesLocked().add(mMockConnection); + mUserState.getBindingServicesLocked().add(COMPONENT_NAME); + mUserState.setLastSentClientStateLocked( + STATE_FLAG_ACCESSIBILITY_ENABLED + | STATE_FLAG_TOUCH_EXPLORATION_ENABLED + | STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED); + mUserState.setNonInteractiveUiTimeoutLocked(30); + mUserState.setInteractiveUiTimeoutLocked(30); + mUserState.mEnabledServices.add(COMPONENT_NAME); + mUserState.mTouchExplorationGrantedServices.add(COMPONENT_NAME); + mUserState.setTouchExplorationEnabledLocked(true); + mUserState.setDisplayMagnificationEnabledLocked(true); + mUserState.setNavBarMagnificationEnabledLocked(true); + mUserState.setServiceAssignedToAccessibilityButtonLocked(COMPONENT_NAME); + mUserState.setNavBarMagnificationAssignedToAccessibilityButtonLocked(true); + mUserState.setAutoclickEnabledLocked(true); + mUserState.setUserNonInteractiveUiTimeoutLocked(30); + mUserState.setUserInteractiveUiTimeoutLocked(30); + + mUserState.onSwitchToAnotherUserLocked(); + + verify(mMockConnection).unbindLocked(); + assertTrue(mUserState.getBoundServicesLocked().isEmpty()); + assertTrue(mUserState.getBindingServicesLocked().isEmpty()); + assertEquals(-1, mUserState.getLastSentClientStateLocked()); + assertEquals(0, mUserState.getNonInteractiveUiTimeoutLocked()); + assertEquals(0, mUserState.getInteractiveUiTimeoutLocked()); + assertTrue(mUserState.mEnabledServices.isEmpty()); + assertTrue(mUserState.mTouchExplorationGrantedServices.isEmpty()); + assertFalse(mUserState.isTouchExplorationEnabledLocked()); + assertFalse(mUserState.isDisplayMagnificationEnabledLocked()); + assertFalse(mUserState.isNavBarMagnificationEnabledLocked()); + assertNull(mUserState.getServiceAssignedToAccessibilityButtonLocked()); + assertFalse(mUserState.isNavBarMagnificationAssignedToAccessibilityButtonLocked()); + assertFalse(mUserState.isAutoclickEnabledLocked()); + assertEquals(0, mUserState.getUserNonInteractiveUiTimeoutLocked()); + assertEquals(0, mUserState.getUserInteractiveUiTimeoutLocked()); + } + + @Test + public void addService_connectionAlreadyAdded_notAddAgain() { + mUserState.getBoundServicesLocked().add(mMockConnection); + + mUserState.addServiceLocked(mMockConnection); + + verify(mMockConnection, never()).onAdded(); + } + + @Test + public void addService_connectionNotYetAddedToBoundService_addAndNotifyServices() { + when(mMockConnection.getComponentName()).thenReturn(COMPONENT_NAME); + + mUserState.addServiceLocked(mMockConnection); + + verify(mMockConnection).onAdded(); + assertTrue(mUserState.getBoundServicesLocked().contains(mMockConnection)); + assertEquals(mMockConnection, mUserState.mComponentNameToServiceMap.get(COMPONENT_NAME)); + verify(mMockListener).onServiceInfoChangedLocked(eq(mUserState)); + } + + @Test + public void reconcileSoftKeyboardMode_whenStateNotMatchSettings_setBothDefault() { + // When soft kb show mode is hidden in settings and is auto in state. + putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, + SHOW_MODE_HIDDEN, USER_ID); + + mUserState.reconcileSoftKeyboardModeWithSettingsLocked(); + + assertEquals(SHOW_MODE_AUTO, mUserState.getSoftKeyboardShowModeLocked()); + assertEquals(SHOW_MODE_AUTO, + getSecureInt(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE)); + assertNull(mUserState.getServiceChangingSoftKeyboardModeLocked()); + } + + @Test + public void + reconcileSoftKeyboardMode_stateIgnoreHardKb_settingsShowImeHardKb_setAutoOverride() { + // When show mode is ignore hard kb without original hard kb value + // and show ime with hard kb is hide + putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, + SHOW_MODE_IGNORE_HARD_KEYBOARD, USER_ID); + mUserState.setSoftKeyboardModeLocked(SHOW_MODE_IGNORE_HARD_KEYBOARD, COMPONENT_NAME); + putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, + STATE_HIDE_IME, USER_ID); + + mUserState.reconcileSoftKeyboardModeWithSettingsLocked(); + + assertEquals(SHOW_MODE_AUTO | SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN, + getSecureInt(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE)); + assertNull(mUserState.getServiceChangingSoftKeyboardModeLocked()); + } + + @Test + public void removeService_serviceChangingSoftKeyboardMode_removeAndSetSoftKbModeAuto() { + mUserState.setServiceChangingSoftKeyboardModeLocked(COMPONENT_NAME); + mUserState.mComponentNameToServiceMap.put(COMPONENT_NAME, mMockConnection); + mUserState.setSoftKeyboardModeLocked(SHOW_MODE_HIDDEN, COMPONENT_NAME); + + mUserState.removeServiceLocked(mMockConnection); + + assertFalse(mUserState.getBoundServicesLocked().contains(mMockConnection)); + verify(mMockConnection).onRemoved(); + assertEquals(SHOW_MODE_AUTO, mUserState.getSoftKeyboardShowModeLocked()); + assertNull(mUserState.mComponentNameToServiceMap.get(COMPONENT_NAME)); + verify(mMockListener).onServiceInfoChangedLocked(eq(mUserState)); + } + + @Test + public void serviceDisconnected_removeServiceAndAddToBinding() { + when(mMockConnection.getComponentName()).thenReturn(COMPONENT_NAME); + mUserState.addServiceLocked(mMockConnection); + + mUserState.serviceDisconnectedLocked(mMockConnection); + + assertFalse(mUserState.getBoundServicesLocked().contains(mMockConnection)); + assertTrue(mUserState.getBindingServicesLocked().contains(COMPONENT_NAME)); + } + + @Test + public void setSoftKeyboardMode_withInvalidShowMode_shouldKeepDefaultAuto() { + final int invalidShowMode = SHOW_MODE_HIDDEN | SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE; + + assertFalse(mUserState.setSoftKeyboardModeLocked(invalidShowMode, null)); + + assertEquals(SHOW_MODE_AUTO, mUserState.getSoftKeyboardShowModeLocked()); + } + + @Test + public void setSoftKeyboardMode_newModeSameWithCurrentState_returnTrue() { + when(mMockConnection.getComponentName()).thenReturn(COMPONENT_NAME); + mUserState.addServiceLocked(mMockConnection); + + assertTrue(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null)); + } + + @Test + public void setSoftKeyboardMode_withIgnoreHardKb_whenHardKbOverridden_returnFalseAdNoChange() { + putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, + SHOW_MODE_AUTO | SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN, USER_ID); + + assertFalse(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_IGNORE_HARD_KEYBOARD, null)); + + assertEquals(SHOW_MODE_AUTO, mUserState.getSoftKeyboardShowModeLocked()); + } + + @Test + public void + setSoftKeyboardMode_withIgnoreHardKb_whenShowImeWithHardKb_setOriginalHardKbValue() { + putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, STATE_SHOW_IME, USER_ID); + + assertTrue(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_IGNORE_HARD_KEYBOARD, null)); + + assertEquals(SHOW_MODE_IGNORE_HARD_KEYBOARD | SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE, + getSecureInt(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE)); + } + + @Test + public void setSoftKeyboardMode_whenCurrentIgnoreHardKb_shouldSetShowImeWithHardKbValue() { + mUserState.setSoftKeyboardModeLocked(SHOW_MODE_IGNORE_HARD_KEYBOARD, COMPONENT_NAME); + putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, STATE_HIDE_IME, USER_ID); + putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, + SHOW_MODE_IGNORE_HARD_KEYBOARD | SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE, USER_ID); + + assertTrue(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null)); + + assertEquals(STATE_SHOW_IME, getSecureInt(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD)); + } + + @Test + public void setSoftKeyboardMode_withRequester_shouldUpdateInternalStateAndSettingsAsIs() { + assertTrue(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_HIDDEN, COMPONENT_NAME)); + + assertEquals(SHOW_MODE_HIDDEN, mUserState.getSoftKeyboardShowModeLocked()); + assertEquals(SHOW_MODE_HIDDEN, + getSecureInt(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE)); + assertEquals(COMPONENT_NAME, mUserState.getServiceChangingSoftKeyboardModeLocked()); + } + + @Test + public void setSoftKeyboardMode_shouldNotifyBoundService() { + mUserState.addServiceLocked(mMockConnection); + + assertTrue(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_HIDDEN, COMPONENT_NAME)); + + verify(mMockConnection).notifySoftKeyboardShowModeChangedLocked(eq(SHOW_MODE_HIDDEN)); + } + + private int getSecureInt(String key) { + return Settings.Secure.getInt(mMockResolver, key, -1); + } + + private void putSecureIntForUser(String key, int value, int userId) { + Settings.Secure.putIntForUser(mMockResolver, key, value, userId); + } +} diff --git a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java index deb6f71c695b..8da927dcb4ab 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java @@ -56,7 +56,6 @@ public class UiAutomationManagerTest { MessageCapturingHandler mMessageCapturingHandler; - @Mock AccessibilityManagerService.UserState mMockUserState; @Mock Context mMockContext; @Mock AccessibilityServiceInfo mMockServiceInfo; @Mock ResolveInfo mMockResolveInfo; |