diff options
author | Ted Bauer <tedbauer@google.com> | 2023-08-31 23:37:02 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2023-08-31 23:37:02 +0000 |
commit | 3740d5bac307f8625f959f9b586db56af021f60e (patch) | |
tree | f072cc2557ce8ae3c1bf49815388d8e51ee98d30 | |
parent | a0887835ca2f6fe7d168a76f307936879175fede (diff) | |
parent | aa82369979da7e3a8b7ba79963b3b0e22d5e1e37 (diff) | |
download | ConfigInfrastructure-3740d5bac307f8625f959f9b586db56af021f60e.tar.gz |
Merge "Add APIs to set sticky local overrides in DC." into main
-rw-r--r-- | apex/Android.bp | 1 | ||||
-rw-r--r-- | framework/Android.bp | 4 | ||||
-rw-r--r-- | framework/api/system-current.txt | 5 | ||||
-rw-r--r-- | framework/jarjar-rules.txt | 1 | ||||
-rw-r--r-- | framework/java/android/provider/DeviceConfig.java | 154 | ||||
-rw-r--r-- | framework/java/android/provider/DeviceConfigDataStore.java | 2 | ||||
-rw-r--r-- | framework/java/android/provider/SettingsConfigDataStore.java | 10 |
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, |