Merge changes from topic "rhedjao_a11y_button_rollback" into rvc-dev

* changes:
  Rollback the changes of a11y button
  Migrates the navi-bar magnification setting key
This commit is contained in:
Jason Hsu
2020-04-24 03:07:39 +00:00
committed by Android (Google) Code Review
5 changed files with 169 additions and 33 deletions

View File

@@ -4692,24 +4692,16 @@ public class SettingsProvider extends ContentProvider {
if (currentVersion == 185) {
// Deprecate ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, and migrate it
// to ACCESSIBILITY_BUTTON_TARGET_COMPONENT.
// to ACCESSIBILITY_BUTTON_TARGETS.
final SettingsState secureSettings = getSecureSettingsLocked(userId);
final Setting magnifyNavbarEnabled = secureSettings.getSettingLocked(
Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
if ("1".equals(magnifyNavbarEnabled.getValue())) {
secureSettings.insertSettingLocked(
Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
Secure.ACCESSIBILITY_BUTTON_TARGETS,
ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER,
null /* tag */, false /* makeDefault */,
SettingsState.SYSTEM_PACKAGE_NAME);
} else {
// Clear a11y button targets list setting. A11yManagerService will end up
// adding all legacy enabled services that want the button to the list, so
// there's no need to keep tracking them.
secureSettings.insertSettingLocked(
Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
null, null /* tag */, false /* makeDefault */,
SettingsState.SYSTEM_PACKAGE_NAME);
}
secureSettings.deleteSettingLocked(
Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);

View File

@@ -1081,6 +1081,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
pw.append(", eventTypes="
+ AccessibilityEvent.eventTypeToString(mEventTypes));
pw.append(", notificationTimeout=" + mNotificationTimeout);
pw.append(", requestA11yBtn=" + mRequestAccessibilityButton);
pw.append("]");
}
}

View File

@@ -23,6 +23,7 @@ import static android.view.accessibility.AccessibilityManager.ShortcutType;
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
import static com.android.internal.util.FunctionalUtils.ignoreRemoteException;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.server.accessibility.AccessibilityUserState.doesShortcutTargetsStringContain;
import android.Manifest;
import android.accessibilityservice.AccessibilityGestureEvent;
@@ -876,6 +877,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
throw new SecurityException("Caller does not hold permission "
+ android.Manifest.permission.STATUS_BAR_SERVICE);
}
if (targetName == null) {
synchronized (mLock) {
final AccessibilityUserState userState = getCurrentUserStateLocked();
targetName = userState.getTargetAssignedToAccessibilityButton();
}
}
mMainHandler.sendMessage(obtainMessage(
AccessibilityManagerService::performAccessibilityShortcutInternal, this,
displayId, ACCESSIBILITY_BUTTON, targetName));
@@ -1828,7 +1835,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
somethingChanged |= readMagnificationEnabledSettingsLocked(userState);
somethingChanged |= readAutoclickEnabledSettingLocked(userState);
somethingChanged |= readAccessibilityShortcutKeySettingLocked(userState);
somethingChanged |= readAccessibilityButtonSettingsLocked(userState);
somethingChanged |= readAccessibilityButtonTargetsLocked(userState);
somethingChanged |= readAccessibilityButtonTargetComponentLocked(userState);
somethingChanged |= readUserRecommendedUiTimeoutSettingsLocked(userState);
return somethingChanged;
}
@@ -1948,9 +1956,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return true;
}
private boolean readAccessibilityButtonSettingsLocked(AccessibilityUserState userState) {
private boolean readAccessibilityButtonTargetsLocked(AccessibilityUserState userState) {
final Set<String> targetsFromSetting = new ArraySet<>();
readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
userState.mUserId, targetsFromSetting, str -> str);
final Set<String> currentTargets =
@@ -1964,6 +1972,23 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return true;
}
private boolean readAccessibilityButtonTargetComponentLocked(AccessibilityUserState userState) {
final String componentId = Settings.Secure.getStringForUser(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, userState.mUserId);
if (TextUtils.isEmpty(componentId)) {
if (userState.getTargetAssignedToAccessibilityButton() == null) {
return false;
}
userState.setTargetAssignedToAccessibilityButton(null);
return true;
}
if (componentId.equals(userState.getTargetAssignedToAccessibilityButton())) {
return false;
}
userState.setTargetAssignedToAccessibilityButton(componentId);
return true;
}
private boolean readUserRecommendedUiTimeoutSettingsLocked(AccessibilityUserState userState) {
final int nonInteractiveUiTimeout = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
@@ -1984,7 +2009,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
/**
* Check if the targets that will be enabled by the accessibility shortcut key is installed.
* Check if the target that will be enabled by the accessibility shortcut key is installed.
* If it isn't, remove it from the list and associated setting so a side loaded service can't
* spoof the package name of the default service.
*/
@@ -2145,7 +2170,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
/**
* 1) Update accessibility button availability to accessibility services.
* 2) Check if the targets that will be enabled by the accessibility button is installed.
* 2) Check if the target that will be enabled by the accessibility button is installed.
* If it isn't, remove it from the list and associated setting so a side loaded service can't
* spoof the package name of the default service.
*/
@@ -2172,8 +2197,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
// Update setting key with new value.
persistColonDelimitedSetToSettingLocked(
Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
persistColonDelimitedSetToSettingLocked(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
userState.mUserId, currentTargets, str -> str);
scheduleNotifyClientsOfServicesStateChangeLocked(userState);
}
@@ -2182,7 +2206,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
* 1) Check if the service assigned to accessibility button target sdk version > Q.
* If it isn't, remove it from the list and associated setting.
* (It happens when an accessibility service package is downgraded.)
* 2) Check if an enabled service targeting sdk version > Q and requesting a11y button is
* 2) For a service targeting sdk version > Q and requesting a11y button, it should be in the
* enabled list if's assigned to a11y button.
* (It happens when an accessibility service package is same graded, and updated requesting
* a11y button flag)
* 3) Check if an enabled service targeting sdk version > Q and requesting a11y button is
* assigned to a shortcut. If it isn't, assigns it to the accessibility button.
* (It happens when an enabled accessibility service package is upgraded.)
*
@@ -2207,11 +2235,22 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return false;
}
if (serviceInfo.getResolveInfo().serviceInfo.applicationInfo
.targetSdkVersion > Build.VERSION_CODES.Q) {
return false;
.targetSdkVersion <= Build.VERSION_CODES.Q) {
// A11y services targeting sdk version <= Q should not be in the list.
Slog.v(LOG_TAG, "Legacy service " + componentName
+ " should not in the button");
return true;
}
// A11y services targeting sdk version <= Q should not be in the list.
return true;
final boolean requestA11yButton = (serviceInfo.flags
& AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
if (requestA11yButton && !userState.mEnabledServices.contains(componentName)) {
// An a11y service targeting sdk version > Q and request A11y button and is assigned
// to a11y btn should be in the enabled list.
Slog.v(LOG_TAG, "Service requesting a11y button and be assigned to the button"
+ componentName + " should be enabled state");
return true;
}
return false;
});
boolean changed = (lastSize != buttonTargets.size());
lastSize = buttonTargets.size();
@@ -2234,15 +2273,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
.targetSdkVersion > Build.VERSION_CODES.Q && requestA11yButton)) {
return;
}
final String serviceName = serviceInfo.getComponentName().flattenToString();
final String serviceName = componentName.flattenToString();
if (TextUtils.isEmpty(serviceName)) {
return;
}
if (shortcutKeyTargets.contains(serviceName) || buttonTargets.contains(serviceName)) {
if (doesShortcutTargetsStringContain(buttonTargets, serviceName)
|| doesShortcutTargetsStringContain(shortcutKeyTargets, serviceName)) {
return;
}
// For enabled a11y services targeting sdk version > Q and requesting a11y button should
// be assigned to a shortcut.
Slog.v(LOG_TAG, "A enabled service requesting a11y button " + componentName
+ " should be assign to the button or shortcut.");
buttonTargets.add(serviceName);
});
changed |= (lastSize != buttonTargets.size());
@@ -2251,8 +2293,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
// Update setting key with new value.
persistColonDelimitedSetToSettingLocked(
Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
persistColonDelimitedSetToSettingLocked(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
userState.mUserId, buttonTargets, str -> str);
scheduleNotifyClientsOfServicesStateChangeLocked(userState);
}
@@ -2353,12 +2394,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return;
}
// In case the caller specified a target name
if (targetName != null) {
if (!shortcutTargets.contains(targetName)) {
Slog.d(LOG_TAG, "Perform shortcut failed, invalid target name:" + targetName);
return;
}
} else {
if (targetName != null && !doesShortcutTargetsStringContain(shortcutTargets, targetName)) {
Slog.v(LOG_TAG, "Perform shortcut failed, invalid target name:" + targetName);
targetName = null;
}
if (targetName == null) {
// In case there are many targets assigned to the given shortcut.
if (shortcutTargets.size() > 1) {
showAccessibilityTargetsSelection(displayId, shortcutType);
@@ -2983,6 +3023,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private final Uri mAccessibilityButtonComponentIdUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT);
private final Uri mAccessibilityButtonTargetsUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
private final Uri mUserNonInteractiveUiTimeoutUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS);
@@ -3015,6 +3058,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mAccessibilityShortcutServiceIdUri, false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(
mAccessibilityButtonComponentIdUri, false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(
mAccessibilityButtonTargetsUri, false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(
mUserNonInteractiveUiTimeoutUri, false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(
@@ -3061,7 +3106,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
onUserStateChangedLocked(userState);
}
} else if (mAccessibilityButtonComponentIdUri.equals(uri)) {
if (readAccessibilityButtonSettingsLocked(userState)) {
if (readAccessibilityButtonTargetComponentLocked(userState)) {
onUserStateChangedLocked(userState);
}
} else if (mAccessibilityButtonTargetsUri.equals(uri)) {
if (readAccessibilityButtonTargetsLocked(userState)) {
onUserStateChangedLocked(userState);
}
} else if (mUserNonInteractiveUiTimeoutUri.equals(uri)

View File

@@ -49,6 +49,7 @@ import com.android.internal.accessibility.AccessibilityShortcutController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -96,6 +97,8 @@ class AccessibilityUserState {
private ComponentName mServiceChangingSoftKeyboardMode;
private String mTargetAssignedToAccessibilityButton;
private boolean mBindInstantServiceAllowed;
private boolean mIsAutoclickEnabled;
private boolean mIsDisplayMagnificationEnabled;
@@ -152,6 +155,7 @@ class AccessibilityUserState {
mTouchExplorationGrantedServices.clear();
mAccessibilityShortcutKeyTargets.clear();
mAccessibilityButtonTargets.clear();
mTargetAssignedToAccessibilityButton = null;
mIsTouchExplorationEnabled = false;
mServiceHandlesDoubleTap = false;
mRequestMultiFingerGestures = false;
@@ -469,6 +473,8 @@ class AccessibilityUserState {
}
}
pw.println("}");
pw.append(" button target:{").append(mTargetAssignedToAccessibilityButton);
pw.println("}");
pw.append(" Bound services:{");
final int serviceCount = mBoundServices.size();
for (int j = 0; j < serviceCount; j++) {
@@ -716,4 +722,56 @@ class AccessibilityUserState {
public void setUserNonInteractiveUiTimeoutLocked(int timeout) {
mUserNonInteractiveUiTimeout = timeout;
}
/**
* Gets a shortcut target which is assigned to the accessibility button by the chooser
* activity.
*
* @return The flattened component name or the system class name of the shortcut target.
*/
public String getTargetAssignedToAccessibilityButton() {
return mTargetAssignedToAccessibilityButton;
}
/**
* Sets a shortcut target which is assigned to the accessibility button by the chooser
* activity.
*
* @param target The flattened component name or the system class name of the shortcut target.
*/
public void setTargetAssignedToAccessibilityButton(String target) {
mTargetAssignedToAccessibilityButton = target;
}
/**
* Whether or not the given target name is contained in the shortcut collection. Since the
* component name string format could be short or long, this function un-flatten the component
* name from the string in {@code shortcutTargets} and compared with the given target name.
*
* @param shortcutTargets The shortcut type.
* @param targetName The target name.
* @return {@code true} if the target is in the shortcut collection.
*/
public static boolean doesShortcutTargetsStringContain(Collection<String> shortcutTargets,
String targetName) {
if (shortcutTargets == null || targetName == null) {
return false;
}
// Some system features, such as magnification, don't have component name. Using string
// compare first.
if (shortcutTargets.contains(targetName)) {
return true;
}
final ComponentName targetComponentName = ComponentName.unflattenFromString(targetName);
if (targetComponentName == null) {
return false;
}
for (String stringName : shortcutTargets) {
if (!TextUtils.isEmpty(stringName)
&& targetComponentName.equals(ComponentName.unflattenFromString(stringName))) {
return true;
}
}
return false;
}
}

View File

@@ -25,6 +25,8 @@ import static android.view.accessibility.AccessibilityManager.STATE_FLAG_ACCESSI
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 com.android.server.accessibility.AccessibilityUserState.doesShortcutTargetsStringContain;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNull;
@@ -41,6 +43,7 @@ import android.content.Context;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
import android.testing.DexmakerShareClassLoaderRule;
import android.util.ArraySet;
import com.android.internal.util.test.FakeSettingsProvider;
@@ -56,6 +59,12 @@ public class AccessibilityUserStateTest {
private static final ComponentName COMPONENT_NAME =
new ComponentName("com.android.server.accessibility", "AccessibilityUserStateTest");
private static final ComponentName COMPONENT_NAME1 =
new ComponentName("com.android.server.accessibility",
"com.android.server.accessibility.AccessibilityUserStateTest1");
private static final ComponentName COMPONENT_NAME2 =
new ComponentName("com.android.server.accessibility",
"com.android.server.accessibility.AccessibilityUserStateTest2");
// Values of setting key SHOW_IME_WITH_HARD_KEYBOARD
private static final int STATE_HIDE_IME = 0;
@@ -111,6 +120,7 @@ public class AccessibilityUserStateTest {
mUserState.mTouchExplorationGrantedServices.add(COMPONENT_NAME);
mUserState.mAccessibilityShortcutKeyTargets.add(COMPONENT_NAME.flattenToString());
mUserState.mAccessibilityButtonTargets.add(COMPONENT_NAME.flattenToString());
mUserState.setTargetAssignedToAccessibilityButton(COMPONENT_NAME.flattenToString());
mUserState.setTouchExplorationEnabledLocked(true);
mUserState.setDisplayMagnificationEnabledLocked(true);
mUserState.setAutoclickEnabledLocked(true);
@@ -129,6 +139,7 @@ public class AccessibilityUserStateTest {
assertTrue(mUserState.mTouchExplorationGrantedServices.isEmpty());
assertTrue(mUserState.mAccessibilityShortcutKeyTargets.isEmpty());
assertTrue(mUserState.mAccessibilityButtonTargets.isEmpty());
assertNull(mUserState.getTargetAssignedToAccessibilityButton());
assertFalse(mUserState.isTouchExplorationEnabledLocked());
assertFalse(mUserState.isDisplayMagnificationEnabledLocked());
assertFalse(mUserState.isAutoclickEnabledLocked());
@@ -285,6 +296,31 @@ public class AccessibilityUserStateTest {
verify(mMockConnection).notifySoftKeyboardShowModeChangedLocked(eq(SHOW_MODE_HIDDEN));
}
@Test
public void doesShortcutTargetsStringContain_returnFalse() {
assertFalse(doesShortcutTargetsStringContain(null, null));
assertFalse(doesShortcutTargetsStringContain(null,
COMPONENT_NAME.flattenToShortString()));
assertFalse(doesShortcutTargetsStringContain(new ArraySet<>(), null));
final ArraySet<String> shortcutTargets = new ArraySet<>();
shortcutTargets.add(COMPONENT_NAME.flattenToString());
assertFalse(doesShortcutTargetsStringContain(shortcutTargets,
COMPONENT_NAME1.flattenToString()));
}
@Test
public void isAssignedToShortcutLocked_withDifferentTypeComponentString_returnTrue() {
final ArraySet<String> shortcutTargets = new ArraySet<>();
shortcutTargets.add(COMPONENT_NAME1.flattenToShortString());
shortcutTargets.add(COMPONENT_NAME2.flattenToString());
assertTrue(doesShortcutTargetsStringContain(shortcutTargets,
COMPONENT_NAME1.flattenToString()));
assertTrue(doesShortcutTargetsStringContain(shortcutTargets,
COMPONENT_NAME2.flattenToShortString()));
}
@Test
public void isShortcutTargetInstalledLocked_returnTrue() {
mUserState.mInstalledServices.add(mMockServiceInfo);