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

Commit 19b6418a authored by Ted Bauer's avatar Ted Bauer Committed by Android (Google) Code Review
Browse files

Merge "Load aconfig files from mainline module protobufs." into main

parents 24b50720 7055b48f
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<>();