Loading packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java +66 −0 Original line number Diff line number Diff line Loading @@ -137,6 +137,13 @@ final class SettingsState { private static final String ATTR_VALUE_BASE64 = "valueBase64"; private static final String ATTR_DEFAULT_VALUE_BASE64 = "defaultValueBase64"; /** * In the config table, there are special flags of the form {@code staged/namespace*flagName}. * On boot, when the XML file is initially parsed, these transform into * {@code namespace/flagName}, and the special staged flags are deleted. */ private static final String CONFIG_STAGED_PREFIX = "staged/"; // This was used in version 120 and before. private static final String NULL_VALUE_OLD_STYLE = "null"; Loading Loading @@ -1191,6 +1198,42 @@ final class SettingsState { } } /** * Transforms a staged flag name to its real flag name. * * Staged flags take the form {@code staged/namespace*flagName}. If * {@code stagedFlagName} takes the proper form, returns * {@code namespace/flagName}. Otherwise, returns {@code stagedFlagName} * unmodified, and logs an error message. * */ @VisibleForTesting public static String createRealFlagName(String stagedFlagName) { int slashIndex = stagedFlagName.indexOf("/"); if (slashIndex == -1 || slashIndex == stagedFlagName.length() - 1 || slashIndex == 0) { Slog.w(LOG_TAG, "invalid staged flag, not applying: " + stagedFlagName); return stagedFlagName; } String namespaceAndFlag = stagedFlagName.substring(slashIndex + 1); int starIndex = namespaceAndFlag.indexOf("*"); if (starIndex == -1 || starIndex == namespaceAndFlag.length() - 1 || starIndex == 0) { Slog.w(LOG_TAG, "invalid staged flag, not applying: " + stagedFlagName); return stagedFlagName; } String namespace = namespaceAndFlag.substring(0, starIndex); String flagName = namespaceAndFlag.substring(starIndex + 1); return namespace + "/" + flagName; } @GuardedBy("mLock") private void parseSettingsLocked(TypedXmlPullParser parser) throws IOException, XmlPullParserException { Loading @@ -1199,6 +1242,7 @@ final class SettingsState { final int outerDepth = parser.getDepth(); int type; HashSet<String> flagsWithStagedValueApplied = new HashSet<String>(); while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { Loading @@ -1221,6 +1265,18 @@ final class SettingsState { fromSystem = parser.getAttributeBoolean(null, ATTR_DEFAULT_SYS_SET, false); tag = getValueAttribute(parser, ATTR_TAG, ATTR_TAG_BASE64); } if (isConfigSettingsKey(mKey)) { if (flagsWithStagedValueApplied.contains(name)) { continue; } if (name.startsWith(CONFIG_STAGED_PREFIX)) { name = createRealFlagName(name); flagsWithStagedValueApplied.add(name); } } mSettings.put(name, new Setting(name, value, defaultValue, packageName, tag, fromSystem, id, isPreservedInRestore)); Loading @@ -1229,6 +1285,16 @@ final class SettingsState { } } } if (isConfigSettingsKey(mKey) && !flagsWithStagedValueApplied.isEmpty()) { // On boot, the config table XML file includes special staged flags. On the initial // boot XML -> HashMap parse, these staged flags get transformed into real flags. // After this, the HashMap contains no special staged flags (only the transformed // real flags), but the XML still does. We then have no need for the special staged // flags in the XML, so we overwrite the XML with the latest contents of the // HashMap. writeStateAsyncLocked(); } } @GuardedBy("mLock") Loading packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java +79 −0 Original line number Diff line number Diff line Loading @@ -51,6 +51,21 @@ public class SettingsStateTest extends AndroidTestCase { private static final String SYSTEM_PACKAGE = "android"; private static final String SETTING_NAME = "test_setting"; private static final String FLAG_NAME_1 = "namespace123/flag456"; private static final String FLAG_NAME_1_STAGED = "staged/namespace123*flag456"; private static final String FLAG_NAME_2 = "not_staged/flag101"; private static final String INVALID_STAGED_FLAG_1 = "stagednamespace*flagName"; private static final String INVALID_STAGED_FLAG_2 = "staged/"; private static final String INVALID_STAGED_FLAG_3 = "staged/namespace*"; private static final String INVALID_STAGED_FLAG_4 = "staged/*flagName"; private static final String VALID_STAGED_FLAG_1 = "staged/namespace*flagName"; private static final String VALID_STAGED_FLAG_1_TRANSFORMED = "namespace/flagName"; private static final String VALUE1 = "5"; private static final String VALUE2 = "6"; private final Object mLock = new Object(); private File mSettingsFile; Loading Loading @@ -454,4 +469,68 @@ public class SettingsStateTest extends AndroidTestCase { } } } public void testApplyStagedConfigValues() { int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0); Object lock = new Object(); SettingsState settingsState = new SettingsState( getContext(), lock, mSettingsFile, configKey, SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); synchronized (lock) { settingsState.insertSettingLocked( FLAG_NAME_1_STAGED, VALUE1, null, false, TEST_PACKAGE); settingsState.insertSettingLocked(FLAG_NAME_2, VALUE2, null, false, TEST_PACKAGE); settingsState.persistSyncLocked(); assertEquals(VALUE1, settingsState.getSettingLocked(FLAG_NAME_1_STAGED).getValue()); assertEquals(VALUE2, settingsState.getSettingLocked(FLAG_NAME_2).getValue()); } settingsState = new SettingsState(getContext(), lock, mSettingsFile, configKey, SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); synchronized (lock) { assertEquals(VALUE1, settingsState.getSettingLocked(FLAG_NAME_1).getValue()); assertEquals(VALUE2, settingsState.getSettingLocked(FLAG_NAME_2).getValue()); assertEquals(null, settingsState.getSettingLocked(FLAG_NAME_1_STAGED).getValue()); } } public void testStagingTransformation() { assertEquals(INVALID_STAGED_FLAG_1, SettingsState.createRealFlagName(INVALID_STAGED_FLAG_1)); assertEquals(INVALID_STAGED_FLAG_2, SettingsState.createRealFlagName(INVALID_STAGED_FLAG_2)); assertEquals(INVALID_STAGED_FLAG_3, SettingsState.createRealFlagName(INVALID_STAGED_FLAG_3)); assertEquals(INVALID_STAGED_FLAG_4, SettingsState.createRealFlagName(INVALID_STAGED_FLAG_4)); assertEquals(VALID_STAGED_FLAG_1_TRANSFORMED, SettingsState.createRealFlagName(VALID_STAGED_FLAG_1)); } public void testInvalidStagedFlagsUnaffectedByReboot() { int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0); Object lock = new Object(); SettingsState settingsState = new SettingsState( getContext(), lock, mSettingsFile, configKey, SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); synchronized (lock) { settingsState.insertSettingLocked(INVALID_STAGED_FLAG_1, VALUE2, null, false, TEST_PACKAGE); settingsState.persistSyncLocked(); assertEquals(VALUE2, settingsState.getSettingLocked(INVALID_STAGED_FLAG_1).getValue()); } settingsState = new SettingsState(getContext(), lock, mSettingsFile, configKey, SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); synchronized (lock) { assertEquals(VALUE2, settingsState.getSettingLocked(INVALID_STAGED_FLAG_1).getValue()); } } } Loading
packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java +66 −0 Original line number Diff line number Diff line Loading @@ -137,6 +137,13 @@ final class SettingsState { private static final String ATTR_VALUE_BASE64 = "valueBase64"; private static final String ATTR_DEFAULT_VALUE_BASE64 = "defaultValueBase64"; /** * In the config table, there are special flags of the form {@code staged/namespace*flagName}. * On boot, when the XML file is initially parsed, these transform into * {@code namespace/flagName}, and the special staged flags are deleted. */ private static final String CONFIG_STAGED_PREFIX = "staged/"; // This was used in version 120 and before. private static final String NULL_VALUE_OLD_STYLE = "null"; Loading Loading @@ -1191,6 +1198,42 @@ final class SettingsState { } } /** * Transforms a staged flag name to its real flag name. * * Staged flags take the form {@code staged/namespace*flagName}. If * {@code stagedFlagName} takes the proper form, returns * {@code namespace/flagName}. Otherwise, returns {@code stagedFlagName} * unmodified, and logs an error message. * */ @VisibleForTesting public static String createRealFlagName(String stagedFlagName) { int slashIndex = stagedFlagName.indexOf("/"); if (slashIndex == -1 || slashIndex == stagedFlagName.length() - 1 || slashIndex == 0) { Slog.w(LOG_TAG, "invalid staged flag, not applying: " + stagedFlagName); return stagedFlagName; } String namespaceAndFlag = stagedFlagName.substring(slashIndex + 1); int starIndex = namespaceAndFlag.indexOf("*"); if (starIndex == -1 || starIndex == namespaceAndFlag.length() - 1 || starIndex == 0) { Slog.w(LOG_TAG, "invalid staged flag, not applying: " + stagedFlagName); return stagedFlagName; } String namespace = namespaceAndFlag.substring(0, starIndex); String flagName = namespaceAndFlag.substring(starIndex + 1); return namespace + "/" + flagName; } @GuardedBy("mLock") private void parseSettingsLocked(TypedXmlPullParser parser) throws IOException, XmlPullParserException { Loading @@ -1199,6 +1242,7 @@ final class SettingsState { final int outerDepth = parser.getDepth(); int type; HashSet<String> flagsWithStagedValueApplied = new HashSet<String>(); while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { Loading @@ -1221,6 +1265,18 @@ final class SettingsState { fromSystem = parser.getAttributeBoolean(null, ATTR_DEFAULT_SYS_SET, false); tag = getValueAttribute(parser, ATTR_TAG, ATTR_TAG_BASE64); } if (isConfigSettingsKey(mKey)) { if (flagsWithStagedValueApplied.contains(name)) { continue; } if (name.startsWith(CONFIG_STAGED_PREFIX)) { name = createRealFlagName(name); flagsWithStagedValueApplied.add(name); } } mSettings.put(name, new Setting(name, value, defaultValue, packageName, tag, fromSystem, id, isPreservedInRestore)); Loading @@ -1229,6 +1285,16 @@ final class SettingsState { } } } if (isConfigSettingsKey(mKey) && !flagsWithStagedValueApplied.isEmpty()) { // On boot, the config table XML file includes special staged flags. On the initial // boot XML -> HashMap parse, these staged flags get transformed into real flags. // After this, the HashMap contains no special staged flags (only the transformed // real flags), but the XML still does. We then have no need for the special staged // flags in the XML, so we overwrite the XML with the latest contents of the // HashMap. writeStateAsyncLocked(); } } @GuardedBy("mLock") Loading
packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java +79 −0 Original line number Diff line number Diff line Loading @@ -51,6 +51,21 @@ public class SettingsStateTest extends AndroidTestCase { private static final String SYSTEM_PACKAGE = "android"; private static final String SETTING_NAME = "test_setting"; private static final String FLAG_NAME_1 = "namespace123/flag456"; private static final String FLAG_NAME_1_STAGED = "staged/namespace123*flag456"; private static final String FLAG_NAME_2 = "not_staged/flag101"; private static final String INVALID_STAGED_FLAG_1 = "stagednamespace*flagName"; private static final String INVALID_STAGED_FLAG_2 = "staged/"; private static final String INVALID_STAGED_FLAG_3 = "staged/namespace*"; private static final String INVALID_STAGED_FLAG_4 = "staged/*flagName"; private static final String VALID_STAGED_FLAG_1 = "staged/namespace*flagName"; private static final String VALID_STAGED_FLAG_1_TRANSFORMED = "namespace/flagName"; private static final String VALUE1 = "5"; private static final String VALUE2 = "6"; private final Object mLock = new Object(); private File mSettingsFile; Loading Loading @@ -454,4 +469,68 @@ public class SettingsStateTest extends AndroidTestCase { } } } public void testApplyStagedConfigValues() { int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0); Object lock = new Object(); SettingsState settingsState = new SettingsState( getContext(), lock, mSettingsFile, configKey, SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); synchronized (lock) { settingsState.insertSettingLocked( FLAG_NAME_1_STAGED, VALUE1, null, false, TEST_PACKAGE); settingsState.insertSettingLocked(FLAG_NAME_2, VALUE2, null, false, TEST_PACKAGE); settingsState.persistSyncLocked(); assertEquals(VALUE1, settingsState.getSettingLocked(FLAG_NAME_1_STAGED).getValue()); assertEquals(VALUE2, settingsState.getSettingLocked(FLAG_NAME_2).getValue()); } settingsState = new SettingsState(getContext(), lock, mSettingsFile, configKey, SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); synchronized (lock) { assertEquals(VALUE1, settingsState.getSettingLocked(FLAG_NAME_1).getValue()); assertEquals(VALUE2, settingsState.getSettingLocked(FLAG_NAME_2).getValue()); assertEquals(null, settingsState.getSettingLocked(FLAG_NAME_1_STAGED).getValue()); } } public void testStagingTransformation() { assertEquals(INVALID_STAGED_FLAG_1, SettingsState.createRealFlagName(INVALID_STAGED_FLAG_1)); assertEquals(INVALID_STAGED_FLAG_2, SettingsState.createRealFlagName(INVALID_STAGED_FLAG_2)); assertEquals(INVALID_STAGED_FLAG_3, SettingsState.createRealFlagName(INVALID_STAGED_FLAG_3)); assertEquals(INVALID_STAGED_FLAG_4, SettingsState.createRealFlagName(INVALID_STAGED_FLAG_4)); assertEquals(VALID_STAGED_FLAG_1_TRANSFORMED, SettingsState.createRealFlagName(VALID_STAGED_FLAG_1)); } public void testInvalidStagedFlagsUnaffectedByReboot() { int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0); Object lock = new Object(); SettingsState settingsState = new SettingsState( getContext(), lock, mSettingsFile, configKey, SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); synchronized (lock) { settingsState.insertSettingLocked(INVALID_STAGED_FLAG_1, VALUE2, null, false, TEST_PACKAGE); settingsState.persistSyncLocked(); assertEquals(VALUE2, settingsState.getSettingLocked(INVALID_STAGED_FLAG_1).getValue()); } settingsState = new SettingsState(getContext(), lock, mSettingsFile, configKey, SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); synchronized (lock) { assertEquals(VALUE2, settingsState.getSettingLocked(INVALID_STAGED_FLAG_1).getValue()); } } }