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

Commit b62f02cb authored by Rhed Jao's avatar Rhed Jao
Browse files

Fix an IllegalStateException in the OverlayConfigParser

The issue could be reproduced when the device defined an
overlay configuration file and an overlay package which
was in the configuration file also declared the required
system property in its manifest. The device works fine when
the required system property matches the corresponding
value on the device. But an IllegalStateException was thrown
from the system if it didn't match.

The root cause is that the package parser fails to parse the
overlay package if the required system property in the manifest
does not match the corresponding value on the device. It resulted
in the OverlayConfigParser could not find the package present
in the partition and throws the exception.

To resolve this issue, this CL skips the overlay configuration
if the package not found was caused by the system property
conditionon on the device.

Bug: 193422327
Test: atest OverlayConfigTest
Change-Id: I5650d796d92e3c4825b0d035e8e3b18f36d4cb47
parent c1349106
Loading
Loading
Loading
Loading
+33 −2
Original line number Diff line number Diff line
@@ -108,6 +108,16 @@ public class ApkLite {
    private final boolean mOverlayIsStatic;
    /** Indicate the priority of this overlay package */
    private final int mOverlayPriority;
    /**
     * A comma separated list of system property names to control whether the overlay should be
     * excluded based on the system property condition.
     */
    private final @Nullable String mRequiredSystemPropertyName;
    /**
     * A comma separated list of system property values to control whether the overlay should be
     * excluded based on the system property condition.
     */
    private final @Nullable String mRequiredSystemPropertyValue;

    /**
     * Indicate the policy to deal with user data when rollback is committed
@@ -125,6 +135,7 @@ public class ApkLite {
            boolean debuggable, boolean profileableByShell, boolean multiArch, boolean use32bitAbi,
            boolean useEmbeddedDex, boolean extractNativeLibs, boolean isolatedSplits,
            String targetPackageName, boolean overlayIsStatic, int overlayPriority,
            String requiredSystemPropertyName, String requiredSystemPropertyValue,
            int minSdkVersion, int targetSdkVersion, int rollbackDataPolicy,
            Set<String> requiredSplitTypes, Set<String> splitTypes) {
        mPath = path;
@@ -153,6 +164,8 @@ public class ApkLite {
        mTargetPackageName = targetPackageName;
        mOverlayIsStatic = overlayIsStatic;
        mOverlayPriority = overlayPriority;
        mRequiredSystemPropertyName = requiredSystemPropertyName;
        mRequiredSystemPropertyValue = requiredSystemPropertyValue;
        mMinSdkVersion = minSdkVersion;
        mTargetSdkVersion = targetSdkVersion;
        mRollbackDataPolicy = rollbackDataPolicy;
@@ -418,6 +431,24 @@ public class ApkLite {
        return mOverlayPriority;
    }

    /**
     * A comma separated list of system property names to control whether the overlay should be
     * excluded based on the system property condition.
     */
    @DataClass.Generated.Member
    public @Nullable String getRequiredSystemPropertyName() {
        return mRequiredSystemPropertyName;
    }

    /**
     * A comma separated list of system property values to control whether the overlay should be
     * excluded based on the system property condition.
     */
    @DataClass.Generated.Member
    public @Nullable String getRequiredSystemPropertyValue() {
        return mRequiredSystemPropertyValue;
    }

    /**
     * Indicate the policy to deal with user data when rollback is committed
     *
@@ -431,10 +462,10 @@ public class ApkLite {
    }

    @DataClass.Generated(
            time = 1628562554893L,
            time = 1631763761543L,
            codegenVersion = "1.0.23",
            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/ApkLite.java",
            inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mSplitTypes\nprivate final  int mVersionCodeMajor\nprivate final  int mVersionCode\nprivate final  int mRevisionCode\nprivate final  int mInstallLocation\nprivate final  int mMinSdkVersion\nprivate final  int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final  boolean mFeatureSplit\nprivate final  boolean mIsolatedSplits\nprivate final  boolean mSplitRequired\nprivate final  boolean mCoreApp\nprivate final  boolean mDebuggable\nprivate final  boolean mProfileableByShell\nprivate final  boolean mMultiArch\nprivate final  boolean mUse32bitAbi\nprivate final  boolean mExtractNativeLibs\nprivate final  boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final  boolean mOverlayIsStatic\nprivate final  int mOverlayPriority\nprivate final  int mRollbackDataPolicy\npublic  long getLongVersionCode()\nprivate  boolean hasAnyRequiredSplitTypes()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
            inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mSplitTypes\nprivate final  int mVersionCodeMajor\nprivate final  int mVersionCode\nprivate final  int mRevisionCode\nprivate final  int mInstallLocation\nprivate final  int mMinSdkVersion\nprivate final  int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final  boolean mFeatureSplit\nprivate final  boolean mIsolatedSplits\nprivate final  boolean mSplitRequired\nprivate final  boolean mCoreApp\nprivate final  boolean mDebuggable\nprivate final  boolean mProfileableByShell\nprivate final  boolean mMultiArch\nprivate final  boolean mUse32bitAbi\nprivate final  boolean mExtractNativeLibs\nprivate final  boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final  boolean mOverlayIsStatic\nprivate final  int mOverlayPriority\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyName\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyValue\nprivate final  int mRollbackDataPolicy\npublic  long getLongVersionCode()\nprivate  boolean hasAnyRequiredSplitTypes()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
    @Deprecated
    private void __metadata() {}

+12 −10
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.content.pm.parsing;

import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
import static android.content.pm.parsing.ParsingPackageUtils.PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY;
import static android.content.pm.parsing.ParsingPackageUtils.checkRequiredSystemProperties;
import static android.content.pm.parsing.ParsingPackageUtils.parsePublicKey;
import static android.content.pm.parsing.ParsingPackageUtils.validateName;
@@ -332,7 +333,7 @@ public class ApkLiteParseUtils {
                signingDetails = SigningDetails.UNKNOWN;
            }

            return parseApkLite(input, apkPath, parser, signingDetails);
            return parseApkLite(input, apkPath, parser, signingDetails, flags);
        } catch (XmlPullParserException | IOException | RuntimeException e) {
            Slog.w(TAG, "Failed to parse " + apkPath, e);
            return input.error(PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
@@ -350,7 +351,7 @@ public class ApkLiteParseUtils {
    }

    private static ParseResult<ApkLite> parseApkLite(ParseInput input, String codePath,
            XmlResourceParser parser, SigningDetails signingDetails)
            XmlResourceParser parser, SigningDetails signingDetails, int flags)
            throws IOException, XmlPullParserException {
        ParseResult<Pair<String, String>> result = parsePackageSplitNames(input, parser);
        if (result.isError()) {
@@ -522,14 +523,14 @@ public class ApkLiteParseUtils {
        }

        // Check to see if overlay should be excluded based on system property condition
        if (!checkRequiredSystemProperties(requiredSystemPropertyName,
                requiredSystemPropertyValue)) {
            Slog.i(TAG, "Skipping target and overlay pair " + targetPackage + " and "
        if ((flags & PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY) == 0
                && !checkRequiredSystemProperties(
                        requiredSystemPropertyName, requiredSystemPropertyValue)) {
            String message = "Skipping target and overlay pair " + targetPackage + " and "
                    + codePath + ": overlay ignored due to required system property: "
                    + requiredSystemPropertyName + " with value: " + requiredSystemPropertyValue);
            targetPackage = null;
            overlayIsStatic = false;
            overlayPriority = 0;
                    + requiredSystemPropertyName + " with value: " + requiredSystemPropertyValue;
            Slog.i(TAG, message);
            return input.skip(message);
        }

        return input.success(
@@ -538,7 +539,8 @@ public class ApkLiteParseUtils {
                        versionCodeMajor, revisionCode, installLocation, verifiers, signingDetails,
                        coreApp, debuggable, profilableByShell, multiArch, use32bitAbi,
                        useEmbeddedDex, extractNativeLibs, isolatedSplits, targetPackage,
                        overlayIsStatic, overlayPriority, minSdkVersion, targetSdkVersion,
                        overlayIsStatic, overlayPriority, requiredSystemPropertyName,
                        requiredSystemPropertyValue, minSdkVersion, targetSdkVersion,
                        rollbackDataPolicy, requiredSplitTypes.first, requiredSplitTypes.second));
    }

+6 −0
Original line number Diff line number Diff line
@@ -217,6 +217,11 @@ public class ParsingPackageUtils {
    public static final int PARSE_IS_SYSTEM_DIR = 1 << 4;
    public static final int PARSE_COLLECT_CERTIFICATES = 1 << 5;
    public static final int PARSE_ENFORCE_CODE = 1 << 6;
    /**
     * This flag is applied in the ApkLiteParser. Used by OverlayConfigParser to ignore the
     * checks of required system property within the overlay tag.
     */
    public static final int PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY = 1 << 7;
    public static final int PARSE_CHATTY = 1 << 31;

    @IntDef(flag = true, prefix = { "PARSE_" }, value = {
@@ -227,6 +232,7 @@ public class ParsingPackageUtils {
            PARSE_IGNORE_PROCESSES,
            PARSE_IS_SYSTEM_DIR,
            PARSE_MUST_BE_APK,
            PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface ParseFlags {}
+12 −12
Original line number Diff line number Diff line
@@ -35,6 +35,8 @@ import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Supplier;

@@ -116,14 +118,16 @@ public class OverlayConfig {
        }

        boolean foundConfigFile = false;
        ArrayList<ParsedOverlayInfo> packageManagerOverlayInfos = null;
        final Map<String, ParsedOverlayInfo> packageManagerOverlayInfos =
                packageProvider == null ? null : getOverlayPackageInfos(packageProvider);

        final ArrayList<ParsedConfiguration> overlays = new ArrayList<>();
        for (int i = 0, n = partitions.size(); i < n; i++) {
            final OverlayPartition partition = partitions.get(i);
            final OverlayScanner scanner = (scannerFactory == null) ? null : scannerFactory.get();
            final ArrayList<ParsedConfiguration> partitionOverlays =
                    OverlayConfigParser.getConfigurations(partition, scanner);
                    OverlayConfigParser.getConfigurations(partition, scanner,
                            packageManagerOverlayInfos);
            if (partitionOverlays != null) {
                foundConfigFile = true;
                overlays.addAll(partitionOverlays);
@@ -138,12 +142,8 @@ public class OverlayConfig {
            if (scannerFactory != null) {
                partitionOverlayInfos = new ArrayList<>(scanner.getAllParsedInfos());
            } else {
                if (packageManagerOverlayInfos == null) {
                    packageManagerOverlayInfos = getOverlayPackageInfos(packageProvider);
                }

                // Filter out overlays not present in the partition.
                partitionOverlayInfos = new ArrayList<>(packageManagerOverlayInfos);
                partitionOverlayInfos = new ArrayList<>(packageManagerOverlayInfos.values());
                for (int j = partitionOverlayInfos.size() - 1; j >= 0; j--) {
                    if (!partition.containsFile(partitionOverlayInfos.get(j).path)) {
                        partitionOverlayInfos.remove(j);
@@ -289,14 +289,14 @@ public class OverlayConfig {
    }

    @NonNull
    private static ArrayList<ParsedOverlayInfo> getOverlayPackageInfos(
    private static Map<String, ParsedOverlayInfo> getOverlayPackageInfos(
            @NonNull PackageProvider packageManager) {
        final ArrayList<ParsedOverlayInfo> overlays = new ArrayList<>();
        final HashMap<String, ParsedOverlayInfo> overlays = new HashMap<>();
        packageManager.forEachPackage((ParsingPackageRead p, Boolean isSystem) -> {
            if (p.getOverlayTarget() != null && isSystem) {
                overlays.add(new ParsedOverlayInfo(p.getPackageName(), p.getOverlayTarget(),
                        p.getTargetSdkVersion(), p.isOverlayIsStatic(), p.getOverlayPriority(),
                        new File(p.getBaseApkPath())));
                overlays.put(p.getPackageName(), new ParsedOverlayInfo(p.getPackageName(),
                        p.getOverlayTarget(), p.getTargetSdkVersion(), p.isOverlayIsStatic(),
                        p.getOverlayPriority(), new File(p.getBaseApkPath())));
            }
        });
        return overlays;
+36 −9
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import android.util.Log;
import android.util.Xml;

import com.android.internal.content.om.OverlayScanner.ParsedOverlayInfo;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;

import libcore.io.IoUtils;
@@ -40,6 +41,7 @@ import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Map;

/**
 * Responsible for parsing configurations of Runtime Resource Overlays that control mutability,
@@ -192,7 +194,8 @@ final class OverlayConfigParser {
     */
    @Nullable
    static ArrayList<ParsedConfiguration> getConfigurations(
            @NonNull OverlayPartition partition, @Nullable OverlayScanner scanner) {
            @NonNull OverlayPartition partition, @Nullable OverlayScanner scanner,
            @Nullable Map<String, ParsedOverlayInfo> packageManagerOverlayInfos) {
        if (partition.getOverlayFolder() == null) {
            return null;
        }
@@ -207,11 +210,12 @@ final class OverlayConfigParser {
        }

        final ParsingContext parsingContext = new ParsingContext(partition);
        readConfigFile(configFile, scanner, parsingContext);
        readConfigFile(configFile, scanner, packageManagerOverlayInfos, parsingContext);
        return parsingContext.mOrderedConfigurations;
    }

    private static void readConfigFile(@NonNull File configFile, @Nullable OverlayScanner scanner,
            @Nullable Map<String, ParsedOverlayInfo> packageManagerOverlayInfos,
            @NonNull ParsingContext parsingContext) {
        FileReader configReader;
        try {
@@ -231,10 +235,12 @@ final class OverlayConfigParser {
                final String name = parser.getName();
                switch (name) {
                    case "merge":
                        parseMerge(configFile, parser, scanner, parsingContext);
                        parseMerge(configFile, parser, scanner, packageManagerOverlayInfos,
                                parsingContext);
                        break;
                    case "overlay":
                        parseOverlay(configFile, parser, scanner, parsingContext);
                        parseOverlay(configFile, parser, scanner, packageManagerOverlayInfos,
                                parsingContext);
                        break;
                    default:
                        Log.w(TAG, String.format("Tag %s is unknown in %s at %s",
@@ -258,7 +264,9 @@ final class OverlayConfigParser {
     * configuration files.
     */
    private static void parseMerge(@NonNull File configFile, @NonNull XmlPullParser parser,
            @Nullable OverlayScanner scanner, @NonNull ParsingContext parsingContext) {
            @Nullable OverlayScanner scanner,
            @Nullable Map<String, ParsedOverlayInfo> packageManagerOverlayInfos,
            @NonNull ParsingContext parsingContext) {
        final String path = parser.getAttributeValue(null, "path");
        if (path == null) {
            throw new IllegalStateException(String.format("<merge> without path in %s at %s"
@@ -304,7 +312,7 @@ final class OverlayConfigParser {
                            parser.getPositionDescription()));
        }

        readConfigFile(includedConfigFile, scanner, parsingContext);
        readConfigFile(includedConfigFile, scanner, packageManagerOverlayInfos, parsingContext);
        parsingContext.mMergeDepth--;
    }

@@ -330,7 +338,12 @@ final class OverlayConfigParser {
     * order of non-configured overlays when enabled by the OverlayManagerService is undefined.
     */
    private static void parseOverlay(@NonNull File configFile, @NonNull XmlPullParser parser,
            @Nullable OverlayScanner scanner, @NonNull ParsingContext parsingContext) {
            @Nullable OverlayScanner scanner,
            @Nullable Map<String, ParsedOverlayInfo> packageManagerOverlayInfos,
            @NonNull ParsingContext parsingContext) {
        Preconditions.checkArgument((scanner == null) != (packageManagerOverlayInfos == null),
                "scanner and packageManagerOverlayInfos cannot be both null or both non-null");

        final String packageName = parser.getAttributeValue(null, "package");
        if (packageName == null) {
            throw new IllegalStateException(String.format("\"<overlay> without package in %s at %s",
@@ -338,16 +351,30 @@ final class OverlayConfigParser {
        }

        // Ensure the overlay being configured is present in the partition during zygote
        // initialization.
        // initialization, unless the package is an excluded overlay package.
        ParsedOverlayInfo info = null;
        if (scanner != null) {
            info = scanner.getParsedInfo(packageName);
            if (info == null|| !parsingContext.mPartition.containsOverlay(info.path)) {
            if (info == null
                    && scanner.isExcludedOverlayPackage(packageName, parsingContext.mPartition)) {
                Log.d(TAG, "overlay " + packageName + " in partition "
                        + parsingContext.mPartition.getOverlayFolder() + " is ignored.");
                return;
            } else if (info == null || !parsingContext.mPartition.containsOverlay(info.path)) {
                throw new IllegalStateException(
                        String.format("overlay %s not present in partition %s in %s at %s",
                                packageName, parsingContext.mPartition.getOverlayFolder(),
                                configFile, parser.getPositionDescription()));
            }
        } else {
            // Zygote shall have crashed itself, if there's an overlay apk not present in the
            // partition. For the overlay package not found in the package manager, we can assume
            // that it's an excluded overlay package.
            if (packageManagerOverlayInfos.get(packageName) == null) {
                Log.d(TAG, "overlay " + packageName + " in partition "
                        + parsingContext.mPartition.getOverlayFolder() + " is ignored.");
                return;
            }
        }

        if (parsingContext.mConfiguredOverlays.contains(packageName)) {
Loading