Loading packages/SettingsProvider/Android.bp +1 −1 Original line number Diff line number Diff line Loading @@ -59,10 +59,10 @@ android_test { // Note we statically link SettingsProviderLib to do some unit tests. It's not accessible otherwise // because this test is not an instrumentation test. (because the target runs in the system process.) "SettingsProviderLib", "androidx.test.rules", "flag-junit", "junit", "libaconfig_java_proto_lite", "mockito-target-minus-junit4", "platform-test-annotations", "truth", Loading packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java +67 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,9 @@ package com.android.providers.settings; import static android.os.Process.FIRST_APPLICATION_UID; import android.aconfig.Aconfig.flag_state; import android.aconfig.Aconfig.parsed_flag; import android.aconfig.Aconfig.parsed_flags; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; Loading Loading @@ -147,6 +150,17 @@ final class SettingsState { */ private static final String CONFIG_STAGED_PREFIX = "staged/"; private static final List<String> sAconfigTextProtoFilesOnDevice = List.of( "/system/etc/aconfig_flags.pb", "/system_ext/etc/aconfig_flags.pb", "/product/etc/aconfig_flags.pb", "/vendor/etc/aconfig_flags.pb"); /** * This tag is applied to all aconfig default value-loaded flags. */ private static final String BOOT_LOADED_DEFAULT_TAG = "BOOT_LOADED_DEFAULT"; // This was used in version 120 and before. private static final String NULL_VALUE_OLD_STYLE = "null"; Loading Loading @@ -315,6 +329,59 @@ final class SettingsState { synchronized (mLock) { readStateSyncLocked(); if (Flags.loadAconfigDefaults()) { // Only load aconfig defaults if this is the first boot, the XML // file doesn't exist yet, or this device is on its first boot after // an OTA. boolean shouldLoadAconfigValues = isConfigSettingsKey(mKey) && (!file.exists() || mContext.getPackageManager().isDeviceUpgrading()); if (shouldLoadAconfigValues) { loadAconfigDefaultValuesLocked(); } } } } @GuardedBy("mLock") private void loadAconfigDefaultValuesLocked() { for (String fileName : sAconfigTextProtoFilesOnDevice) { try (FileInputStream inputStream = new FileInputStream(fileName)) { byte[] contents = inputStream.readAllBytes(); loadAconfigDefaultValues(contents); } catch (IOException e) { Slog.e(LOG_TAG, "failed to read protobuf", e); } } } @VisibleForTesting @GuardedBy("mLock") public void loadAconfigDefaultValues(byte[] fileContents) { try { parsed_flags parsedFlags = parsed_flags.parseFrom(fileContents); if (parsedFlags == null) { Slog.e(LOG_TAG, "failed to parse aconfig protobuf"); return; } for (parsed_flag flag : parsedFlags.getParsedFlagList()) { String flagName = flag.getNamespace() + "/" + flag.getPackage() + "." + flag.getName(); String value = flag.getState() == flag_state.ENABLED ? "true" : "false"; Setting existingSetting = getSettingLocked(flagName); boolean isDefaultLoaded = existingSetting.getTag() != null && existingSetting.getTag().equals(BOOT_LOADED_DEFAULT_TAG); if (existingSetting.getValue() == null || isDefaultLoaded) { insertSettingLocked(flagName, value, BOOT_LOADED_DEFAULT_TAG, false, flag.getPackage()); } } } catch (IOException e) { Slog.e(LOG_TAG, "failed to parse protobuf", e); } } Loading packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig +8 −0 Original line number Diff line number Diff line Loading @@ -6,3 +6,11 @@ flag { description: "When enabled, allows setting and displaying local overrides via adb." bug: "298392357" } flag { name: "load_aconfig_defaults" namespace: "core_experiments_team_internal" description: "When enabled, loads aconfig default values into DeviceConfig on boot." bug: "311155098" is_fixed_read_only: true } packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java +83 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,9 @@ */ package com.android.providers.settings; import android.aconfig.Aconfig; import android.aconfig.Aconfig.parsed_flag; import android.aconfig.Aconfig.parsed_flags; import android.os.Looper; import android.test.AndroidTestCase; import android.util.Xml; Loading Loading @@ -84,6 +87,86 @@ public class SettingsStateTest extends AndroidTestCase { super.tearDown(); } public void testLoadValidAconfigProto() { int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0); Object lock = new Object(); SettingsState settingsState = new SettingsState( getContext(), lock, mSettingsFile, configKey, SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); parsed_flags flags = parsed_flags .newBuilder() .addParsedFlag(parsed_flag .newBuilder() .setPackage("com.android.flags") .setName("flag1") .setNamespace("test_namespace") .setDescription("test flag") .addBug("12345678") .setState(Aconfig.flag_state.DISABLED) .setPermission(Aconfig.flag_permission.READ_WRITE)) .addParsedFlag(parsed_flag .newBuilder() .setPackage("com.android.flags") .setName("flag2") .setNamespace("test_namespace") .setDescription("another test flag") .addBug("12345678") .setState(Aconfig.flag_state.ENABLED) .setPermission(Aconfig.flag_permission.READ_WRITE)) .build(); synchronized (lock) { settingsState.loadAconfigDefaultValues(flags.toByteArray()); settingsState.persistSettingsLocked(); } settingsState.waitForHandler(); synchronized (lock) { assertEquals("false", settingsState.getSettingLocked( "test_namespace/com.android.flags.flag1").getValue()); assertEquals("true", settingsState.getSettingLocked( "test_namespace/com.android.flags.flag2").getValue()); } } public void testSkipLoadingAconfigFlagWithMissingFields() { int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0); Object lock = new Object(); SettingsState settingsState = new SettingsState( getContext(), lock, mSettingsFile, configKey, SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); parsed_flags flags = parsed_flags .newBuilder() .addParsedFlag(parsed_flag .newBuilder() .setDescription("test flag") .addBug("12345678") .setState(Aconfig.flag_state.DISABLED) .setPermission(Aconfig.flag_permission.READ_WRITE)) .build(); synchronized (lock) { settingsState.loadAconfigDefaultValues(flags.toByteArray()); settingsState.persistSettingsLocked(); } settingsState.waitForHandler(); synchronized (lock) { assertEquals(null, settingsState.getSettingLocked( "test_namespace/com.android.flags.flag1").getValue()); } } public void testInvalidAconfigProtoDoesNotCrash() { SettingsState settingsState = getSettingStateObject(); settingsState.loadAconfigDefaultValues("invalid protobuf".getBytes()); } public void testIsBinary() { assertFalse(SettingsState.isBinary(" abc 日本語")); Loading Loading
packages/SettingsProvider/Android.bp +1 −1 Original line number Diff line number Diff line Loading @@ -59,10 +59,10 @@ android_test { // Note we statically link SettingsProviderLib to do some unit tests. It's not accessible otherwise // because this test is not an instrumentation test. (because the target runs in the system process.) "SettingsProviderLib", "androidx.test.rules", "flag-junit", "junit", "libaconfig_java_proto_lite", "mockito-target-minus-junit4", "platform-test-annotations", "truth", Loading
packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java +67 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,9 @@ package com.android.providers.settings; import static android.os.Process.FIRST_APPLICATION_UID; import android.aconfig.Aconfig.flag_state; import android.aconfig.Aconfig.parsed_flag; import android.aconfig.Aconfig.parsed_flags; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; Loading Loading @@ -147,6 +150,17 @@ final class SettingsState { */ private static final String CONFIG_STAGED_PREFIX = "staged/"; private static final List<String> sAconfigTextProtoFilesOnDevice = List.of( "/system/etc/aconfig_flags.pb", "/system_ext/etc/aconfig_flags.pb", "/product/etc/aconfig_flags.pb", "/vendor/etc/aconfig_flags.pb"); /** * This tag is applied to all aconfig default value-loaded flags. */ private static final String BOOT_LOADED_DEFAULT_TAG = "BOOT_LOADED_DEFAULT"; // This was used in version 120 and before. private static final String NULL_VALUE_OLD_STYLE = "null"; Loading Loading @@ -315,6 +329,59 @@ final class SettingsState { synchronized (mLock) { readStateSyncLocked(); if (Flags.loadAconfigDefaults()) { // Only load aconfig defaults if this is the first boot, the XML // file doesn't exist yet, or this device is on its first boot after // an OTA. boolean shouldLoadAconfigValues = isConfigSettingsKey(mKey) && (!file.exists() || mContext.getPackageManager().isDeviceUpgrading()); if (shouldLoadAconfigValues) { loadAconfigDefaultValuesLocked(); } } } } @GuardedBy("mLock") private void loadAconfigDefaultValuesLocked() { for (String fileName : sAconfigTextProtoFilesOnDevice) { try (FileInputStream inputStream = new FileInputStream(fileName)) { byte[] contents = inputStream.readAllBytes(); loadAconfigDefaultValues(contents); } catch (IOException e) { Slog.e(LOG_TAG, "failed to read protobuf", e); } } } @VisibleForTesting @GuardedBy("mLock") public void loadAconfigDefaultValues(byte[] fileContents) { try { parsed_flags parsedFlags = parsed_flags.parseFrom(fileContents); if (parsedFlags == null) { Slog.e(LOG_TAG, "failed to parse aconfig protobuf"); return; } for (parsed_flag flag : parsedFlags.getParsedFlagList()) { String flagName = flag.getNamespace() + "/" + flag.getPackage() + "." + flag.getName(); String value = flag.getState() == flag_state.ENABLED ? "true" : "false"; Setting existingSetting = getSettingLocked(flagName); boolean isDefaultLoaded = existingSetting.getTag() != null && existingSetting.getTag().equals(BOOT_LOADED_DEFAULT_TAG); if (existingSetting.getValue() == null || isDefaultLoaded) { insertSettingLocked(flagName, value, BOOT_LOADED_DEFAULT_TAG, false, flag.getPackage()); } } } catch (IOException e) { Slog.e(LOG_TAG, "failed to parse protobuf", e); } } Loading
packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig +8 −0 Original line number Diff line number Diff line Loading @@ -6,3 +6,11 @@ flag { description: "When enabled, allows setting and displaying local overrides via adb." bug: "298392357" } flag { name: "load_aconfig_defaults" namespace: "core_experiments_team_internal" description: "When enabled, loads aconfig default values into DeviceConfig on boot." bug: "311155098" is_fixed_read_only: true }
packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java +83 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,9 @@ */ package com.android.providers.settings; import android.aconfig.Aconfig; import android.aconfig.Aconfig.parsed_flag; import android.aconfig.Aconfig.parsed_flags; import android.os.Looper; import android.test.AndroidTestCase; import android.util.Xml; Loading Loading @@ -84,6 +87,86 @@ public class SettingsStateTest extends AndroidTestCase { super.tearDown(); } public void testLoadValidAconfigProto() { int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0); Object lock = new Object(); SettingsState settingsState = new SettingsState( getContext(), lock, mSettingsFile, configKey, SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); parsed_flags flags = parsed_flags .newBuilder() .addParsedFlag(parsed_flag .newBuilder() .setPackage("com.android.flags") .setName("flag1") .setNamespace("test_namespace") .setDescription("test flag") .addBug("12345678") .setState(Aconfig.flag_state.DISABLED) .setPermission(Aconfig.flag_permission.READ_WRITE)) .addParsedFlag(parsed_flag .newBuilder() .setPackage("com.android.flags") .setName("flag2") .setNamespace("test_namespace") .setDescription("another test flag") .addBug("12345678") .setState(Aconfig.flag_state.ENABLED) .setPermission(Aconfig.flag_permission.READ_WRITE)) .build(); synchronized (lock) { settingsState.loadAconfigDefaultValues(flags.toByteArray()); settingsState.persistSettingsLocked(); } settingsState.waitForHandler(); synchronized (lock) { assertEquals("false", settingsState.getSettingLocked( "test_namespace/com.android.flags.flag1").getValue()); assertEquals("true", settingsState.getSettingLocked( "test_namespace/com.android.flags.flag2").getValue()); } } public void testSkipLoadingAconfigFlagWithMissingFields() { int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0); Object lock = new Object(); SettingsState settingsState = new SettingsState( getContext(), lock, mSettingsFile, configKey, SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); parsed_flags flags = parsed_flags .newBuilder() .addParsedFlag(parsed_flag .newBuilder() .setDescription("test flag") .addBug("12345678") .setState(Aconfig.flag_state.DISABLED) .setPermission(Aconfig.flag_permission.READ_WRITE)) .build(); synchronized (lock) { settingsState.loadAconfigDefaultValues(flags.toByteArray()); settingsState.persistSettingsLocked(); } settingsState.waitForHandler(); synchronized (lock) { assertEquals(null, settingsState.getSettingLocked( "test_namespace/com.android.flags.flag1").getValue()); } } public void testInvalidAconfigProtoDoesNotCrash() { SettingsState settingsState = getSettingStateObject(); settingsState.loadAconfigDefaultValues("invalid protobuf".getBytes()); } public void testIsBinary() { assertFalse(SettingsState.isBinary(" abc 日本語")); Loading