Loading core/java/android/content/res/flags.aconfig +12 −1 Original line number Original line Diff line number Diff line Loading @@ -138,3 +138,14 @@ flag { description: "flag always meant to be false, for testing resource flagging within cts tests" description: "flag always meant to be false, for testing resource flagging within cts tests" bug: "377974898" bug: "377974898" } } flag { name: "use_new_aconfig_storage" is_exported: true namespace: "resource_manager" description: "Retrieve flag values from new Aconfig flag storage in AconfigFlags.java" bug: "352348353" metadata { purpose: PURPOSE_BUGFIX } } core/java/com/android/internal/pm/pkg/component/AconfigFlags.java +62 −15 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.internal.pm.pkg.component; package com.android.internal.pm.pkg.component; import static android.provider.flags.Flags.newStoragePublicApi; import static com.android.internal.pm.pkg.parsing.ParsingUtils.ANDROID_RES_NAMESPACE; import static com.android.internal.pm.pkg.parsing.ParsingUtils.ANDROID_RES_NAMESPACE; import android.aconfig.DeviceProtos; import android.aconfig.DeviceProtos; Loading @@ -27,6 +28,7 @@ import android.annotation.Nullable; import android.content.res.Flags; import android.content.res.Flags; import android.os.Environment; import android.os.Environment; import android.os.Process; import android.os.Process; import android.os.flagging.AconfigPackage; import android.util.ArrayMap; import android.util.ArrayMap; import android.util.Slog; import android.util.Slog; import android.util.Xml; import android.util.Xml; Loading @@ -43,6 +45,7 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.IOException; import java.util.Arrays; import java.util.Arrays; import java.util.Map; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** /** * A class that manages a cache of all device feature flags and their default + override values. * A class that manages a cache of all device feature flags and their default + override values. Loading @@ -58,7 +61,8 @@ public class AconfigFlags { private static final String OVERRIDE_PREFIX = "device_config_overrides/"; private static final String OVERRIDE_PREFIX = "device_config_overrides/"; private static final String STAGED_PREFIX = "staged/"; private static final String STAGED_PREFIX = "staged/"; private final ArrayMap<String, Boolean> mFlagValues = new ArrayMap<>(); private final Map<String, Boolean> mFlagValues = new ArrayMap<>(); private final Map<String, AconfigPackage> mAconfigPackages = new ConcurrentHashMap<>(); public AconfigFlags() { public AconfigFlags() { if (!Flags.manifestFlagging()) { if (!Flags.manifestFlagging()) { Loading @@ -67,6 +71,11 @@ public class AconfigFlags { } } return; return; } } if (useNewStorage()) { Slog.i(LOG_TAG, "Using new flag storage"); } else { Slog.i(LOG_TAG, "Using OLD proto flag storage"); final var defaultFlagProtoFiles = final var defaultFlagProtoFiles = (Process.myUid() == Process.SYSTEM_UID) ? DeviceProtos.parsedFlagsProtoPaths() (Process.myUid() == Process.SYSTEM_UID) ? DeviceProtos.parsedFlagsProtoPaths() : Arrays.asList(DeviceProtos.PATHS); : Arrays.asList(DeviceProtos.PATHS); Loading @@ -74,15 +83,20 @@ public class AconfigFlags { try (var inputStream = new FileInputStream(fileName)) { try (var inputStream = new FileInputStream(fileName)) { loadAconfigDefaultValues(inputStream.readAllBytes()); loadAconfigDefaultValues(inputStream.readAllBytes()); } catch (IOException e) { } catch (IOException e) { Slog.e(LOG_TAG, "Failed to read Aconfig values from " + fileName, e); Slog.w(LOG_TAG, "Failed to read Aconfig values from " + fileName, e); } } } } if (Process.myUid() == Process.SYSTEM_UID) { if (Process.myUid() == Process.SYSTEM_UID) { // Server overrides are only accessible to the system, no need to even try loading them // Server overrides are only accessible to the system, no need to even try loading // in user processes. // them in user processes. loadServerOverrides(); loadServerOverrides(); } } } } } private static boolean useNewStorage() { return newStoragePublicApi() && Flags.useNewAconfigStorage(); } private void loadServerOverrides() { private void loadServerOverrides() { // Reading the proto files is enough for READ_ONLY flags but if it's a READ_WRITE flag // Reading the proto files is enough for READ_ONLY flags but if it's a READ_WRITE flag Loading Loading @@ -200,12 +214,45 @@ public class AconfigFlags { */ */ @Nullable @Nullable public Boolean getFlagValue(@NonNull String flagPackageAndName) { public Boolean getFlagValue(@NonNull String flagPackageAndName) { if (useNewStorage()) { return getFlagValueFromNewStorage(flagPackageAndName); } else { Boolean value = mFlagValues.get(flagPackageAndName); Boolean value = mFlagValues.get(flagPackageAndName); if (DEBUG) { if (DEBUG) { Slog.v(LOG_TAG, "Aconfig flag value for " + flagPackageAndName + " = " + value); Slog.v(LOG_TAG, "Aconfig flag value for " + flagPackageAndName + " = " + value); } } return value; return value; } } } private Boolean getFlagValueFromNewStorage(String flagPackageAndName) { int index = flagPackageAndName.lastIndexOf('.'); if (index < 0) { Slog.e(LOG_TAG, "Unable to parse package name from " + flagPackageAndName); return null; } String flagPackage = flagPackageAndName.substring(0, index); String flagName = flagPackageAndName.substring(index + 1); Boolean value = null; AconfigPackage aconfigPackage = mAconfigPackages.computeIfAbsent(flagPackage, p -> { try { return AconfigPackage.load(p); } catch (Exception e) { Slog.e(LOG_TAG, "Failed to load aconfig package " + p, e); return null; } }); if (aconfigPackage != null) { // Default value is false for when the flag is not found. // Note: Unlike with the old storage, with AconfigPackage, we don't have a way to // know if the flag is not found or if it's found but the value is false. value = aconfigPackage.getBooleanFlagValue(flagName, false); } if (DEBUG) { Slog.v(LOG_TAG, "Aconfig flag value for " + flagPackageAndName + " = " + value); } return value; } /** /** * Check if the element in {@code parser} should be skipped because of the feature flag. * Check if the element in {@code parser} should be skipped because of the feature flag. Loading Loading
core/java/android/content/res/flags.aconfig +12 −1 Original line number Original line Diff line number Diff line Loading @@ -138,3 +138,14 @@ flag { description: "flag always meant to be false, for testing resource flagging within cts tests" description: "flag always meant to be false, for testing resource flagging within cts tests" bug: "377974898" bug: "377974898" } } flag { name: "use_new_aconfig_storage" is_exported: true namespace: "resource_manager" description: "Retrieve flag values from new Aconfig flag storage in AconfigFlags.java" bug: "352348353" metadata { purpose: PURPOSE_BUGFIX } }
core/java/com/android/internal/pm/pkg/component/AconfigFlags.java +62 −15 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.internal.pm.pkg.component; package com.android.internal.pm.pkg.component; import static android.provider.flags.Flags.newStoragePublicApi; import static com.android.internal.pm.pkg.parsing.ParsingUtils.ANDROID_RES_NAMESPACE; import static com.android.internal.pm.pkg.parsing.ParsingUtils.ANDROID_RES_NAMESPACE; import android.aconfig.DeviceProtos; import android.aconfig.DeviceProtos; Loading @@ -27,6 +28,7 @@ import android.annotation.Nullable; import android.content.res.Flags; import android.content.res.Flags; import android.os.Environment; import android.os.Environment; import android.os.Process; import android.os.Process; import android.os.flagging.AconfigPackage; import android.util.ArrayMap; import android.util.ArrayMap; import android.util.Slog; import android.util.Slog; import android.util.Xml; import android.util.Xml; Loading @@ -43,6 +45,7 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.IOException; import java.util.Arrays; import java.util.Arrays; import java.util.Map; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** /** * A class that manages a cache of all device feature flags and their default + override values. * A class that manages a cache of all device feature flags and their default + override values. Loading @@ -58,7 +61,8 @@ public class AconfigFlags { private static final String OVERRIDE_PREFIX = "device_config_overrides/"; private static final String OVERRIDE_PREFIX = "device_config_overrides/"; private static final String STAGED_PREFIX = "staged/"; private static final String STAGED_PREFIX = "staged/"; private final ArrayMap<String, Boolean> mFlagValues = new ArrayMap<>(); private final Map<String, Boolean> mFlagValues = new ArrayMap<>(); private final Map<String, AconfigPackage> mAconfigPackages = new ConcurrentHashMap<>(); public AconfigFlags() { public AconfigFlags() { if (!Flags.manifestFlagging()) { if (!Flags.manifestFlagging()) { Loading @@ -67,6 +71,11 @@ public class AconfigFlags { } } return; return; } } if (useNewStorage()) { Slog.i(LOG_TAG, "Using new flag storage"); } else { Slog.i(LOG_TAG, "Using OLD proto flag storage"); final var defaultFlagProtoFiles = final var defaultFlagProtoFiles = (Process.myUid() == Process.SYSTEM_UID) ? DeviceProtos.parsedFlagsProtoPaths() (Process.myUid() == Process.SYSTEM_UID) ? DeviceProtos.parsedFlagsProtoPaths() : Arrays.asList(DeviceProtos.PATHS); : Arrays.asList(DeviceProtos.PATHS); Loading @@ -74,15 +83,20 @@ public class AconfigFlags { try (var inputStream = new FileInputStream(fileName)) { try (var inputStream = new FileInputStream(fileName)) { loadAconfigDefaultValues(inputStream.readAllBytes()); loadAconfigDefaultValues(inputStream.readAllBytes()); } catch (IOException e) { } catch (IOException e) { Slog.e(LOG_TAG, "Failed to read Aconfig values from " + fileName, e); Slog.w(LOG_TAG, "Failed to read Aconfig values from " + fileName, e); } } } } if (Process.myUid() == Process.SYSTEM_UID) { if (Process.myUid() == Process.SYSTEM_UID) { // Server overrides are only accessible to the system, no need to even try loading them // Server overrides are only accessible to the system, no need to even try loading // in user processes. // them in user processes. loadServerOverrides(); loadServerOverrides(); } } } } } private static boolean useNewStorage() { return newStoragePublicApi() && Flags.useNewAconfigStorage(); } private void loadServerOverrides() { private void loadServerOverrides() { // Reading the proto files is enough for READ_ONLY flags but if it's a READ_WRITE flag // Reading the proto files is enough for READ_ONLY flags but if it's a READ_WRITE flag Loading Loading @@ -200,12 +214,45 @@ public class AconfigFlags { */ */ @Nullable @Nullable public Boolean getFlagValue(@NonNull String flagPackageAndName) { public Boolean getFlagValue(@NonNull String flagPackageAndName) { if (useNewStorage()) { return getFlagValueFromNewStorage(flagPackageAndName); } else { Boolean value = mFlagValues.get(flagPackageAndName); Boolean value = mFlagValues.get(flagPackageAndName); if (DEBUG) { if (DEBUG) { Slog.v(LOG_TAG, "Aconfig flag value for " + flagPackageAndName + " = " + value); Slog.v(LOG_TAG, "Aconfig flag value for " + flagPackageAndName + " = " + value); } } return value; return value; } } } private Boolean getFlagValueFromNewStorage(String flagPackageAndName) { int index = flagPackageAndName.lastIndexOf('.'); if (index < 0) { Slog.e(LOG_TAG, "Unable to parse package name from " + flagPackageAndName); return null; } String flagPackage = flagPackageAndName.substring(0, index); String flagName = flagPackageAndName.substring(index + 1); Boolean value = null; AconfigPackage aconfigPackage = mAconfigPackages.computeIfAbsent(flagPackage, p -> { try { return AconfigPackage.load(p); } catch (Exception e) { Slog.e(LOG_TAG, "Failed to load aconfig package " + p, e); return null; } }); if (aconfigPackage != null) { // Default value is false for when the flag is not found. // Note: Unlike with the old storage, with AconfigPackage, we don't have a way to // know if the flag is not found or if it's found but the value is false. value = aconfigPackage.getBooleanFlagValue(flagName, false); } if (DEBUG) { Slog.v(LOG_TAG, "Aconfig flag value for " + flagPackageAndName + " = " + value); } return value; } /** /** * Check if the element in {@code parser} should be skipped because of the feature flag. * Check if the element in {@code parser} should be skipped because of the feature flag. Loading