Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fd5b97ac authored by Ted Bauer's avatar Ted Bauer Committed by Android (Google) Code Review
Browse files

Merge "Apply staged flags in SettingsProvider." into main

parents 6af9e062 2804c41a
Loading
Loading
Loading
Loading
+66 −0
Original line number Diff line number Diff line
@@ -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";

@@ -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 {
@@ -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) {
@@ -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));

@@ -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")
+79 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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());
        }
    }
}