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

Commit 0dd2f0b3 authored by Mark Punzalan's avatar Mark Punzalan
Browse files

Use new flag storage in AconfigFlags

Also reduced logging level of one of the statements from ERROR to WARNING

Bug: 352348353
Bug: 383143057
Flag: android.content.res.use_new_aconfig_storage
Test: build + boot on Pixel 6 Pro
Change-Id: I33949c04a5fa3544dbc48ce5f80e31204f705477
parent 17e32cd6
Loading
Loading
Loading
Loading
+12 −1
Original line number Diff line number Diff line
@@ -130,3 +130,14 @@ flag {
    description: "flag always meant to be false, for testing resource flagging within cts tests"
    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
    }
}
+62 −15
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

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 android.aconfig.DeviceProtos;
@@ -27,6 +28,7 @@ import android.annotation.Nullable;
import android.content.res.Flags;
import android.os.Environment;
import android.os.Process;
import android.os.flagging.AconfigPackage;
import android.util.ArrayMap;
import android.util.Slog;
import android.util.Xml;
@@ -43,6 +45,7 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
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.
@@ -58,7 +61,8 @@ public class AconfigFlags {
    private static final String OVERRIDE_PREFIX = "device_config_overrides/";
    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() {
        if (!Flags.manifestFlagging()) {
@@ -67,6 +71,11 @@ public class AconfigFlags {
            }
            return;
        }

        if (useNewStorage()) {
            Slog.i(LOG_TAG, "Using new flag storage");
        } else {
            Slog.i(LOG_TAG, "Using OLD proto flag storage");
            final var defaultFlagProtoFiles =
                    (Process.myUid() == Process.SYSTEM_UID) ? DeviceProtos.parsedFlagsProtoPaths()
                            : Arrays.asList(DeviceProtos.PATHS);
@@ -74,15 +83,20 @@ public class AconfigFlags {
                try (var inputStream = new FileInputStream(fileName)) {
                    loadAconfigDefaultValues(inputStream.readAllBytes());
                } 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) {
            // Server overrides are only accessible to the system, no need to even try loading them
            // in user processes.
                // Server overrides are only accessible to the system, no need to even try loading
                // them in user processes.
                loadServerOverrides();
            }
        }
    }

    private static boolean useNewStorage() {
        return newStoragePublicApi() && Flags.useNewAconfigStorage();
    }

    private void loadServerOverrides() {
        // Reading the proto files is enough for READ_ONLY flags but if it's a READ_WRITE flag
@@ -200,12 +214,45 @@ public class AconfigFlags {
     */
    @Nullable
    public Boolean getFlagValue(@NonNull String flagPackageAndName) {
        if (useNewStorage()) {
            return getFlagValueFromNewStorage(flagPackageAndName);
        } else {
            Boolean value = mFlagValues.get(flagPackageAndName);
            if (DEBUG) {
                Slog.v(LOG_TAG, "Aconfig flag value for " + flagPackageAndName + " = " + 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.