Loading packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java +50 −12 Original line number Diff line number Diff line Loading @@ -72,6 +72,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; Loading Loading @@ -157,6 +158,9 @@ final class SettingsState { "/product/etc/aconfig_flags.pb", "/vendor/etc/aconfig_flags.pb"); private static final String APEX_DIR = "/apex"; private static final String APEX_ACONFIG_PATH_SUFFIX = "/etc/aconfig_flags.pb"; /** * This tag is applied to all aconfig default value-loaded flags. */ Loading Loading @@ -238,7 +242,7 @@ final class SettingsState { private int mNextHistoricalOpIdx; @GuardedBy("mLock") @Nullable @NonNull private Map<String, Map<String, String>> mNamespaceDefaults; public static final int SETTINGS_TYPE_GLOBAL = 0; Loading Loading @@ -332,23 +336,29 @@ final class SettingsState { mHistoricalOperations = Build.IS_DEBUGGABLE ? new ArrayList<>(HISTORICAL_OPERATION_COUNT) : null; mNamespaceDefaults = new HashMap<>(); synchronized (mLock) { readStateSyncLocked(); if (Flags.loadAconfigDefaults()) { if (isConfigSettingsKey(mKey)) { loadAconfigDefaultValuesLocked(); loadAconfigDefaultValuesLocked(sAconfigTextProtoFilesOnDevice); } } if (Flags.loadApexAconfigProtobufs()) { if (isConfigSettingsKey(mKey)) { List<String> apexProtoPaths = listApexProtoPaths(); loadAconfigDefaultValuesLocked(apexProtoPaths); } } } } @GuardedBy("mLock") private void loadAconfigDefaultValuesLocked() { mNamespaceDefaults = new HashMap<>(); for (String fileName : sAconfigTextProtoFilesOnDevice) { private void loadAconfigDefaultValuesLocked(List<String> filePaths) { for (String fileName : filePaths) { try (FileInputStream inputStream = new FileInputStream(fileName)) { loadAconfigDefaultValues(inputStream.readAllBytes(), mNamespaceDefaults); } catch (IOException e) { Loading @@ -357,14 +367,42 @@ final class SettingsState { } } private List<String> listApexProtoPaths() { LinkedList<String> paths = new LinkedList(); File apexDirectory = new File(APEX_DIR); if (!apexDirectory.isDirectory()) { return paths; } File[] subdirs = apexDirectory.listFiles(); if (subdirs == null) { return paths; } for (File prefix : subdirs) { // For each mainline modules, there are two directories, one <modulepackage>/, // and one <modulepackage>@<versioncode>/. Just read the former. if (prefix.getAbsolutePath().contains("@")) { continue; } File protoPath = new File(prefix + APEX_ACONFIG_PATH_SUFFIX); if (!protoPath.exists()) { continue; } paths.add(protoPath.getAbsolutePath()); } return paths; } @VisibleForTesting @GuardedBy("mLock") public void addAconfigDefaultValuesFromMap( @NonNull Map<String, Map<String, String>> defaultMap) { if (mNamespaceDefaults != null) { mNamespaceDefaults.putAll(defaultMap); } } @VisibleForTesting @GuardedBy("mLock") Loading Loading @@ -447,7 +485,7 @@ final class SettingsState { return names; } @Nullable @NonNull public Map<String, Map<String, String>> getAconfigDefaultValues() { synchronized (mLock) { return mNamespaceDefaults; Loading Loading @@ -519,9 +557,9 @@ final class SettingsState { return false; } // Aconfig flags are always boot stable, so we anytime we write one, we staged it to be // Aconfig flags are always boot stable, so we anytime we write one, we stage it to be // applied on reboot. if (Flags.stageAllAconfigFlags() && mNamespaceDefaults != null) { if (Flags.stageAllAconfigFlags()) { int slashIndex = name.indexOf("/"); boolean stageFlag = isConfigSettingsKey(mKey) && slashIndex != -1 Loading packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig +8 −0 Original line number Diff line number Diff line Loading @@ -25,3 +25,11 @@ flag { purpose: PURPOSE_BUGFIX } } flag { name: "load_apex_aconfig_protobufs" namespace: "core_experiments_team_internal" description: "When enabled, loads aconfig default values in apex flag protobufs into DeviceConfig on boot." bug: "327383546" is_fixed_read_only: true } packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java +0 −33 Original line number Diff line number Diff line Loading @@ -25,7 +25,6 @@ import android.aconfig.Aconfig; import android.aconfig.Aconfig.parsed_flag; import android.aconfig.Aconfig.parsed_flags; import android.os.Looper; import android.platform.test.annotations.RequiresFlagsDisabled; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; Loading Loading @@ -230,38 +229,6 @@ public class SettingsStateTest { } } @Test @RequiresFlagsDisabled(Flags.FLAG_LOAD_ACONFIG_DEFAULTS) public void testAddingAconfigMapOnNullIsNoOp() { int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0); Object lock = new Object(); SettingsState settingsState = new SettingsState( InstrumentationRegistry.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("flag5") .setNamespace("test_namespace") .setDescription("test flag") .addBug("12345678") .setState(Aconfig.flag_state.DISABLED) .setPermission(Aconfig.flag_permission.READ_WRITE)) .build(); synchronized (lock) { Map<String, Map<String, String>> defaults = new HashMap<>(); settingsState.loadAconfigDefaultValues(flags.toByteArray(), defaults); settingsState.addAconfigDefaultValuesFromMap(defaults); assertEquals(null, settingsState.getAconfigDefaultValues()); } } @Test public void testInvalidAconfigProtoDoesNotCrash() { Map<String, Map<String, String>> defaults = new HashMap<>(); Loading Loading
packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java +50 −12 Original line number Diff line number Diff line Loading @@ -72,6 +72,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; Loading Loading @@ -157,6 +158,9 @@ final class SettingsState { "/product/etc/aconfig_flags.pb", "/vendor/etc/aconfig_flags.pb"); private static final String APEX_DIR = "/apex"; private static final String APEX_ACONFIG_PATH_SUFFIX = "/etc/aconfig_flags.pb"; /** * This tag is applied to all aconfig default value-loaded flags. */ Loading Loading @@ -238,7 +242,7 @@ final class SettingsState { private int mNextHistoricalOpIdx; @GuardedBy("mLock") @Nullable @NonNull private Map<String, Map<String, String>> mNamespaceDefaults; public static final int SETTINGS_TYPE_GLOBAL = 0; Loading Loading @@ -332,23 +336,29 @@ final class SettingsState { mHistoricalOperations = Build.IS_DEBUGGABLE ? new ArrayList<>(HISTORICAL_OPERATION_COUNT) : null; mNamespaceDefaults = new HashMap<>(); synchronized (mLock) { readStateSyncLocked(); if (Flags.loadAconfigDefaults()) { if (isConfigSettingsKey(mKey)) { loadAconfigDefaultValuesLocked(); loadAconfigDefaultValuesLocked(sAconfigTextProtoFilesOnDevice); } } if (Flags.loadApexAconfigProtobufs()) { if (isConfigSettingsKey(mKey)) { List<String> apexProtoPaths = listApexProtoPaths(); loadAconfigDefaultValuesLocked(apexProtoPaths); } } } } @GuardedBy("mLock") private void loadAconfigDefaultValuesLocked() { mNamespaceDefaults = new HashMap<>(); for (String fileName : sAconfigTextProtoFilesOnDevice) { private void loadAconfigDefaultValuesLocked(List<String> filePaths) { for (String fileName : filePaths) { try (FileInputStream inputStream = new FileInputStream(fileName)) { loadAconfigDefaultValues(inputStream.readAllBytes(), mNamespaceDefaults); } catch (IOException e) { Loading @@ -357,14 +367,42 @@ final class SettingsState { } } private List<String> listApexProtoPaths() { LinkedList<String> paths = new LinkedList(); File apexDirectory = new File(APEX_DIR); if (!apexDirectory.isDirectory()) { return paths; } File[] subdirs = apexDirectory.listFiles(); if (subdirs == null) { return paths; } for (File prefix : subdirs) { // For each mainline modules, there are two directories, one <modulepackage>/, // and one <modulepackage>@<versioncode>/. Just read the former. if (prefix.getAbsolutePath().contains("@")) { continue; } File protoPath = new File(prefix + APEX_ACONFIG_PATH_SUFFIX); if (!protoPath.exists()) { continue; } paths.add(protoPath.getAbsolutePath()); } return paths; } @VisibleForTesting @GuardedBy("mLock") public void addAconfigDefaultValuesFromMap( @NonNull Map<String, Map<String, String>> defaultMap) { if (mNamespaceDefaults != null) { mNamespaceDefaults.putAll(defaultMap); } } @VisibleForTesting @GuardedBy("mLock") Loading Loading @@ -447,7 +485,7 @@ final class SettingsState { return names; } @Nullable @NonNull public Map<String, Map<String, String>> getAconfigDefaultValues() { synchronized (mLock) { return mNamespaceDefaults; Loading Loading @@ -519,9 +557,9 @@ final class SettingsState { return false; } // Aconfig flags are always boot stable, so we anytime we write one, we staged it to be // Aconfig flags are always boot stable, so we anytime we write one, we stage it to be // applied on reboot. if (Flags.stageAllAconfigFlags() && mNamespaceDefaults != null) { if (Flags.stageAllAconfigFlags()) { int slashIndex = name.indexOf("/"); boolean stageFlag = isConfigSettingsKey(mKey) && slashIndex != -1 Loading
packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig +8 −0 Original line number Diff line number Diff line Loading @@ -25,3 +25,11 @@ flag { purpose: PURPOSE_BUGFIX } } flag { name: "load_apex_aconfig_protobufs" namespace: "core_experiments_team_internal" description: "When enabled, loads aconfig default values in apex flag protobufs into DeviceConfig on boot." bug: "327383546" is_fixed_read_only: true }
packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java +0 −33 Original line number Diff line number Diff line Loading @@ -25,7 +25,6 @@ import android.aconfig.Aconfig; import android.aconfig.Aconfig.parsed_flag; import android.aconfig.Aconfig.parsed_flags; import android.os.Looper; import android.platform.test.annotations.RequiresFlagsDisabled; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; Loading Loading @@ -230,38 +229,6 @@ public class SettingsStateTest { } } @Test @RequiresFlagsDisabled(Flags.FLAG_LOAD_ACONFIG_DEFAULTS) public void testAddingAconfigMapOnNullIsNoOp() { int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0); Object lock = new Object(); SettingsState settingsState = new SettingsState( InstrumentationRegistry.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("flag5") .setNamespace("test_namespace") .setDescription("test flag") .addBug("12345678") .setState(Aconfig.flag_state.DISABLED) .setPermission(Aconfig.flag_permission.READ_WRITE)) .build(); synchronized (lock) { Map<String, Map<String, String>> defaults = new HashMap<>(); settingsState.loadAconfigDefaultValues(flags.toByteArray(), defaults); settingsState.addAconfigDefaultValuesFromMap(defaults); assertEquals(null, settingsState.getAconfigDefaultValues()); } } @Test public void testInvalidAconfigProtoDoesNotCrash() { Map<String, Map<String, String>> defaults = new HashMap<>(); Loading