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

Commit 5fb405ba authored by Svetoslav Ganov's avatar Svetoslav Ganov
Browse files

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
parent aff4c97f
Loading
Loading
Loading
Loading
+80 −29
Original line number Diff line number Diff line
@@ -182,6 +182,18 @@ public class SettingsProvider extends ContentProvider {
    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 @@ public class SettingsProvider extends ContentProvider {
                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 @@ public class SettingsProvider extends ContentProvider {
                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 @@ public class SettingsProvider extends ContentProvider {
                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 @@ public class SettingsProvider extends ContentProvider {

        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 class SettingsProvider extends ContentProvider {
        }

        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 @@ public class SettingsProvider extends ContentProvider {
                        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,26 +2279,19 @@ public class SettingsProvider extends ContentProvider {
                success = settingsState.deleteSettingLocked(name);
            }

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

            SettingsState settingsState = peekSettingsStateLocked(key);
            if (settingsState == null) {
                return null;
            }

            // getSettingLocked will return non-null result
            return settingsState.getSettingLocked(name);
        }

        public boolean updateSettingLocked(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;
@@ -2288,6 +2301,10 @@ public class SettingsProvider extends ContentProvider {
                        makeDefault, packageName);
            }

            if (success && criticalSettings != null && criticalSettings.contains(name)) {
                settingsState.persistSyncLocked();
            }

            if (forceNotify || success) {
                notifyForSettingsChange(key, name);
            }
@@ -2295,6 +2312,18 @@ public class SettingsProvider extends ContentProvider {
            return success;
        }

        public Setting getSettingLocked(int type, int userId, String name) {
            final int key = makeKey(type, userId);

            SettingsState settingsState = peekSettingsStateLocked(key);
            if (settingsState == null) {
                return null;
            }

            // getSettingLocked will return non-null result
            return settingsState.getSettingLocked(name);
        }

        public void resetSettingsLocked(int type, int userId, String packageName, int mode,
                String tag) {
            final int key = makeKey(type, userId);
@@ -2306,56 +2335,78 @@ public class SettingsProvider extends ContentProvider {
            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;
            }
+18 −14
Original line number Diff line number Diff line
@@ -113,7 +113,7 @@ final public class SettingsService extends Binder {
        String mKey = null;
        String mValue = null;
        String mPackageName = null;
        String mToken = null;
        String mTag = null;
        int mResetMode = -1;
        boolean mMakeDefault;

@@ -185,7 +185,7 @@ final public class SettingsService extends Binder {
                        if (peekNextArg() == null) {
                            valid = true;
                        } else {
                            mToken = getNextArg();
                            mTag = getNextArg();
                            if (peekNextArg() == null) {
                                valid = true;
                            } else {
@@ -218,10 +218,10 @@ final public class SettingsService extends Binder {
                    // 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 @@ final public class SettingsService extends Binder {
                    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 @@ final public class SettingsService extends Binder {
                    }
                    break;
                case RESET:
                    resetForUser(iprovider, mUser, mTable, mToken);
                    resetForUser(iprovider, mUser, mTable, mTag);
                    break;
                default:
                    perr.println("Unspecified command");
@@ -358,7 +358,7 @@ final public class SettingsService extends Binder {
        }

        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 @@ final public class SettingsService extends Binder {
                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 @@ final public class SettingsService extends Binder {
        }

        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 @@ final public class SettingsService extends Binder {
                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 @@ final public class SettingsService extends Binder {
                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.");
+12 −9
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ import java.io.PrintWriter;
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 @@ final class SettingsState {
    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 @@ final class SettingsState {
        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 @@ final class SettingsState {
    }

    // 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 @@ final class SettingsState {
        String oldValue = setting.getValue();
        String oldDefaultValue = setting.getDefaultValue();

        if (!setting.reset(packageName)) {
        if (!setting.reset()) {
            return false;
        }

@@ -817,7 +818,7 @@ final class SettingsState {
        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 @@ final class SettingsState {
        }

        /** @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;
+7 −4
Original line number Diff line number Diff line
@@ -206,19 +206,22 @@ abstract class BaseSettingsProviderTest {
        resetToDefaultsViaShell(type, packageName, null);
    }

    protected static void resetToDefaultsViaShell(int type, String packageName, String token)
    protected static void resetToDefaultsViaShell(int type, String packageName, String tag)
            throws IOException {
        switch (type) {
            case SETTING_TYPE_GLOBAL: {
                executeShellCommand("settings reset global " + packageName + " " + token);
                executeShellCommand("settings reset global " + packageName + " "
                        + (tag != null ? tag : ""));
            } break;

            case SETTING_TYPE_SECURE: {
                executeShellCommand("settings reset secure " + packageName + " " + token);
                executeShellCommand("settings reset secure " + packageName + " "
                        + (tag != null ? tag : ""));
            } break;

            case SETTING_TYPE_SYSTEM: {
                executeShellCommand("settings reset system " + packageName + " " + token);
                executeShellCommand("settings reset system " + packageName + " "
                        + (tag != null ? tag : ""));
            } break;

            default: {
+0 −4
Original line number Diff line number Diff line
@@ -59,8 +59,6 @@ public class RescueParty {
    private static final int LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS = 3;
    private static final int LEVEL_FACTORY_RESET = 4;

    private static final boolean DISABLE_RESET_SETTINGS = true;

    /** Threshold for boot loops */
    private static final Threshold sBoot = new BootThreshold();
    /** Threshold for app crash loops */
@@ -181,8 +179,6 @@ public class RescueParty {
    }

    private static void resetAllSettings(Context context, int mode) throws Exception {
        if (DISABLE_RESET_SETTINGS) return;

        // Try our best to reset all settings possible, and once finished
        // rethrow any exception that we encountered
        Exception res = null;