summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTed Bauer <tedbauer@google.com>2023-08-31 23:37:02 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2023-08-31 23:37:02 +0000
commit3740d5bac307f8625f959f9b586db56af021f60e (patch)
treef072cc2557ce8ae3c1bf49815388d8e51ee98d30
parenta0887835ca2f6fe7d168a76f307936879175fede (diff)
parentaa82369979da7e3a8b7ba79963b3b0e22d5e1e37 (diff)
downloadConfigInfrastructure-3740d5bac307f8625f959f9b586db56af021f60e.tar.gz
Merge "Add APIs to set sticky local overrides in DC." into main
-rw-r--r--apex/Android.bp1
-rw-r--r--framework/Android.bp4
-rw-r--r--framework/api/system-current.txt5
-rw-r--r--framework/jarjar-rules.txt1
-rw-r--r--framework/java/android/provider/DeviceConfig.java154
-rw-r--r--framework/java/android/provider/DeviceConfigDataStore.java2
-rw-r--r--framework/java/android/provider/SettingsConfigDataStore.java10
7 files changed, 177 insertions, 0 deletions
diff --git a/apex/Android.bp b/apex/Android.bp
index 325eeee..243af7c 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -41,6 +41,7 @@ bootclasspath_fragment {
// result in a build failure due to inconsistent flags.
package_prefixes: [
"android.provider.aidl",
+ "android.provider.internal.modules.utils.build",
],
},
// The bootclasspath_fragments that provide APIs on which this depends.
diff --git a/framework/Android.bp b/framework/Android.bp
index f14baea..1fb9ffd 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -35,4 +35,8 @@ java_sdk_library {
impl_library_visibility: [
"//packages/modules/ConfigInfrastructure:__subpackages__"
],
+ static_libs: [
+ "modules-utils-build",
+ ],
+ jarjar_rules: "jarjar-rules.txt",
}
diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt
index 254a8b3..a69e7fd 100644
--- a/framework/api/system-current.txt
+++ b/framework/api/system-current.txt
@@ -3,9 +3,12 @@ package android.provider {
public final class DeviceConfig {
method public static void addOnPropertiesChangedListener(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.provider.DeviceConfig.OnPropertiesChangedListener);
+ method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static void clearAllLocalOverrides();
+ method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static void clearLocalOverride(@NonNull String, @NonNull String);
method @RequiresPermission(android.Manifest.permission.MONITOR_DEVICE_CONFIG_ACCESS) public static void clearMonitorCallback(@NonNull android.content.ContentResolver);
method @RequiresPermission(anyOf={android.Manifest.permission.WRITE_DEVICE_CONFIG, android.Manifest.permission.WRITE_ALLOWLISTED_DEVICE_CONFIG}) public static boolean deleteProperty(@NonNull String, @NonNull String);
method @NonNull public static java.util.Set<java.lang.String> getAdbWritableFlags();
+ method @NonNull public static java.util.Set<android.provider.DeviceConfig.Properties> getAllProperties();
method public static boolean getBoolean(@NonNull String, @NonNull String, boolean);
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static float getFloat(@NonNull String, @NonNull String, float);
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static int getInt(@NonNull String, @NonNull String, int);
@@ -15,8 +18,10 @@ package android.provider {
method @NonNull public static java.util.List<java.lang.String> getPublicNamespaces();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static String getString(@NonNull String, @NonNull String, @Nullable String);
method @RequiresPermission(anyOf={android.Manifest.permission.WRITE_DEVICE_CONFIG, android.Manifest.permission.READ_WRITE_SYNC_DISABLED_MODE_CONFIG}) public static int getSyncDisabledMode();
+ method @NonNull public static java.util.Map<java.lang.String,java.util.Map<java.lang.String,java.lang.String>> getUnderlyingValuesForOverriddenFlags();
method public static void removeOnPropertiesChangedListener(@NonNull android.provider.DeviceConfig.OnPropertiesChangedListener);
method @RequiresPermission(anyOf={android.Manifest.permission.WRITE_DEVICE_CONFIG, android.Manifest.permission.WRITE_ALLOWLISTED_DEVICE_CONFIG}) public static void resetToDefaults(int, @Nullable String);
+ method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setLocalOverride(@NonNull String, @NonNull String, @NonNull String);
method @RequiresPermission(android.Manifest.permission.MONITOR_DEVICE_CONFIG_ACCESS) public static void setMonitorCallback(@NonNull android.content.ContentResolver, @NonNull java.util.concurrent.Executor, @NonNull android.provider.DeviceConfig.MonitorCallback);
method @RequiresPermission(anyOf={android.Manifest.permission.WRITE_DEVICE_CONFIG, android.Manifest.permission.WRITE_ALLOWLISTED_DEVICE_CONFIG}) public static boolean setProperties(@NonNull android.provider.DeviceConfig.Properties) throws android.provider.DeviceConfig.BadConfigException;
method @RequiresPermission(anyOf={android.Manifest.permission.WRITE_DEVICE_CONFIG, android.Manifest.permission.WRITE_ALLOWLISTED_DEVICE_CONFIG}) public static boolean setProperty(@NonNull String, @NonNull String, @Nullable String, boolean);
diff --git a/framework/jarjar-rules.txt b/framework/jarjar-rules.txt
new file mode 100644
index 0000000..c0fbba5
--- /dev/null
+++ b/framework/jarjar-rules.txt
@@ -0,0 +1 @@
+rule com.android.modules.utils.** android.provider.internal.modules.utils.@1
diff --git a/framework/java/android/provider/DeviceConfig.java b/framework/java/android/provider/DeviceConfig.java
index 203283a..997007e 100644
--- a/framework/java/android/provider/DeviceConfig.java
+++ b/framework/java/android/provider/DeviceConfig.java
@@ -32,6 +32,7 @@ import android.annotation.SystemApi;
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.net.Uri;
+import com.android.modules.utils.build.SdkLevel;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
@@ -46,6 +47,7 @@ import java.lang.annotation.Target;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -1049,6 +1051,9 @@ public final class DeviceConfig {
private static final DeviceConfigDataStore sDataStore = new SettingsConfigDataStore();
+ private static final String DEVICE_CONFIG_OVERRIDES_NAMESPACE =
+ "device_config_overrides";
+
/**
* Interface for monitoring callback functions.
*
@@ -1109,6 +1114,8 @@ public final class DeviceConfig {
* Each call to {@link #setProperties(Properties)} is also atomic and ensures that either none
* or all of the change is picked up here, but never only part of it.
*
+ * If there are any local overrides applied, they will take precedence over underlying values.
+ *
* @param namespace The namespace containing the properties to look up.
* @param names The names of properties to look up, or empty to fetch all properties for the
* given namespace.
@@ -1124,9 +1131,80 @@ public final class DeviceConfig {
@NonNull
@RequiresPermission(READ_DEVICE_CONFIG)
public static Properties getProperties(@NonNull String namespace, @NonNull String... names) {
+ Properties propertiesWithoutOverrides =
+ getPropertiesWithoutOverrides(namespace, names);
+ if (SdkLevel.isAtLeastV()) {
+ return applyOverrides(propertiesWithoutOverrides);
+ } else {
+ return propertiesWithoutOverrides;
+ }
+ }
+
+ @NonNull
+ private static Properties getPropertiesWithoutOverrides(@NonNull String namespace,
+ @NonNull String... names) {
return sDataStore.getProperties(namespace, names);
}
+ private static Properties applyOverrides(@NonNull Properties properties) {
+ Properties overrides =
+ getPropertiesWithoutOverrides(DEVICE_CONFIG_OVERRIDES_NAMESPACE);
+ Map<String, String> newPropertiesMap = new HashMap<>();
+
+ HashSet<String> flags = new HashSet(properties.getKeyset());
+ for (String override : overrides.getKeyset()) {
+ String[] namespaceAndFlag = override.split(":");
+ if (properties.getNamespace().equals(namespaceAndFlag[0])) {
+ flags.add(namespaceAndFlag[1]);
+ }
+ }
+
+ for (String flag : flags) {
+ String override =
+ overrides.getString(properties.getNamespace() + ":" + flag, null);
+ if (override != null) {
+ newPropertiesMap.put(flag, override);
+ } else {
+ newPropertiesMap.put(flag, properties.getString(flag, null));
+ }
+ }
+ return new Properties(properties.getNamespace(), newPropertiesMap);
+ }
+
+ /**
+ * List all stored flags.
+ *
+ * The keys take the form {@code namespace/name}, and the values are the flag values.
+ *
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ public static Set<Properties> getAllProperties() {
+ Map<String, String> properties = sDataStore.getAllProperties();
+ Map<String, Map<String, String>> propertyMaps = new HashMap<>();
+ for (String flag : properties.keySet()) {
+ String[] namespaceAndFlag = flag.split("/");
+ String namespace = namespaceAndFlag[0];
+ String flagName = namespaceAndFlag[1];
+ String override =
+ getProperty(DEVICE_CONFIG_OVERRIDES_NAMESPACE, namespace + ":" + flagName);
+
+ String value = override != null ? override : properties.get(flag);
+
+ if (!propertyMaps.containsKey(namespace)) {
+ propertyMaps.put(namespace, new HashMap<>());
+ }
+ propertyMaps.get(namespace).put(flagName, value);
+ }
+
+ HashSet<Properties> result = new HashSet<>();
+ for (Map.Entry<String, Map<String, String>> entry : propertyMaps.entrySet()) {
+ result.add(new Properties(entry.getKey(), entry.getValue()));
+ }
+ return result;
+ }
+
/**
* Look up the String value of a property for a particular namespace.
*
@@ -1240,6 +1318,82 @@ public final class DeviceConfig {
}
/**
+ * Set flag {@code namespace/name} to {@code value}, and ignores server-updates for this flag.
+ *
+ * Can still be called even if there is no underlying value set.
+ *
+ * Returns {@code true} if successful, or {@code false} if the storage implementation throws
+ * errors.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(WRITE_DEVICE_CONFIG)
+ public static boolean setLocalOverride(@NonNull String namespace, @NonNull String name,
+ @NonNull String value) {
+ return setProperty(DEVICE_CONFIG_OVERRIDES_NAMESPACE, namespace + ":" + name, value, false);
+ }
+
+ /**
+ * Clear all local sticky overrides.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(WRITE_DEVICE_CONFIG)
+ public static void clearAllLocalOverrides() {
+ Properties overrides = getProperties(DEVICE_CONFIG_OVERRIDES_NAMESPACE);
+ for (String overrideName : overrides.getKeyset()) {
+ deleteProperty(DEVICE_CONFIG_OVERRIDES_NAMESPACE, overrideName);
+ }
+ }
+
+ /**
+ * Clear local sticky override for flag {@code namespace/name}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(WRITE_DEVICE_CONFIG)
+ public static void clearLocalOverride(@NonNull String namespace,
+ @NonNull String name) {
+ deleteProperty(DEVICE_CONFIG_OVERRIDES_NAMESPACE, namespace + ":" + name);
+ }
+
+ /**
+ * Return a map containing all flags that have been overridden.
+ *
+ * The keys of the outer map are namespaces. They keys of the inner maps are
+ * flag names. The values of the inner maps are the underlying flag values
+ * (not to be confused with their overridden values).
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public static Map<String, Map<String, String>> getUnderlyingValuesForOverriddenFlags() {
+ Properties overrides = getProperties(DEVICE_CONFIG_OVERRIDES_NAMESPACE);
+ HashMap<String, Map<String, String>> result = new HashMap<>();
+ for (Map.Entry<String, String> entry : overrides.getPropertyValues().entrySet()) {
+ String[] namespaceAndFlag = entry.getKey().split(":");
+ String namespace = namespaceAndFlag[0];
+ String flag = namespaceAndFlag[1];
+
+ String actualValue =
+ getPropertiesWithoutOverrides(namespace, flag)
+ .getString(flag, null);
+ if (result.get(namespace) != null) {
+ result.get(namespace).put(flag, actualValue);
+ } else {
+ HashMap<String, String> innerMap = new HashMap<>();
+ innerMap.put(flag, actualValue);
+ result.put(namespace, innerMap);
+ }
+ }
+ return result;
+ }
+
+ /**
* Create a new property with the provided name and value in the provided namespace, or
* update the value of such a property if it already exists. The same name can exist in multiple
* namespaces and might have different values in any or all namespaces.
diff --git a/framework/java/android/provider/DeviceConfigDataStore.java b/framework/java/android/provider/DeviceConfigDataStore.java
index d74d833..bd9647c 100644
--- a/framework/java/android/provider/DeviceConfigDataStore.java
+++ b/framework/java/android/provider/DeviceConfigDataStore.java
@@ -24,11 +24,13 @@ import android.database.ContentObserver;
import android.provider.DeviceConfig;
import java.util.concurrent.Executor;
+import java.util.Map;
/**
* @hide
*/
public interface DeviceConfigDataStore {
+ @NonNull Map<String, String> getAllProperties();
@NonNull DeviceConfig.Properties getProperties(@NonNull String namespace, @NonNull String ... names);
diff --git a/framework/java/android/provider/SettingsConfigDataStore.java b/framework/java/android/provider/SettingsConfigDataStore.java
index f48017e..33a880d 100644
--- a/framework/java/android/provider/SettingsConfigDataStore.java
+++ b/framework/java/android/provider/SettingsConfigDataStore.java
@@ -17,13 +17,18 @@
package android.provider;
import android.content.ContentResolver;
+import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.annotation.NonNull;
import android.annotation.Nullable;
import java.util.Arrays;
+import java.util.ArrayList;
import java.util.concurrent.Executor;
+import java.util.HashMap;
+import java.util.Map;
+
/**
* TODO: want to change the package of this class
@@ -32,6 +37,11 @@ import java.util.concurrent.Executor;
*/
public class SettingsConfigDataStore implements DeviceConfigDataStore {
@Override
+ public @NonNull Map<String, String> getAllProperties() {
+ return Settings.Config.getAllStrings();
+ }
+
+ @Override
public @NonNull DeviceConfig.Properties getProperties(@NonNull String namespace,
@NonNull String... names) {
return new DeviceConfig.Properties(namespace,