Ensure settings provider joins the rescue party
The settings provider has logic to incrementally reset state
in an effort to recover from pushing a bad value putting the
system in a bad state. The settings provider was not force
persisting its state after a reset so after the reboot we
lost the reset changes. Also while resetting we were also
resetting the package name leading to a promotion of the reset
value to being set by the system. Updated the tests and while at
this added logic to synchronously persist critical settings
such as device provisioned.
Test: All tests pass. Manually tested end-to-end rescue party
bug:34677175
Change-Id: Ib240072df2fa549dae39c301008adf48cdf1573a
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 25e1f16..7a9ba20 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -182,6 +182,18 @@
private static final Bundle NULL_SETTING_BUNDLE = Bundle.forPair(
Settings.NameValueTable.VALUE, null);
+ // Changes to these global settings are synchronously persisted
+ private static final Set<String> CRITICAL_GLOBAL_SETTINGS = new ArraySet<>();
+ static {
+ CRITICAL_GLOBAL_SETTINGS.add(Settings.Global.DEVICE_PROVISIONED);
+ }
+
+ // Changes to these secure settings are synchronously persisted
+ private static final Set<String> CRITICAL_SECURE_SETTINGS = new ArraySet<>();
+ static {
+ CRITICAL_SECURE_SETTINGS.add(Settings.Secure.USER_SETUP_COMPLETE);
+ }
+
// Per user secure settings that moved to the for all users global settings.
static final Set<String> sSecureMovedToGlobalSettings = new ArraySet<>();
static {
@@ -949,18 +961,18 @@
case MUTATION_OPERATION_INSERT: {
return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_GLOBAL,
UserHandle.USER_SYSTEM, name, value, tag, makeDefault,
- getCallingPackage(), forceNotify);
+ getCallingPackage(), forceNotify, CRITICAL_GLOBAL_SETTINGS);
}
case MUTATION_OPERATION_DELETE: {
return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_GLOBAL,
- UserHandle.USER_SYSTEM, name, forceNotify);
+ UserHandle.USER_SYSTEM, name, forceNotify, CRITICAL_GLOBAL_SETTINGS);
}
case MUTATION_OPERATION_UPDATE: {
return mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_GLOBAL,
UserHandle.USER_SYSTEM, name, value, tag, makeDefault,
- getCallingPackage(), forceNotify);
+ getCallingPackage(), forceNotify, CRITICAL_GLOBAL_SETTINGS);
}
case MUTATION_OPERATION_RESET: {
@@ -1156,18 +1168,18 @@
case MUTATION_OPERATION_INSERT: {
return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SECURE,
owningUserId, name, value, tag, makeDefault,
- getCallingPackage(), forceNotify);
+ getCallingPackage(), forceNotify, CRITICAL_SECURE_SETTINGS);
}
case MUTATION_OPERATION_DELETE: {
return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_SECURE,
- owningUserId, name, forceNotify);
+ owningUserId, name, forceNotify, CRITICAL_SECURE_SETTINGS);
}
case MUTATION_OPERATION_UPDATE: {
return mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_SECURE,
owningUserId, name, value, tag, makeDefault,
- getCallingPackage(), forceNotify);
+ getCallingPackage(), forceNotify, CRITICAL_SECURE_SETTINGS);
}
case MUTATION_OPERATION_RESET: {
@@ -1304,18 +1316,20 @@
case MUTATION_OPERATION_INSERT: {
validateSystemSettingValue(name, value);
return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SYSTEM,
- owningUserId, name, value, null, false, getCallingPackage(), false);
+ owningUserId, name, value, null, false, getCallingPackage(),
+ false, null);
}
case MUTATION_OPERATION_DELETE: {
return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_SYSTEM,
- owningUserId, name, false);
+ owningUserId, name, false, null);
}
case MUTATION_OPERATION_UPDATE: {
validateSystemSettingValue(name, value);
return mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_SYSTEM,
- owningUserId, name, value, null, false, getCallingPackage(), false);
+ owningUserId, name, value, null, false, getCallingPackage(),
+ false, null);
}
}
@@ -1689,7 +1703,7 @@
return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SECURE,
owningUserId, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, newProviders,
- tag, makeDefault, getCallingPackage(), forceNotify);
+ tag, makeDefault, getCallingPackage(), forceNotify, CRITICAL_SECURE_SETTINGS);
}
private static void warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk(
@@ -2234,7 +2248,8 @@
}
public boolean insertSettingLocked(int type, int userId, String name, String value,
- String tag, boolean makeDefault, String packageName, boolean forceNotify) {
+ String tag, boolean makeDefault, String packageName, boolean forceNotify,
+ Set<String> criticalSettings) {
final int key = makeKey(type, userId);
boolean success = false;
@@ -2244,13 +2259,18 @@
tag, makeDefault, packageName);
}
+ if (success && criticalSettings != null && criticalSettings.contains(name)) {
+ settingsState.persistSyncLocked();
+ }
+
if (forceNotify || success) {
notifyForSettingsChange(key, name);
}
return success;
}
- public boolean deleteSettingLocked(int type, int userId, String name, boolean forceNotify) {
+ public boolean deleteSettingLocked(int type, int userId, String name, boolean forceNotify,
+ Set<String> criticalSettings) {
final int key = makeKey(type, userId);
boolean success = false;
@@ -2259,12 +2279,39 @@
success = settingsState.deleteSettingLocked(name);
}
+ if (success && criticalSettings != null && criticalSettings.contains(name)) {
+ settingsState.persistSyncLocked();
+ }
+
if (forceNotify || success) {
notifyForSettingsChange(key, name);
}
return success;
}
+ public boolean updateSettingLocked(int type, int userId, String name, String value,
+ String tag, boolean makeDefault, String packageName, boolean forceNotify,
+ Set<String> criticalSettings) {
+ final int key = makeKey(type, userId);
+
+ boolean success = false;
+ SettingsState settingsState = peekSettingsStateLocked(key);
+ if (settingsState != null) {
+ success = settingsState.updateSettingLocked(name, value, tag,
+ makeDefault, packageName);
+ }
+
+ if (success && criticalSettings != null && criticalSettings.contains(name)) {
+ settingsState.persistSyncLocked();
+ }
+
+ if (forceNotify || success) {
+ notifyForSettingsChange(key, name);
+ }
+
+ return success;
+ }
+
public Setting getSettingLocked(int type, int userId, String name) {
final int key = makeKey(type, userId);
@@ -2277,24 +2324,6 @@
return settingsState.getSettingLocked(name);
}
- public boolean updateSettingLocked(int type, int userId, String name, String value,
- String tag, boolean makeDefault, String packageName, boolean forceNotify) {
- final int key = makeKey(type, userId);
-
- boolean success = false;
- SettingsState settingsState = peekSettingsStateLocked(key);
- if (settingsState != null) {
- success = settingsState.updateSettingLocked(name, value, tag,
- makeDefault, packageName);
- }
-
- if (forceNotify || success) {
- notifyForSettingsChange(key, name);
- }
-
- return success;
- }
-
public void resetSettingsLocked(int type, int userId, String packageName, int mode,
String tag) {
final int key = makeKey(type, userId);
@@ -2306,56 +2335,78 @@
switch (mode) {
case Settings.RESET_MODE_PACKAGE_DEFAULTS: {
for (String name : settingsState.getSettingNamesLocked()) {
+ boolean someSettingChanged = false;
Setting setting = settingsState.getSettingLocked(name);
if (packageName.equals(setting.getPackageName())) {
if (tag != null && !tag.equals(setting.getTag())) {
continue;
}
- if (settingsState.resetSettingLocked(name, packageName)) {
+ if (settingsState.resetSettingLocked(name)) {
+ someSettingChanged = true;
notifyForSettingsChange(key, name);
}
}
+ if (someSettingChanged) {
+ settingsState.persistSyncLocked();
+ }
}
} break;
case Settings.RESET_MODE_UNTRUSTED_DEFAULTS: {
for (String name : settingsState.getSettingNamesLocked()) {
+ boolean someSettingChanged = false;
Setting setting = settingsState.getSettingLocked(name);
if (!SettingsState.isSystemPackage(getContext(),
setting.getPackageName())) {
- if (settingsState.resetSettingLocked(name, packageName)) {
+ if (settingsState.resetSettingLocked(name)) {
+ someSettingChanged = true;
notifyForSettingsChange(key, name);
}
}
+ if (someSettingChanged) {
+ settingsState.persistSyncLocked();
+ }
}
} break;
case Settings.RESET_MODE_UNTRUSTED_CHANGES: {
for (String name : settingsState.getSettingNamesLocked()) {
+ boolean someSettingChanged = false;
Setting setting = settingsState.getSettingLocked(name);
if (!SettingsState.isSystemPackage(getContext(),
setting.getPackageName())) {
if (setting.isDefaultFromSystem()) {
- if (settingsState.resetSettingLocked(name, packageName)) {
+ if (settingsState.resetSettingLocked(name)) {
+ someSettingChanged = true;
notifyForSettingsChange(key, name);
}
} else if (settingsState.deleteSettingLocked(name)) {
+ someSettingChanged = true;
notifyForSettingsChange(key, name);
}
}
+ if (someSettingChanged) {
+ settingsState.persistSyncLocked();
+ }
}
} break;
case Settings.RESET_MODE_TRUSTED_DEFAULTS: {
for (String name : settingsState.getSettingNamesLocked()) {
Setting setting = settingsState.getSettingLocked(name);
+ boolean someSettingChanged = false;
if (setting.isDefaultFromSystem()) {
- if (settingsState.resetSettingLocked(name, packageName)) {
+ if (settingsState.resetSettingLocked(name)) {
+ someSettingChanged = true;
notifyForSettingsChange(key, name);
}
} else if (settingsState.deleteSettingLocked(name)) {
+ someSettingChanged = true;
notifyForSettingsChange(key, name);
}
+ if (someSettingChanged) {
+ settingsState.persistSyncLocked();
+ }
}
} break;
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
index 2d59324..a6fadf9 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
@@ -113,7 +113,7 @@
String mKey = null;
String mValue = null;
String mPackageName = null;
- String mToken = null;
+ String mTag = null;
int mResetMode = -1;
boolean mMakeDefault;
@@ -185,7 +185,7 @@
if (peekNextArg() == null) {
valid = true;
} else {
- mToken = getNextArg();
+ mTag = getNextArg();
if (peekNextArg() == null) {
valid = true;
} else {
@@ -218,10 +218,10 @@
// what we have so far is a valid command
valid = true;
// keep going; there may be another PUT arg
- } else if (mToken == null) {
- mToken = arg;
- if ("default".equalsIgnoreCase(mToken)) {
- mToken = null;
+ } else if (mTag == null) {
+ mTag = arg;
+ if ("default".equalsIgnoreCase(mTag)) {
+ mTag = null;
mMakeDefault = true;
if (peekNextArg() == null) {
valid = true;
@@ -282,7 +282,7 @@
pout.println(getForUser(iprovider, mUser, mTable, mKey));
break;
case PUT:
- putForUser(iprovider, mUser, mTable, mKey, mValue, mToken, mMakeDefault);
+ putForUser(iprovider, mUser, mTable, mKey, mValue, mTag, mMakeDefault);
break;
case DELETE:
pout.println("Deleted "
@@ -294,7 +294,7 @@
}
break;
case RESET:
- resetForUser(iprovider, mUser, mTable, mToken);
+ resetForUser(iprovider, mUser, mTable, mTag);
break;
default:
perr.println("Unspecified command");
@@ -358,7 +358,7 @@
}
void putForUser(IContentProvider provider, int userHandle, final String table,
- final String key, final String value, String token, boolean makeDefault) {
+ final String key, final String value, String tag, boolean makeDefault) {
final String callPutCommand;
if ("system".equals(table)) {
callPutCommand = Settings.CALL_METHOD_PUT_SYSTEM;
@@ -378,7 +378,9 @@
Bundle arg = new Bundle();
arg.putString(Settings.NameValueTable.VALUE, value);
arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
- arg.putString(Settings.CALL_METHOD_TAG_KEY, token);
+ if (tag != null) {
+ arg.putString(Settings.CALL_METHOD_TAG_KEY, tag);
+ }
if (makeDefault) {
arg.putBoolean(Settings.CALL_METHOD_MAKE_DEFAULT_KEY, true);
}
@@ -409,7 +411,7 @@
}
void resetForUser(IContentProvider provider, int userHandle,
- String table, String token) {
+ String table, String tag) {
final String callResetCommand;
if ("secure".equals(table)) callResetCommand = Settings.CALL_METHOD_RESET_SECURE;
else if ("global".equals(table)) callResetCommand = Settings.CALL_METHOD_RESET_GLOBAL;
@@ -422,7 +424,9 @@
Bundle arg = new Bundle();
arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
arg.putInt(Settings.CALL_METHOD_RESET_MODE_KEY, mResetMode);
- arg.putString(Settings.CALL_METHOD_TAG_KEY, token);
+ if (tag != null) {
+ arg.putString(Settings.CALL_METHOD_TAG_KEY, tag);
+ }
String packageName = mPackageName != null ? mPackageName : resolveCallingPackage();
arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
provider.call(packageName, callResetCommand, null, arg);
@@ -465,9 +469,9 @@
pw.println(" Print this help text.");
pw.println(" get [--user <USER_ID> | current] NAMESPACE KEY");
pw.println(" Retrieve the current value of KEY.");
- pw.println(" put [--user <USER_ID> | current] NAMESPACE KEY VALUE [TOKEN] [default]");
+ pw.println(" put [--user <USER_ID> | current] NAMESPACE KEY VALUE [TAG] [default]");
pw.println(" Change the contents of KEY to VALUE.");
- pw.println(" TOKEN to associate with the setting.");
+ pw.println(" TAG to associate with the setting.");
pw.println(" {default} to set as the default, case-insensitive only for global/secure namespace");
pw.println(" delete NAMESPACE KEY");
pw.println(" Delete the entry for KEY.");
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index a74be35..56ae618 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -64,6 +64,7 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
/**
* This class contains the state for one type of settings. It is responsible
@@ -129,7 +130,7 @@
private static final String HISTORICAL_OPERATION_INITIALIZE = "initialize";
private static final String HISTORICAL_OPERATION_RESET = "reset";
- private static final String SHELL_PACKAGE_NAME = "shell";
+ private static final String SHELL_PACKAGE_NAME = "com.android.shell";
private static final String ROOT_PACKAGE_NAME = "root";
private static final String NULL_VALUE = "null";
@@ -307,7 +308,7 @@
Setting newState;
if (oldState != null) {
- if (!oldState.update(value, makeDefault, packageName, tag)) {
+ if (!oldState.update(value, makeDefault, packageName, tag, false)) {
return false;
}
newState = oldState;
@@ -351,7 +352,7 @@
}
// The settings provider must hold its lock when calling here.
- public boolean resetSettingLocked(String name, String packageName) {
+ public boolean resetSettingLocked(String name) {
if (TextUtils.isEmpty(name) || !hasSettingLocked(name)) {
return false;
}
@@ -362,7 +363,7 @@
String oldValue = setting.getValue();
String oldDefaultValue = setting.getDefaultValue();
- if (!setting.reset(packageName)) {
+ if (!setting.reset()) {
return false;
}
@@ -817,7 +818,7 @@
public Setting(String name, String value, boolean makeDefault, String packageName,
String tag) {
this.name = name;
- update(value, makeDefault, packageName, tag);
+ update(value, makeDefault, packageName, tag, false);
}
public Setting(String name, String value, String defaultValue,
@@ -877,16 +878,18 @@
}
/** @return whether the value changed */
- public boolean reset(String packageName) {
- return update(this.defaultValue, false, packageName, null);
+ public boolean reset() {
+ return update(this.defaultValue, false, packageName, null, true);
}
- public boolean update(String value, boolean setDefault, String packageName, String tag) {
+ public boolean update(String value, boolean setDefault, String packageName, String tag,
+ boolean forceNonSystemPackage) {
if (NULL_VALUE.equals(value)) {
value = null;
}
- final boolean callerSystem = !isNull() && isSystemPackage(mContext, packageName);
+ final boolean callerSystem = !forceNonSystemPackage &&
+ !isNull() && isSystemPackage(mContext, packageName);
// Settings set by the system are always defaults.
if (callerSystem) {
setDefault = true;