Accessibility settings get into state not allowing to enable accessibility.

If the setting for enabled accessibility serivces contains a service that
is not installed on the device the system gets into a state that does not
allow enabling accessibility. This state is not recoverable.

bug:5343351

Change-Id: I6127ae00f7a16b190cfe43b68b8e71d49b10911e
This commit is contained in:
Svetoslav Ganov
2011-09-20 11:10:44 -07:00
parent 1e596f3035
commit 232d031a13

View File

@@ -27,6 +27,7 @@ import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo; import android.content.pm.ServiceInfo;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.net.Uri; import android.net.Uri;
@@ -82,7 +83,7 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
// Timeout before we update the services if packages are added/removed since // Timeout before we update the services if packages are added/removed since
// the AccessibilityManagerService has to do that processing first to generate // the AccessibilityManagerService has to do that processing first to generate
// the AccessibilityServiceInfo we need for proper presentation. // the AccessibilityServiceInfo we need for proper presentation.
private static final long DELAY_UPDATE_SERVICES_PREFERENCES_MILLIS = 1000; private static final long DELAY_UPDATE_SERVICES_MILLIS = 1000;
private static final String ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR = ":"; private static final String ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR = ":";
@@ -125,9 +126,12 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
private static final int DIALOG_ID_NO_ACCESSIBILITY_SERVICES = 1; private static final int DIALOG_ID_NO_ACCESSIBILITY_SERVICES = 1;
// Auxiliary members. // Auxiliary members.
private final SimpleStringSplitter mStringColonSplitter = private final static SimpleStringSplitter sStringColonSplitter =
new SimpleStringSplitter(ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR.charAt(0)); new SimpleStringSplitter(ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR.charAt(0));
private static final Set<ComponentName> sInstalledServices = new HashSet<ComponentName>();
private static final Set<ComponentName> sEnabledServices = new HashSet<ComponentName>();
private final Map<String, String> mLongPressTimeoutValuetoTitleMap = private final Map<String, String> mLongPressTimeoutValuetoTitleMap =
new HashMap<String, String>(); new HashMap<String, String>();
@@ -139,6 +143,7 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
@Override @Override
public void dispatchMessage(Message msg) { public void dispatchMessage(Message msg) {
super.dispatchMessage(msg); super.dispatchMessage(msg);
loadInstalledServices();
updateServicesPreferences(); updateServicesPreferences();
} }
}; };
@@ -167,6 +172,7 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
loadInstalledServices();
updateAllPreferences(); updateAllPreferences();
if (mServicesCategory.getPreference(0) == mNoServicesMessagePreference) { if (mServicesCategory.getPreference(0) == mNoServicesMessagePreference) {
offerInstallAccessibilitySerivceOnce(); offerInstallAccessibilitySerivceOnce();
@@ -296,7 +302,7 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
String settingValue = Settings.Secure.getString(getContentResolver(), String settingValue = Settings.Secure.getString(getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
if (settingValue != null) { if (settingValue != null) {
SimpleStringSplitter splitter = mStringColonSplitter; SimpleStringSplitter splitter = sStringColonSplitter;
splitter.setString(settingValue); splitter.setString(settingValue);
while (splitter.hasNext()) { while (splitter.hasNext()) {
enabledComponentNames.add(ComponentName.unflattenFromString(splitter.next())); enabledComponentNames.add(ComponentName.unflattenFromString(splitter.next()));
@@ -493,30 +499,46 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
} }
} }
private void loadInstalledServices() {
List<AccessibilityServiceInfo> installedServiceInfos =
AccessibilityManager.getInstance(getActivity())
.getInstalledAccessibilityServiceList();
Set<ComponentName> installedServices = sInstalledServices;
installedServices.clear();
final int installedServiceInfoCount = installedServiceInfos.size();
for (int i = 0; i < installedServiceInfoCount; i++) {
ResolveInfo resolveInfo = installedServiceInfos.get(i).getResolveInfo();
ComponentName installedService = new ComponentName(
resolveInfo.serviceInfo.packageName,
resolveInfo.serviceInfo.name);
installedServices.add(installedService);
}
}
private class SettingsPackageMonitor extends PackageMonitor { private class SettingsPackageMonitor extends PackageMonitor {
@Override @Override
public void onPackageAdded(String packageName, int uid) { public void onPackageAdded(String packageName, int uid) {
Message message = mHandler.obtainMessage(); Message message = mHandler.obtainMessage();
mHandler.sendMessageDelayed(message, DELAY_UPDATE_SERVICES_PREFERENCES_MILLIS); mHandler.sendMessageDelayed(message, DELAY_UPDATE_SERVICES_MILLIS);
} }
@Override @Override
public void onPackageAppeared(String packageName, int reason) { public void onPackageAppeared(String packageName, int reason) {
Message message = mHandler.obtainMessage(); Message message = mHandler.obtainMessage();
mHandler.sendMessageDelayed(message, DELAY_UPDATE_SERVICES_PREFERENCES_MILLIS); mHandler.sendMessageDelayed(message, DELAY_UPDATE_SERVICES_MILLIS);
} }
@Override @Override
public void onPackageDisappeared(String packageName, int reason) { public void onPackageDisappeared(String packageName, int reason) {
Message message = mHandler.obtainMessage(); Message message = mHandler.obtainMessage();
mHandler.sendMessageDelayed(message, DELAY_UPDATE_SERVICES_PREFERENCES_MILLIS); mHandler.sendMessageDelayed(message, DELAY_UPDATE_SERVICES_MILLIS);
} }
@Override @Override
public void onPackageRemoved(String packageName, int uid) { public void onPackageRemoved(String packageName, int uid) {
Message message = mHandler.obtainMessage(); Message message = mHandler.obtainMessage();
mHandler.sendMessageDelayed(message, DELAY_UPDATE_SERVICES_PREFERENCES_MILLIS); mHandler.sendMessageDelayed(message, DELAY_UPDATE_SERVICES_MILLIS);
} }
} }
@@ -567,49 +589,69 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
public static class ToggleAccessibilityServiceFragment extends TogglePreferenceFragment { public static class ToggleAccessibilityServiceFragment extends TogglePreferenceFragment {
@Override @Override
public void onPreferenceToggled(String preferenceKey, boolean enabled) { public void onPreferenceToggled(String preferenceKey, boolean enabled) {
String enabledServices = Settings.Secure.getString(getContentResolver(), String enabledServicesSetting = Settings.Secure.getString(getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
if (enabledServices == null) { if (enabledServicesSetting == null) {
enabledServices = ""; enabledServicesSetting = "";
} }
// Due to a legacy bug we can get an enabled services value ending with a
// separator. Make sure to catch and fix that before handling. // Parse the enabled services.
if (enabledServices.endsWith(ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR)) { Set<ComponentName> enabledServices = sEnabledServices;
enabledServices = enabledServices.substring(0, enabledServices.length() - 1); enabledServices.clear();
SimpleStringSplitter colonSplitter = sStringColonSplitter;
colonSplitter.setString(enabledServicesSetting);
while (colonSplitter.hasNext()) {
String componentNameString = colonSplitter.next();
ComponentName enabledService = ComponentName.unflattenFromString(
componentNameString);
if (enabledService != null) {
enabledServices.add(enabledService);
} }
final int length = enabledServices.length(); }
// Determine enabled services and accessibility state.
ComponentName toggledService = ComponentName.unflattenFromString(preferenceKey);
final boolean accessibilityEnabled;
if (enabled) { if (enabled) {
if (enabledServices.contains(preferenceKey)) { // Enabling at least one service enables accessibility.
return; accessibilityEnabled = true;
} enabledServices.add(toggledService);
if (length == 0) {
enabledServices += preferenceKey;
Settings.Secure.putString(getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, enabledServices);
// Enabling the first service enables accessibility.
Settings.Secure.putInt(getContentResolver(),
Settings.Secure.ACCESSIBILITY_ENABLED, 1);
} else if (length > 0) {
enabledServices += ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR + preferenceKey;
Settings.Secure.putString(getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, enabledServices);
}
} else { } else {
final int index = enabledServices.indexOf(preferenceKey); // Check how many enabled and installed services are present.
if (index == 0) { int enabledAndInstalledServiceCount = 0;
enabledServices = enabledServices.replace(preferenceKey, ""); Set<ComponentName> installedServices = sInstalledServices;
for (ComponentName enabledService : enabledServices) {
if (installedServices.contains(enabledService)) {
enabledAndInstalledServiceCount++;
}
}
// Disabling the last service disables accessibility.
accessibilityEnabled = enabledAndInstalledServiceCount > 1
|| (enabledAndInstalledServiceCount == 1
&& !installedServices.contains(toggledService));
enabledServices.remove(toggledService);
}
// Update the enabled services setting.
StringBuilder enabledServicesBuilder = new StringBuilder();
// Keep the enabled services even if they are not installed since we have
// no way to know whether the application restore process has completed.
// In general the system should be responsible for the clean up not settings.
for (ComponentName enabledService : enabledServices) {
enabledServicesBuilder.append(enabledService.flattenToString());
enabledServicesBuilder.append(ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR);
}
final int enabledServicesBuilderLength = enabledServicesBuilder.length();
if (enabledServicesBuilderLength > 0) {
enabledServicesBuilder.deleteCharAt(enabledServicesBuilderLength - 1);
}
Settings.Secure.putString(getContentResolver(), Settings.Secure.putString(getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, enabledServices); Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
// Disabling the last service disables accessibility). enabledServicesBuilder.toString());
// Update accessibility enabled.
Settings.Secure.putInt(getContentResolver(), Settings.Secure.putInt(getContentResolver(),
Settings.Secure.ACCESSIBILITY_ENABLED, 0); Settings.Secure.ACCESSIBILITY_ENABLED, accessibilityEnabled ? 1 : 0);
} else if (index > 0) {
enabledServices = enabledServices.replace(
ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR + preferenceKey, "");
Settings.Secure.putString(getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, enabledServices);
}
}
} }
} }