diff options
| author | 2021-04-30 19:21:55 +0100 | |
|---|---|---|
| committer | 2021-05-14 11:26:13 +0100 | |
| commit | ad3d45a642e30bc5b653a0ee46a95348aa870a4f (patch) | |
| tree | 94f51b568ba4a01327d5207263ac0228ebe7d335 | |
| parent | 0541a04273c8a2695d574c1159fc0127777ff9f5 (diff) | |
Add device_config cmds for disabling syncs
This adds a "sync disabled" mode which disables bulk updates to
device_config (AKA server flags). This is intended for use during
automated and manual tests that use device_config settings to set the
device into specific states for tests. Without this, devices can sync at
an arbitrary point during a test which can undo device_config changes
the tests have made and cause them to fail / flake.
This mechanism is independent of the mechanism used to sync, thereby
making it suitable for use in CTS or other AOSP tests, i.e. to disable
sync regardless of whether GMS core or an alternative is handling the
sync.
Test: atest core/tests/coretests/src/android/provider/DeviceConfigTest.java
Bug: 185786624
Change-Id: Icd0ce798642eb136dc8b9b1a58a4ecbc6212fdba
8 files changed, 425 insertions, 22 deletions
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index d7d1902b9b9f..25844053f14c 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -31,6 +31,7 @@ import android.content.Context; import android.content.pm.PackageManager; import android.database.ContentObserver; import android.net.Uri; +import android.provider.Settings.Config.SyncDisabledMode; import android.provider.Settings.ResetMode; import android.util.ArrayMap; import android.util.Log; @@ -832,6 +833,37 @@ public final class DeviceConfig { } /** + * Disables or re-enables bulk modifications ({@link #setProperties(Properties)}) to device + * config values. This is intended for use during tests to prevent a sync operation clearing + * config values, which could influence the outcome of the tests, i.e. by changing behavior. + * + * @param syncDisabledMode the mode to use, see {@link Settings.Config#SYNC_DISABLED_MODE_NONE}, + * {@link Settings.Config#SYNC_DISABLED_MODE_PERSISTENT} and {@link + * Settings.Config#SYNC_DISABLED_MODE_UNTIL_REBOOT} + * + * @see #isSyncDisabled() + * @hide + */ + @RequiresPermission(WRITE_DEVICE_CONFIG) + public static void setSyncDisabled(@SyncDisabledMode int syncDisabledMode) { + ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver(); + Settings.Config.setSyncDisabled(contentResolver, syncDisabledMode); + } + + /** + * Returns the current state of sync disabling, {@code true} when disabled, {@code false} + * otherwise. + * + * @see #setSyncDisabled(int) + * @hide + */ + @RequiresPermission(WRITE_DEVICE_CONFIG) + public static boolean isSyncDisabled() { + ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver(); + return Settings.Config.isSyncDisabled(contentResolver); + } + + /** * Add a listener for property changes. * <p> * This listener will be called whenever properties in the specified namespace change. Callbacks diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index c51c50685402..eac8cfa118ea 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -267,8 +267,40 @@ public final class Settings { /** @hide */ public static final String EXTRA_NETWORK_TEMPLATE = "network_template"; + /** + * The return values for {@link Settings.Config#set} + * @hide + */ + @IntDef(prefix = "SET_ALL_RESULT_", + value = { SET_ALL_RESULT_FAILURE, SET_ALL_RESULT_SUCCESS, SET_ALL_RESULT_DISABLED }) + @Retention(RetentionPolicy.SOURCE) + @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) + public @interface SetAllResult {} + + /** + * A return value for {@link #KEY_CONFIG_SET_ALL_RETURN}, indicates failure. + * @hide + */ + public static final int SET_ALL_RESULT_FAILURE = 0; + + /** + * A return value for {@link #KEY_CONFIG_SET_ALL_RETURN}, indicates success. + * @hide + */ + public static final int SET_ALL_RESULT_SUCCESS = 1; + + /** + * A return value for {@link #KEY_CONFIG_SET_ALL_RETURN}, indicates a set all is disabled. + * @hide + */ + public static final int SET_ALL_RESULT_DISABLED = 2; + + /** @hide */ + public static final String KEY_CONFIG_SET_ALL_RETURN = "config_set_all_return"; + /** @hide */ - public static final String KEY_CONFIG_SET_RETURN = "config_set_return"; + public static final String KEY_CONFIG_IS_SYNC_DISABLED_RETURN = + "config_is_sync_disabled_return"; /** * An int extra specifying a subscription ID. @@ -2324,6 +2356,11 @@ public final class Settings { public static final String CALL_METHOD_PREFIX_KEY = "_prefix"; /** + * @hide - String argument extra to the fast-path call()-based requests + */ + public static final String CALL_METHOD_SYNC_DISABLED_MODE_KEY = "_disabled_mode"; + + /** * @hide - RemoteCallback monitor callback argument extra to the fast-path call()-based requests */ public static final String CALL_METHOD_MONITOR_CALLBACK_KEY = "_monitor_callback_key"; @@ -2386,6 +2423,15 @@ public final class Settings { /** @hide - Private call() method to reset to defaults the 'configuration' table */ public static final String CALL_METHOD_LIST_CONFIG = "LIST_config"; + /** @hide - Private call() method to disable / re-enable syncs to the 'configuration' table */ + public static final String CALL_METHOD_SET_SYNC_DISABLED_CONFIG = "SET_SYNC_DISABLED_config"; + + /** + * @hide - Private call() method to return whether syncs are disabled for the 'configuration' + * table + */ + public static final String CALL_METHOD_IS_SYNC_DISABLED_CONFIG = "IS_SYNC_DISABLED_config"; + /** @hide - Private call() method to register monitor callback for 'configuration' table */ public static final String CALL_METHOD_REGISTER_MONITOR_CALLBACK_CONFIG = "REGISTER_MONITOR_CALLBACK_config"; @@ -2762,11 +2808,11 @@ public final class Settings { return true; } - public boolean setStringsForPrefix(ContentResolver cr, String prefix, + public @SetAllResult int setStringsForPrefix(ContentResolver cr, String prefix, HashMap<String, String> keyValues) { if (mCallSetAllCommand == null) { // This NameValueCache does not support atomically setting multiple flags - return false; + return SET_ALL_RESULT_FAILURE; } try { Bundle args = new Bundle(); @@ -2776,10 +2822,10 @@ public final class Settings { Bundle bundle = cp.call(cr.getAttributionSource(), mProviderHolder.mUri.getAuthority(), mCallSetAllCommand, null, args); - return bundle.getBoolean(KEY_CONFIG_SET_RETURN); + return bundle.getInt(KEY_CONFIG_SET_ALL_RETURN); } catch (RemoteException e) { // Not supported by the remote side - return false; + return SET_ALL_RESULT_FAILURE; } } @@ -14187,6 +14233,15 @@ public final class Settings { public static final String ARE_USER_DISABLED_HDR_FORMATS_ALLOWED = "are_user_disabled_hdr_formats_allowed"; + /** + * Whether or not syncs (bulk set operations) for {@link DeviceConfig} are disabled + * currently. The value is boolean (1 or 0). The value '1' means that {@link + * DeviceConfig#setProperties(DeviceConfig.Properties)} will return {@code false}. + * + * @hide + */ + public static final String DEVICE_CONFIG_SYNC_DISABLED = "device_config_sync_disabled"; + /** @hide */ public static String zenModeToString(int mode) { if (mode == ZEN_MODE_IMPORTANT_INTERRUPTIONS) return "ZEN_MODE_IMPORTANT_INTERRUPTIONS"; if (mode == ZEN_MODE_ALARMS) return "ZEN_MODE_ALARMS"; @@ -16021,6 +16076,39 @@ public final class Settings { * @hide */ public static final class Config extends NameValueTable { + + /** + * The modes that can be used when disabling syncs to the 'config' settings. + * @hide + */ + @IntDef(prefix = "DISABLE_SYNC_MODE_", + value = { SYNC_DISABLED_MODE_NONE, SYNC_DISABLED_MODE_PERSISTENT, + SYNC_DISABLED_MODE_UNTIL_REBOOT }) + @Retention(RetentionPolicy.SOURCE) + @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) + public @interface SyncDisabledMode {} + + /** + * Sync is not not disabled. + * + * @hide + */ + public static final int SYNC_DISABLED_MODE_NONE = 0; + + /** + * Disabling of Config bulk update / syncing is persistent, i.e. it survives a device + * reboot. + * @hide + */ + public static final int SYNC_DISABLED_MODE_PERSISTENT = 1; + + /** + * Disabling of Config bulk update / syncing is not persistent, i.e. it will not survive a + * device reboot. + * @hide + */ + public static final int SYNC_DISABLED_MODE_UNTIL_REBOOT = 2; + private static final ContentProviderHolder sProviderHolder = new ContentProviderHolder(DeviceConfig.CONTENT_URI); @@ -16113,7 +16201,7 @@ public final class Settings { * @param resolver to access the database with. * @param namespace to which the names should be set. * @param keyValues map of key names (without the prefix) to values. - * @return + * @return true if the name/value pairs were set, false if setting was blocked * * @hide */ @@ -16126,12 +16214,15 @@ public final class Settings { compositeKeyValueMap.put( createCompositeName(namespace, entry.getKey()), entry.getValue()); } - // If can't set given configuration that means it's bad - if (!sNameValueCache.setStringsForPrefix(resolver, createPrefix(namespace), - compositeKeyValueMap)) { - throw new DeviceConfig.BadConfigException(); + int result = sNameValueCache.setStringsForPrefix( + resolver, createPrefix(namespace), compositeKeyValueMap); + if (result == SET_ALL_RESULT_SUCCESS) { + return true; + } else if (result == SET_ALL_RESULT_DISABLED) { + return false; } - return true; + // If can't set given configuration that means it's bad + throw new DeviceConfig.BadConfigException(); } /** @@ -16167,6 +16258,50 @@ public final class Settings { } /** + * Bridge method between {@link DeviceConfig#setSyncDisabled(int)} and the + * {@link com.android.providers.settings.SettingsProvider} implementation. + * + * @hide + */ + @SuppressLint("AndroidFrameworkRequiresPermission") + @RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG) + static void setSyncDisabled( + @NonNull ContentResolver resolver, @SyncDisabledMode int disableSyncMode) { + try { + Bundle args = new Bundle(); + args.putInt(CALL_METHOD_SYNC_DISABLED_MODE_KEY, disableSyncMode); + IContentProvider cp = sProviderHolder.getProvider(resolver); + cp.call(resolver.getAttributionSource(), + sProviderHolder.mUri.getAuthority(), CALL_METHOD_SET_SYNC_DISABLED_CONFIG, + null, args); + } catch (RemoteException e) { + Log.w(TAG, "Can't set sync disabled " + DeviceConfig.CONTENT_URI, e); + } + } + + /** + * Bridge method between {@link DeviceConfig#isSyncDisabled()} and the + * {@link com.android.providers.settings.SettingsProvider} implementation. + * + * @hide + */ + @SuppressLint("AndroidFrameworkRequiresPermission") + @RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG) + static boolean isSyncDisabled(@NonNull ContentResolver resolver) { + try { + Bundle args = Bundle.EMPTY; + IContentProvider cp = sProviderHolder.getProvider(resolver); + Bundle bundle = cp.call(resolver.getAttributionSource(), + sProviderHolder.mUri.getAuthority(), CALL_METHOD_IS_SYNC_DISABLED_CONFIG, + null, args); + return bundle.getBoolean(KEY_CONFIG_IS_SYNC_DISABLED_RETURN); + } catch (RemoteException e) { + Log.w(TAG, "Can't query sync disabled " + DeviceConfig.CONTENT_URI, e); + } + return false; + } + + /** * Register callback for monitoring Config table. * * @param resolver Handle to the content resolver. diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java index 61f58b0d27ce..fd39cdee4c32 100644 --- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java +++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java @@ -762,6 +762,62 @@ public class DeviceConfigTest { // } // } + @Test + public void syncDisabling() throws Exception { + Properties properties1 = new Properties.Builder(NAMESPACE) + .setString(KEY, VALUE) + .build(); + Properties properties2 = new Properties.Builder(NAMESPACE) + .setString(KEY, VALUE2) + .build(); + + try { + // Ensure the device starts in a known state. + DeviceConfig.setSyncDisabled(Settings.Config.SYNC_DISABLED_MODE_NONE); + + // Assert starting state. + assertThat(DeviceConfig.isSyncDisabled()).isFalse(); + assertThat(DeviceConfig.setProperties(properties1)).isTrue(); + assertThat(DeviceConfig.getProperties(NAMESPACE, KEY).getString(KEY, DEFAULT_VALUE)) + .isEqualTo(VALUE); + + // Test disabled (persistent). Persistence is not actually tested, that would require + // a host test. + DeviceConfig.setSyncDisabled(Settings.Config.SYNC_DISABLED_MODE_PERSISTENT); + assertThat(DeviceConfig.isSyncDisabled()).isTrue(); + assertThat(DeviceConfig.setProperties(properties2)).isFalse(); + assertThat(DeviceConfig.getProperties(NAMESPACE, KEY).getString(KEY, DEFAULT_VALUE)) + .isEqualTo(VALUE); + + // Return to not disabled. + DeviceConfig.setSyncDisabled(Settings.Config.SYNC_DISABLED_MODE_NONE); + assertThat(DeviceConfig.isSyncDisabled()).isFalse(); + assertThat(DeviceConfig.setProperties(properties2)).isTrue(); + assertThat(DeviceConfig.getProperties(NAMESPACE, KEY).getString(KEY, DEFAULT_VALUE)) + .isEqualTo(VALUE2); + + // Test disabled (persistent). Absence of persistence is not actually tested, that would + // require a host test. + DeviceConfig.setSyncDisabled(Settings.Config.SYNC_DISABLED_MODE_UNTIL_REBOOT); + assertThat(DeviceConfig.isSyncDisabled()).isTrue(); + assertThat(DeviceConfig.setProperties(properties1)).isFalse(); + assertThat(DeviceConfig.getProperties(NAMESPACE, KEY).getString(KEY, DEFAULT_VALUE)) + .isEqualTo(VALUE2); + + // Return to not disabled. + DeviceConfig.setSyncDisabled(Settings.Config.SYNC_DISABLED_MODE_NONE); + assertThat(DeviceConfig.isSyncDisabled()).isFalse(); + assertThat(DeviceConfig.setProperties(properties1)).isTrue(); + assertThat(DeviceConfig.getProperties(NAMESPACE, KEY).getString(KEY, DEFAULT_VALUE)) + .isEqualTo(VALUE); + } finally { + // Try to return to the default sync disabled state in case of failure. + DeviceConfig.setSyncDisabled(Settings.Config.SYNC_DISABLED_MODE_NONE); + + // NAMESPACE will be cleared by cleanUp() + } + } + private static boolean deleteViaContentProvider(String namespace, String key) { ContentResolver resolver = InstrumentationRegistry.getContext().getContentResolver(); String compositeName = namespace + "/" + key; diff --git a/core/tests/coretests/src/android/provider/NameValueCacheTest.java b/core/tests/coretests/src/android/provider/NameValueCacheTest.java index 97e66c481dda..ee0b127c696b 100644 --- a/core/tests/coretests/src/android/provider/NameValueCacheTest.java +++ b/core/tests/coretests/src/android/provider/NameValueCacheTest.java @@ -96,7 +96,8 @@ public class NameValueCacheTest { mCacheGenerationStore.set(0, ++mCurrentGeneration); Bundle result = new Bundle(); - result.putBoolean(Settings.KEY_CONFIG_SET_RETURN, true); + result.putInt(Settings.KEY_CONFIG_SET_ALL_RETURN, + Settings.SET_ALL_RESULT_SUCCESS); return result; }); diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java index b7560d207f0d..c9c3db8a2e27 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java @@ -73,6 +73,7 @@ public class GlobalSettings { Settings.Global.CUSTOM_BUGREPORT_HANDLER_USER, Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, Settings.Global.USER_DISABLED_HDR_FORMATS, - Settings.Global.ARE_USER_DISABLED_HDR_FORMATS_ALLOWED + Settings.Global.ARE_USER_DISABLED_HDR_FORMATS_ALLOWED, + Settings.Global.DEVICE_CONFIG_SYNC_DISABLED, }; } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java index 8f7f1faa4d91..5220a04d73e6 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java @@ -139,6 +139,7 @@ public class GlobalSettingsValidators { /* first= */Global.ONE_HANDED_KEYGUARD_SIDE_LEFT, /* last= */Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT)); VALIDATORS.put(Global.DISABLE_WINDOW_BLURS, BOOLEAN_VALIDATOR); + VALIDATORS.put(Global.DEVICE_CONFIG_SYNC_DISABLED, BOOLEAN_VALIDATOR); } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java index df6ff73cc046..00fd19c2b984 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java @@ -16,7 +16,11 @@ package com.android.providers.settings; -import android.annotation.SystemApi; +import static android.provider.Settings.Config.SYNC_DISABLED_MODE_NONE; +import static android.provider.Settings.Config.SYNC_DISABLED_MODE_PERSISTENT; +import static android.provider.Settings.Config.SYNC_DISABLED_MODE_UNTIL_REBOOT; + +import android.annotation.SuppressLint; import android.app.ActivityManager; import android.content.AttributionSource; import android.content.IContentProvider; @@ -41,10 +45,7 @@ import java.util.Map; /** * Receives shell commands from the command line related to device config flags, and dispatches them * to the SettingsProvider. - * - * @hide */ -@SystemApi public final class DeviceConfigService extends Binder { final SettingsProvider mProvider; @@ -62,18 +63,20 @@ public final class DeviceConfigService extends Binder { final SettingsProvider mProvider; enum CommandVerb { - UNSPECIFIED, GET, PUT, DELETE, LIST, RESET, + SET_SYNC_DISABLED_FOR_TESTS, + IS_SYNC_DISABLED_FOR_TESTS, } MyShellCommand(SettingsProvider provider) { mProvider = provider; } + @SuppressLint("AndroidFrameworkRequiresPermission") @Override public int onCommand(String cmd) { if (cmd == null || "help".equals(cmd) || "-h".equals(cmd)) { @@ -83,6 +86,7 @@ public final class DeviceConfigService extends Binder { final PrintWriter perr = getErrPrintWriter(); boolean isValid = false; + CommandVerb verb; if ("get".equalsIgnoreCase(cmd)) { verb = CommandVerb.GET; @@ -97,21 +101,33 @@ public final class DeviceConfigService extends Binder { } } else if ("reset".equalsIgnoreCase(cmd)) { verb = CommandVerb.RESET; + } else if ("set_sync_disabled_for_tests".equalsIgnoreCase(cmd)) { + verb = CommandVerb.SET_SYNC_DISABLED_FOR_TESTS; + } else if ("is_sync_disabled_for_tests".equalsIgnoreCase(cmd)) { + verb = CommandVerb.IS_SYNC_DISABLED_FOR_TESTS; + if (peekNextArg() != null) { + perr.println("Bad arguments"); + return -1; + } + isValid = true; } else { // invalid perr.println("Invalid command: " + cmd); return -1; } + // Parse args for those commands that have them. + int disableSyncMode = -1; int resetMode = -1; boolean makeDefault = false; String namespace = null; String key = null; String value = null; - String arg = null; + String arg; while ((arg = getNextArg()) != null) { if (verb == CommandVerb.RESET) { if (resetMode == -1) { + // RESET 1st arg (required) if ("untrusted_defaults".equalsIgnoreCase(arg)) { resetMode = Settings.RESET_MODE_UNTRUSTED_DEFAULTS; } else if ("untrusted_clear".equalsIgnoreCase(arg)) { @@ -127,6 +143,7 @@ public final class DeviceConfigService extends Binder { isValid = true; } } else { + // RESET 2nd arg (optional) namespace = arg; if (peekNextArg() == null) { isValid = true; @@ -136,7 +153,26 @@ public final class DeviceConfigService extends Binder { return -1; } } + } else if (verb == CommandVerb.SET_SYNC_DISABLED_FOR_TESTS) { + if (disableSyncMode == -1) { + // DISABLE_SYNC_FOR_TESTS 1st arg (required) + if ("none".equalsIgnoreCase(arg)) { + disableSyncMode = SYNC_DISABLED_MODE_NONE; + } else if ("persistent".equalsIgnoreCase(arg)) { + disableSyncMode = SYNC_DISABLED_MODE_PERSISTENT; + } else if ("until_reboot".equalsIgnoreCase(arg)) { + disableSyncMode = SYNC_DISABLED_MODE_UNTIL_REBOOT; + } else { + // invalid + perr.println("Invalid sync disabled mode: " + arg); + return -1; + } + if (peekNextArg() == null) { + isValid = true; + } + } } else if (namespace == null) { + // GET, PUT, DELETE, LIST 1st arg namespace = arg; if (verb == CommandVerb.LIST) { if (peekNextArg() == null) { @@ -148,8 +184,10 @@ public final class DeviceConfigService extends Binder { } } } else if (key == null) { + // GET, PUT, DELETE 2nd arg key = arg; if ((verb == CommandVerb.GET || verb == CommandVerb.DELETE)) { + // GET, DELETE only have 2 args if (peekNextArg() == null) { isValid = true; } else { @@ -159,11 +197,13 @@ public final class DeviceConfigService extends Binder { } } } else if (value == null) { + // PUT 3rd arg (required) value = arg; if (verb == CommandVerb.PUT && peekNextArg() == null) { isValid = true; } } else if ("default".equalsIgnoreCase(arg)) { + // PUT 4th arg (optional) makeDefault = true; if (verb == CommandVerb.PUT && peekNextArg() == null) { isValid = true; @@ -211,6 +251,12 @@ public final class DeviceConfigService extends Binder { case RESET: DeviceConfig.resetToDefaults(resetMode, namespace); break; + case SET_SYNC_DISABLED_FOR_TESTS: + DeviceConfig.setSyncDisabled(disableSyncMode); + break; + case IS_SYNC_DISABLED_FOR_TESTS: + pout.println(DeviceConfig.isSyncDisabled()); + break; default: perr.println("Unspecified command"); return -1; @@ -241,6 +287,16 @@ public final class DeviceConfigService extends Binder { + "trusted_defaults}"); pw.println(" NAMESPACE limits which flags are reset if provided, otherwise all " + "flags are reset"); + pw.println(" set_sync_disabled_for_tests SYNC_DISABLED_MODE"); + pw.println(" Modifies bulk property setting behavior for tests. When in one of the" + + " disabled modes this ensures that config isn't overwritten."); + pw.println(" SYNC_DISABLED_MODE is one of:"); + pw.println(" none: Sync is not disabled. A reboot may be required to restart" + + " syncing."); + pw.println(" persistent: Sync is disabled, this state will survive a reboot."); + pw.println(" until_reboot: Sync is disabled until the next reboot."); + pw.println(" is_sync_disabled_for_tests"); + pw.println(" Prints 'true' if sync is disabled, 'false' otherwise."); } private boolean delete(IContentProvider provider, String namespace, String key) { diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 941f47f52551..13b3684647a2 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -19,6 +19,12 @@ package com.android.providers.settings; import static android.os.Process.ROOT_UID; import static android.os.Process.SHELL_UID; import static android.os.Process.SYSTEM_UID; +import static android.provider.Settings.Config.SYNC_DISABLED_MODE_NONE; +import static android.provider.Settings.Config.SYNC_DISABLED_MODE_PERSISTENT; +import static android.provider.Settings.Config.SYNC_DISABLED_MODE_UNTIL_REBOOT; +import static android.provider.Settings.SET_ALL_RESULT_DISABLED; +import static android.provider.Settings.SET_ALL_RESULT_FAILURE; +import static android.provider.Settings.SET_ALL_RESULT_SUCCESS; import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER; import static android.provider.Settings.Secure.NOTIFICATION_BUBBLES; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY; @@ -80,8 +86,10 @@ import android.os.UserHandle; import android.os.UserManager; import android.provider.DeviceConfig; import android.provider.Settings; +import android.provider.Settings.Config.SyncDisabledMode; import android.provider.Settings.Global; import android.provider.Settings.Secure; +import android.provider.Settings.SetAllResult; import android.provider.settings.validators.SystemSettingsValidators; import android.provider.settings.validators.Validator; import android.text.TextUtils; @@ -335,6 +343,9 @@ public class SettingsProvider extends ContentProvider { // We have to call in the package manager with no lock held, private volatile IPackageManager mPackageManager; + @GuardedBy("mLock") + private boolean mSyncConfigDisabledUntilReboot; + public static int makeKey(int type, int userId) { return SettingsState.makeKey(type, userId); } @@ -440,11 +451,24 @@ public class SettingsProvider extends ContentProvider { String prefix = getSettingPrefix(args); Map<String, String> flags = getSettingFlags(args); Bundle result = new Bundle(); - result.putBoolean(Settings.KEY_CONFIG_SET_RETURN, + result.putInt(Settings.KEY_CONFIG_SET_ALL_RETURN, setAllConfigSettings(prefix, flags)); return result; } + case Settings.CALL_METHOD_SET_SYNC_DISABLED_CONFIG: { + final int mode = getSyncDisabledMode(args); + setSyncDisabledConfig(mode); + break; + } + + case Settings.CALL_METHOD_IS_SYNC_DISABLED_CONFIG: { + Bundle result = new Bundle(); + result.putBoolean(Settings.KEY_CONFIG_IS_SYNC_DISABLED_RETURN, + isSyncDisabledConfig()); + return result; + } + case Settings.CALL_METHOD_RESET_CONFIG: { final int mode = getResetModeEnforcingPermission(args); String prefix = getSettingPrefix(args); @@ -1099,7 +1123,8 @@ public class SettingsProvider extends ContentProvider { MUTATION_OPERATION_INSERT, 0); } - private boolean setAllConfigSettings(String prefix, Map<String, String> keyValues) { + + private @SetAllResult int setAllConfigSettings(String prefix, Map<String, String> keyValues) { if (DEBUG) { Slog.v(LOG_TAG, "setAllConfigSettings for prefix: " + prefix); } @@ -1107,9 +1132,95 @@ public class SettingsProvider extends ContentProvider { enforceWritePermission(Manifest.permission.WRITE_DEVICE_CONFIG); synchronized (mLock) { + if (isSyncDisabledConfigLocked()) { + return SET_ALL_RESULT_DISABLED; + } final int key = makeKey(SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM); - return mSettingsRegistry.setConfigSettingsLocked(key, prefix, keyValues, + boolean success = mSettingsRegistry.setConfigSettingsLocked(key, prefix, keyValues, resolveCallingPackage()); + return success ? SET_ALL_RESULT_SUCCESS : SET_ALL_RESULT_FAILURE; + } + } + + private void setSyncDisabledConfig(@SyncDisabledMode int syncDisabledMode) { + if (DEBUG) { + Slog.v(LOG_TAG, "setSyncDisabledConfig(" + syncDisabledMode + ")"); + } + + enforceWritePermission(Manifest.permission.WRITE_DEVICE_CONFIG); + + synchronized (mLock) { + setSyncDisabledConfigLocked(syncDisabledMode); + } + } + + private boolean isSyncDisabledConfig() { + if (DEBUG) { + Slog.v(LOG_TAG, "isSyncDisabledConfig"); + } + + enforceWritePermission(Manifest.permission.WRITE_DEVICE_CONFIG); + + synchronized (mLock) { + return isSyncDisabledConfigLocked(); + } + } + + @GuardedBy("mLock") + private void setSyncDisabledConfigLocked(@SyncDisabledMode int syncDisabledMode) { + boolean persistentValue; + boolean inMemoryValue; + if (syncDisabledMode == SYNC_DISABLED_MODE_NONE) { + persistentValue = false; + inMemoryValue = false; + } else if (syncDisabledMode == SYNC_DISABLED_MODE_PERSISTENT) { + persistentValue = true; + inMemoryValue = false; + } else if (syncDisabledMode == SYNC_DISABLED_MODE_UNTIL_REBOOT) { + persistentValue = false; + inMemoryValue = true; + } else { + throw new IllegalArgumentException(Integer.toString(syncDisabledMode)); + } + + mSyncConfigDisabledUntilReboot = inMemoryValue; + + CallingIdentity callingIdentity = clearCallingIdentity(); + try { + String globalSettingValue = persistentValue ? "1" : "0"; + mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_GLOBAL, + UserHandle.USER_SYSTEM, Settings.Global.DEVICE_CONFIG_SYNC_DISABLED, + globalSettingValue, /*tag=*/null, /*makeDefault=*/false, + SettingsState.SYSTEM_PACKAGE_NAME, /*forceNotify=*/false, + /*criticalSettings=*/null, Settings.DEFAULT_OVERRIDEABLE_BY_RESTORE); + } finally { + restoreCallingIdentity(callingIdentity); + } + } + + @GuardedBy("mLock") + private boolean isSyncDisabledConfigLocked() { + // Check the values used for both SYNC_DISABLED_MODE_PERSISTENT and + // SYNC_DISABLED_MODE_UNTIL_REBOOT. + + // The SYNC_DISABLED_MODE_UNTIL_REBOOT value is cheap to check first. + if (mSyncConfigDisabledUntilReboot) { + return true; + } + + // Now check the global setting used to implement SYNC_DISABLED_MODE_PERSISTENT. + CallingIdentity callingIdentity = clearCallingIdentity(); + try { + Setting settingLocked = mSettingsRegistry.getSettingLocked( + SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM, + Global.DEVICE_CONFIG_SYNC_DISABLED); + if (settingLocked == null) { + return false; + } + String settingValue = settingLocked.getValue(); + return settingValue != null && !"0".equals(settingValue); + } finally { + restoreCallingIdentity(callingIdentity); } } @@ -2202,6 +2313,16 @@ public class SettingsProvider extends ContentProvider { return (args != null) && args.getBoolean(Settings.CALL_METHOD_OVERRIDEABLE_BY_RESTORE_KEY); } + private static int getSyncDisabledMode(Bundle args) { + final int mode = (args != null) + ? args.getInt(Settings.CALL_METHOD_SYNC_DISABLED_MODE_KEY) : -1; + if (mode == SYNC_DISABLED_MODE_NONE || mode == SYNC_DISABLED_MODE_UNTIL_REBOOT + || mode == SYNC_DISABLED_MODE_PERSISTENT) { + return mode; + } + throw new IllegalArgumentException("Invalid sync disabled mode: " + mode); + } + private static int getResetModeEnforcingPermission(Bundle args) { final int mode = (args != null) ? args.getInt(Settings.CALL_METHOD_RESET_MODE_KEY) : 0; switch (mode) { |