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

Commit af48c4ba authored by Mark Punzalan's avatar Mark Punzalan Committed by Android (Google) Code Review
Browse files

Merge "Include feature flag state in cached package" into main

parents fa36e4a2 1c5e38ca
Loading
Loading
Loading
Loading
+62 −0
Original line number Original line Diff line number Diff line
@@ -421,6 +421,8 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
    @NonNull
    @NonNull
    private String[] mUsesStaticLibrariesSorted;
    private String[] mUsesStaticLibrariesSorted;


    private Map<String, Boolean> mFeatureFlagState = new ArrayMap<>();

    @NonNull
    @NonNull
    public static PackageImpl forParsing(@NonNull String packageName, @NonNull String baseCodePath,
    public static PackageImpl forParsing(@NonNull String packageName, @NonNull String baseCodePath,
            @NonNull String codePath, @NonNull TypedArray manifestArray, boolean isCoreApp,
            @NonNull String codePath, @NonNull TypedArray manifestArray, boolean isCoreApp,
@@ -2819,6 +2821,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
        queriesProviders = Collections.unmodifiableSet(queriesProviders);
        queriesProviders = Collections.unmodifiableSet(queriesProviders);
        mimeGroups = Collections.unmodifiableSet(mimeGroups);
        mimeGroups = Collections.unmodifiableSet(mimeGroups);
        mKnownActivityEmbeddingCerts = Collections.unmodifiableSet(mKnownActivityEmbeddingCerts);
        mKnownActivityEmbeddingCerts = Collections.unmodifiableSet(mKnownActivityEmbeddingCerts);
        mFeatureFlagState = Collections.unmodifiableMap(mFeatureFlagState);
    }
    }


    @Override
    @Override
@@ -3118,6 +3121,8 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,


    @Override
    @Override
    public void writeToParcel(Parcel dest, int flags) {
    public void writeToParcel(Parcel dest, int flags) {
        writeFeatureFlagState(dest);

        sForBoolean.parcel(this.supportsSmallScreens, dest, flags);
        sForBoolean.parcel(this.supportsSmallScreens, dest, flags);
        sForBoolean.parcel(this.supportsNormalScreens, dest, flags);
        sForBoolean.parcel(this.supportsNormalScreens, dest, flags);
        sForBoolean.parcel(this.supportsLargeScreens, dest, flags);
        sForBoolean.parcel(this.supportsLargeScreens, dest, flags);
@@ -3267,6 +3272,27 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
        dest.writeBoolean(this.mAllowCrossUidActivitySwitchFromBelow);
        dest.writeBoolean(this.mAllowCrossUidActivitySwitchFromBelow);
    }
    }


    private void writeFeatureFlagState(@NonNull Parcel dest) {
        // Use a string array to encode flag state. One string per flag in the form `<flag>=<value>`
        // where value is 0 (disabled), 1 (enabled) or ? (unknown flag or value).
        int featureFlagCount = this.mFeatureFlagState.size();
        String[] featureFlagStateAsArray = new String[featureFlagCount];
        var entryIterator = this.mFeatureFlagState.entrySet().iterator();
        for (int i = 0; i < featureFlagCount; i++) {
            var entry = entryIterator.next();
            Boolean flagValue = entry.getValue();
            if (flagValue == null) {
                featureFlagStateAsArray[i] = entry.getKey() + "=?";
            } else if (flagValue.booleanValue()) {
                featureFlagStateAsArray[i] = entry.getKey() + "=1";
            } else {
                featureFlagStateAsArray[i] = entry.getKey() + "=0";
            }

        }
        dest.writeStringArray(featureFlagStateAsArray);
    }

    public PackageImpl(Parcel in) {
    public PackageImpl(Parcel in) {
        this(in, /* callback */ null);
        this(in, /* callback */ null);
    }
    }
@@ -3275,6 +3301,9 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
        mCallback = callback;
        mCallback = callback;
        // We use the boot classloader for all classes that we load.
        // We use the boot classloader for all classes that we load.
        final ClassLoader boot = Object.class.getClassLoader();
        final ClassLoader boot = Object.class.getClassLoader();

        readFeatureFlagState(in);

        this.supportsSmallScreens = sForBoolean.unparcel(in);
        this.supportsSmallScreens = sForBoolean.unparcel(in);
        this.supportsNormalScreens = sForBoolean.unparcel(in);
        this.supportsNormalScreens = sForBoolean.unparcel(in);
        this.supportsLargeScreens = sForBoolean.unparcel(in);
        this.supportsLargeScreens = sForBoolean.unparcel(in);
@@ -3440,6 +3469,27 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
        // to mutate this instance before it's finalized.
        // to mutate this instance before it's finalized.
    }
    }


    private void readFeatureFlagState(@NonNull Parcel in) {
        // See comment in writeFeatureFlagState() for encoding of flag state.
        String[] featureFlagStateAsArray = in.createStringArray();
        for (String s : featureFlagStateAsArray) {
            int sepIndex = s.lastIndexOf('=');
            if (sepIndex >= 0 && sepIndex == s.length() - 2) {
                String flagPackageAndName = s.substring(0, sepIndex);
                char c = s.charAt(sepIndex + 1);
                Boolean flagValue = null;
                if (c == '1') {
                    flagValue = Boolean.TRUE;
                } else if (c == '0') {
                    flagValue = Boolean.FALSE;
                } else if (c != '?') {
                    continue;
                }
                this.mFeatureFlagState.put(flagPackageAndName, flagValue);
            }
        }
    }

    @NonNull
    @NonNull
    public static final Creator<PackageImpl> CREATOR = new Creator<PackageImpl>() {
    public static final Creator<PackageImpl> CREATOR = new Creator<PackageImpl>() {
        @Override
        @Override
@@ -3660,6 +3710,18 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
        return mBaseAppDataDeviceProtectedDirForSystemUser;
        return mBaseAppDataDeviceProtectedDirForSystemUser;
    }
    }


    @Override
    public PackageImpl addFeatureFlag(
            @NonNull String flagPackageAndName,
            @Nullable Boolean flagValue) {
        mFeatureFlagState.put(flagPackageAndName, flagValue);
        return this;
    }

    public Map<String, Boolean> getFeatureFlagState() {
        return mFeatureFlagState;
    }

    /**
    /**
     * Flags used for a internal bitset. These flags should never be persisted or exposed outside
     * Flags used for a internal bitset. These flags should never be persisted or exposed outside
     * of this class. It is expected that PackageCacher explicitly clears itself whenever the
     * of this class. It is expected that PackageCacher explicitly clears itself whenever the
+15 −8
Original line number Original line Diff line number Diff line
@@ -33,6 +33,7 @@ import android.util.Slog;
import android.util.Xml;
import android.util.Xml;


import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.pm.pkg.parsing.ParsingPackage;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlPullParser;


import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParser;
@@ -199,7 +200,7 @@ public class AconfigFlags {
     * @return the current value of the given Aconfig flag, or null if there is no such flag
     * @return the current value of the given Aconfig flag, or null if there is no such flag
     */
     */
    @Nullable
    @Nullable
    private Boolean getFlagValue(@NonNull String flagPackageAndName) {
    public Boolean getFlagValue(@NonNull String flagPackageAndName) {
        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);
@@ -209,10 +210,13 @@ public class AconfigFlags {


    /**
    /**
     * 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.
     * @param pkg The package being parsed
     * @param parser XML parser object currently parsing an element
     * @param parser XML parser object currently parsing an element
     * @return true if the element is disabled because of its feature flag
     * @return true if the element is disabled because of its feature flag
     */
     */
    public boolean skipCurrentElement(@NonNull XmlResourceParser parser) {
    public boolean skipCurrentElement(
            @NonNull ParsingPackage pkg,
            @NonNull XmlResourceParser parser) {
        if (!Flags.manifestFlagging()) {
        if (!Flags.manifestFlagging()) {
            return false;
            return false;
        }
        }
@@ -227,18 +231,21 @@ public class AconfigFlags {
            featureFlag = featureFlag.substring(1).strip();
            featureFlag = featureFlag.substring(1).strip();
        }
        }
        final Boolean flagValue = getFlagValue(featureFlag);
        final Boolean flagValue = getFlagValue(featureFlag);
        boolean shouldSkip = false;
        if (flagValue == null) {
        if (flagValue == null) {
            Slog.w(LOG_TAG, "Skipping element " + parser.getName()
            Slog.w(LOG_TAG, "Skipping element " + parser.getName()
                    + " due to unknown feature flag " + featureFlag);
                    + " due to unknown feature flag " + featureFlag);
            return true;
            shouldSkip = true;
        }
        } else if (flagValue == negated) {
            // Skip if flag==false && attr=="flag" OR flag==true && attr=="!flag" (negated)
            // Skip if flag==false && attr=="flag" OR flag==true && attr=="!flag" (negated)
        if (flagValue == negated) {
            Slog.i(LOG_TAG, "Skipping element " + parser.getName()
            Slog.i(LOG_TAG, "Skipping element " + parser.getName()
                    + " behind feature flag " + featureFlag + " = " + flagValue);
                    + " behind feature flag " + featureFlag + " = " + flagValue);
            return true;
            shouldSkip = true;
        }
        }
        return false;
        if (android.content.pm.Flags.includeFeatureFlagsInPackageCacher()) {
            pkg.addFeatureFlag(featureFlag, flagValue);
        }
        return shouldSkip;
    }
    }


    /**
    /**
+1 −1
Original line number Original line Diff line number Diff line
@@ -61,7 +61,7 @@ public class ComponentParseUtils {
            if (type != XmlPullParser.START_TAG) {
            if (type != XmlPullParser.START_TAG) {
                continue;
                continue;
            }
            }
            if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(parser)) {
            if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(pkg, parser)) {
                continue;
                continue;
            }
            }


+3 −3
Original line number Original line Diff line number Diff line
@@ -54,7 +54,7 @@ public class InstallConstraintsTagParser {
            return input.skip("install-constraints cannot be used by this package");
            return input.skip("install-constraints cannot be used by this package");
        }
        }


        ParseResult<Set<String>> prefixes = parseFingerprintPrefixes(input, res, parser);
        ParseResult<Set<String>> prefixes = parseFingerprintPrefixes(input, pkg, res, parser);
        if (prefixes.isSuccess()) {
        if (prefixes.isSuccess()) {
            if (validateFingerprintPrefixes(prefixes.getResult())) {
            if (validateFingerprintPrefixes(prefixes.getResult())) {
                return input.success(pkg);
                return input.success(pkg);
@@ -68,7 +68,7 @@ public class InstallConstraintsTagParser {
    }
    }


    private static ParseResult<Set<String>> parseFingerprintPrefixes(
    private static ParseResult<Set<String>> parseFingerprintPrefixes(
            ParseInput input, Resources res, XmlResourceParser parser)
            ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)
            throws XmlPullParserException, IOException {
            throws XmlPullParserException, IOException {
        Set<String> prefixes = new ArraySet<>();
        Set<String> prefixes = new ArraySet<>();
        int type;
        int type;
@@ -81,7 +81,7 @@ public class InstallConstraintsTagParser {
                }
                }
                return input.success(prefixes);
                return input.success(prefixes);
            } else if (type == XmlPullParser.START_TAG) {
            } else if (type == XmlPullParser.START_TAG) {
                if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(parser)) {
                if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(pkg, parser)) {
                    continue;
                    continue;
                }
                }
                if (parser.getName().equals(TAG_FINGERPRINT_PREFIX)) {
                if (parser.getName().equals(TAG_FINGERPRINT_PREFIX)) {
+1 −1
Original line number Original line Diff line number Diff line
@@ -393,7 +393,7 @@ public class ParsedActivityUtils {
            if (type != XmlPullParser.START_TAG) {
            if (type != XmlPullParser.START_TAG) {
                continue;
                continue;
            }
            }
            if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(parser)) {
            if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(pkg, parser)) {
                continue;
                continue;
            }
            }


Loading