summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/provider/DeviceConfig.java32
-rw-r--r--core/java/android/provider/Settings.java157
-rw-r--r--core/tests/coretests/src/android/provider/DeviceConfigTest.java56
-rw-r--r--core/tests/coretests/src/android/provider/NameValueCacheTest.java3
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java3
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java1
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java68
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java127
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) {