Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7055b48f authored by Ted Bauer's avatar Ted Bauer
Browse files

Load aconfig files from mainline module protobufs.

Test: adb shell device_config list | grep adservices # Confirm that adservices aconfig flags are listed, when the flag is enabled.
Bug: 327383546
Change-Id: I092d257896217fc0269f9a421398752a0336282e
parent 9b5907be
Loading
Loading
Loading
Loading
+50 −12
Original line number Diff line number Diff line
@@ -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;
@@ -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.
     */
@@ -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;
@@ -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) {
@@ -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")
@@ -447,7 +485,7 @@ final class SettingsState {
        return names;
    }

    @Nullable
    @NonNull
    public Map<String, Map<String, String>> getAconfigDefaultValues() {
        synchronized (mLock) {
            return mNamespaceDefaults;
@@ -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
+8 −0
Original line number Diff line number Diff line
@@ -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
}
+0 −33
Original line number Diff line number Diff line
@@ -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;
@@ -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<>();