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

Commit 1ce663dd authored by Jackal Guo's avatar Jackal Guo Committed by Android (Google) Code Review
Browse files

Merge "Refactor PackageParser (12/n)"

parents 38cbfbb8 2821e5d4
Loading
Loading
Loading
Loading
+28 −5
Original line number Diff line number Diff line
@@ -55,7 +55,8 @@ import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.overlay.OverlayPaths;
import android.content.pm.split.SplitAssetLoader;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
import android.content.res.ApkAssets;
import android.content.res.AssetManager;
import android.content.res.Configuration;
@@ -1403,20 +1404,26 @@ public class PackageParser {
            // must use v2 signing scheme
            minSignatureScheme = SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2;
        }
        final android.content.pm.SigningDetails verified;
        final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
        final ParseResult<android.content.pm.SigningDetails> result;
        if (skipVerify) {
            // systemDir APKs are already trusted, save time by not verifying; since the signature
            // is not verified and some system apps can have their V2+ signatures stripped allow
            // pulling the certs from the jar signature.
            verified = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(
                        apkPath, SigningDetails.SignatureSchemeVersion.JAR);
            result = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(
                    input, apkPath, SigningDetails.SignatureSchemeVersion.JAR);
        } else {
            verified = ApkSignatureVerifier.verify(apkPath, minSignatureScheme);
            result = ApkSignatureVerifier.verify(input, apkPath, minSignatureScheme);
        }
        if (result.isError()) {
            throw new PackageParserException(result.getErrorCode(), result.getErrorMessage(),
                    result.getException());
        }

        // Verify that entries are signed consistently with the first pkg
        // we encountered. Note that for splits, certificates may have
        // already been populated during an earlier parse of a base APK.
        final android.content.pm.SigningDetails verified = result.getResult();
        if (pkg.mSigningDetails == SigningDetails.UNKNOWN) {
            pkg.mSigningDetails = new SigningDetails(verified.getSignatures(),
                    verified.getSignatureSchemeVersion(),
@@ -8693,6 +8700,22 @@ public class PackageParser {
    // have two branches of methods in SplitAsset related classes so we can keep real classes
    // clean and move all the legacy code to one place.

    /**
     * Simple interface for loading base Assets and Splits. Used by PackageParser when parsing
     * split APKs.
     *
     * @hide
     * @deprecated Do not use. New changes should use
     * {@link android.content.pm.split.SplitAssetLoader} instead.
     */
    @Deprecated
    private interface SplitAssetLoader extends AutoCloseable {
        AssetManager getBaseAssetManager() throws PackageParserException;
        AssetManager getSplitAssetManager(int splitIdx) throws PackageParserException;

        ApkAssets getBaseApkAssets();
    }

    /**
     * A helper class that implements the dependency tree traversal for splits. Callbacks
     * are implemented by subclasses to notify whether a split has already been constructed
+20 −21
Original line number Diff line number Diff line
@@ -19,9 +19,10 @@ package android.content.pm.dex;
import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_DEX_METADATA;
import static android.content.pm.parsing.ApkLiteParseUtils.APK_FILE_EXTENSION;

import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.content.pm.parsing.PackageLite;
import android.content.pm.parsing.result.ParseInput;
import android.content.pm.parsing.result.ParseResult;
import android.os.SystemProperties;
import android.util.ArrayMap;
import android.util.JsonReader;
@@ -176,18 +177,16 @@ public class DexMetadataHelper {
     * Validate that the given file is a dex metadata archive.
     * This is just a validation that the file is a zip archive that contains a manifest.json
     * with the package name and version code.
     *
     * @throws PackageParserException if the file is not a .dm file.
     */
    public static void validateDexMetadataFile(String dmaPath, String packageName, long versionCode)
            throws PackageParserException {
        validateDexMetadataFile(dmaPath, packageName, versionCode,
    public static ParseResult validateDexMetadataFile(ParseInput input, String dmaPath,
            String packageName, long versionCode) {
        return validateDexMetadataFile(input, dmaPath, packageName, versionCode,
               SystemProperties.getBoolean(PROPERTY_DM_JSON_MANIFEST_REQUIRED, false));
    }

    @VisibleForTesting
    public static void validateDexMetadataFile(String dmaPath, String packageName, long versionCode,
            boolean requireManifest) throws PackageParserException {
    public static ParseResult validateDexMetadataFile(ParseInput input, String dmaPath,
            String packageName, long versionCode, boolean requireManifest) {
        StrictJarFile jarFile = null;

        if (DEBUG) {
@@ -197,11 +196,10 @@ public class DexMetadataHelper {

        try {
            jarFile = new StrictJarFile(dmaPath, false, false);
            validateDexMetadataManifest(dmaPath, jarFile, packageName, versionCode,
            return validateDexMetadataManifest(input, dmaPath, jarFile, packageName, versionCode,
                    requireManifest);
        } catch (IOException e) {
            throw new PackageParserException(INSTALL_FAILED_BAD_DEX_METADATA,
                    "Error opening " + dmaPath, e);
            return input.error(INSTALL_FAILED_BAD_DEX_METADATA, "Error opening " + dmaPath, e);
        } finally {
            if (jarFile != null) {
                try {
@@ -213,20 +211,20 @@ public class DexMetadataHelper {
    }

    /** Ensure that packageName and versionCode match the manifest.json in the .dm file */
    private static void validateDexMetadataManifest(String dmaPath, StrictJarFile jarFile,
            String packageName, long versionCode, boolean requireManifest)
            throws IOException, PackageParserException {
    private static ParseResult validateDexMetadataManifest(ParseInput input, String dmaPath,
            StrictJarFile jarFile, String packageName, long versionCode, boolean requireManifest)
            throws IOException {
        if (!requireManifest) {
            if (DEBUG) {
                Log.v(TAG, "validateDexMetadataManifest: " + dmaPath
                        + " manifest.json check skipped");
            }
            return;
            return input.success(null);
        }

        ZipEntry zipEntry = jarFile.findEntry("manifest.json");
        if (zipEntry == null) {
              throw new PackageParserException(INSTALL_FAILED_BAD_DEX_METADATA,
            return input.error(INSTALL_FAILED_BAD_DEX_METADATA,
                    "Missing manifest.json in " + dmaPath);
        }
        InputStream inputStream = jarFile.getInputStream(zipEntry);
@@ -235,7 +233,7 @@ public class DexMetadataHelper {
        try {
          reader = new JsonReader(new InputStreamReader(inputStream, "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            throw new PackageParserException(INSTALL_FAILED_BAD_DEX_METADATA,
            return input.error(INSTALL_FAILED_BAD_DEX_METADATA,
                    "Error opening manifest.json in " + dmaPath, e);
        }
        String jsonPackageName = null;
@@ -255,19 +253,19 @@ public class DexMetadataHelper {
        reader.endObject();

        if (jsonPackageName == null || jsonVersionCode == -1) {
            throw new PackageParserException(INSTALL_FAILED_BAD_DEX_METADATA,
            return input.error(INSTALL_FAILED_BAD_DEX_METADATA,
                    "manifest.json in " + dmaPath
                    + " is missing 'packageName' and/or 'versionCode'");
        }

        if (!jsonPackageName.equals(packageName)) {
            throw new PackageParserException(INSTALL_FAILED_BAD_DEX_METADATA,
            return input.error(INSTALL_FAILED_BAD_DEX_METADATA,
                    "manifest.json in " + dmaPath + " has invalid packageName: " + jsonPackageName
                    + ", expected: " + packageName);
        }

        if (versionCode != jsonVersionCode) {
            throw new PackageParserException(INSTALL_FAILED_BAD_DEX_METADATA,
            return input.error(INSTALL_FAILED_BAD_DEX_METADATA,
                    "manifest.json in " + dmaPath + " has invalid versionCode: " + jsonVersionCode
                    + ", expected: " + versionCode);
        }
@@ -276,6 +274,7 @@ public class DexMetadataHelper {
            Log.v(TAG, "validateDexMetadataManifest: " + dmaPath + ", " + packageName +
                    ", " + versionCode + ": successful");
        }
        return input.success(null);
    }

    /**
+72 −71
Original line number Diff line number Diff line
@@ -18,8 +18,10 @@ package android.content.pm.parsing;

import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
@@ -46,7 +48,6 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.Property;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.Signature;
import android.content.pm.SigningDetails;
import android.content.pm.parsing.component.ComponentParseUtils;
@@ -274,31 +275,25 @@ public class ParsingPackageUtils {
                                manifestArray);
                    }
                });
        try {
        result = parser.parsePackage(input, file, parseFlags);
        if (result.isError()) {
                return result;
            }
        } catch (PackageParser.PackageParserException e) {
            return input.error(PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                    "Error parsing package", e);
            return input.error(result);
        }

        try {
            ParsingPackage pkg = result.getResult();
        final ParsingPackage pkg = result.getResult();
        if (collectCertificates) {
                pkg.setSigningDetails(
                        ParsingPackageUtils.getSigningDetails(pkg, false /* skipVerify */));
            final ParseResult<SigningDetails> ret =
                    ParsingPackageUtils.getSigningDetails(input, pkg, false /*skipVerify*/);
            if (ret.isError()) {
                return input.error(ret);
            }
            pkg.setSigningDetails(ret.getResult());
        }

        // Need to call this to finish the parsing stage
        pkg.hideAsParsed();

        return input.success(pkg);
        } catch (PackageParser.PackageParserException e) {
            return input.error(PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                    "Error collecting package certificates", e);
        }
    }

    private boolean mOnlyCoreApps;
@@ -328,8 +323,8 @@ public class ParsingPackageUtils {
     * requiring identical package name and version codes, a single base APK,
     * and unique split names.
     * <p>
     * Note that this <em>does not</em> perform signature verification; that
     * must be done separately in {@link #getSigningDetails(ParsingPackageRead, boolean)}.
     * Note that this <em>does not</em> perform signature verification; that must
     * be done separately in {@link #getSigningDetails(ParseInput, ParsingPackageRead, boolean)}.
     *
     * If {@code useCaches} is true, the package parser might return a cached
     * result from a previous parse of the same {@code packageFile} with the same
@@ -338,9 +333,7 @@ public class ParsingPackageUtils {
     *
     * @see PackageParser#parsePackageLite(File, int)
     */
    public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile,
            int flags)
            throws PackageParserException {
    public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile, int flags) {
        if (packageFile.isDirectory()) {
            return parseClusterPackage(input, packageFile, flags);
        } else {
@@ -354,8 +347,8 @@ public class ParsingPackageUtils {
     * identical package name and version codes, a single base APK, and unique
     * split names.
     * <p>
     * Note that this <em>does not</em> perform signature verification; that
     * must be done separately in {@link #getSigningDetails(ParsingPackageRead, boolean)}.
     * Note that this <em>does not</em> perform signature verification; that must
     * be done separately in {@link #getSigningDetails(ParseInput, ParsingPackageRead, boolean)}.
     */
    private ParseResult<ParsingPackage> parseClusterPackage(ParseInput input, File packageDir,
            int flags) {
@@ -405,15 +398,19 @@ public class ParsingPackageUtils {

                for (int i = 0; i < num; i++) {
                    final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);
                    final ParseResult<ParsingPackage> split =
                            parseSplitApk(input, pkg, i, splitAssets, flags);
                    if (split.isError()) {
                        return input.error(split);
                    }
                }
            }

            pkg.setUse32BitAbi(lite.isUse32bitAbi());
            return input.success(pkg);
        } catch (PackageParserException e) {
            return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                    "Failed to load assets: " + lite.getBaseApkPath(), e);
        } catch (IllegalArgumentException e) {
            return input.error(e.getCause() instanceof IOException ? INSTALL_FAILED_INVALID_APK
                    : INSTALL_PARSE_FAILED_NOT_APK, e.getMessage(), e);
        } finally {
            IoUtils.closeQuietly(assetLoader);
        }
@@ -422,11 +419,11 @@ public class ParsingPackageUtils {
    /**
     * Parse the given APK file, treating it as as a single monolithic package.
     * <p>
     * Note that this <em>does not</em> perform signature verification; that
     * must be done separately in {@link #getSigningDetails(ParsingPackageRead, boolean)}.
     * Note that this <em>does not</em> perform signature verification; that must
     * be done separately in {@link #getSigningDetails(ParseInput, ParsingPackageRead, boolean)}.
     */
    private ParseResult<ParsingPackage> parseMonolithicPackage(ParseInput input, File apkFile,
            int flags) throws PackageParserException {
            int flags) {
        final ParseResult<PackageLite> liteResult =
                ApkLiteParseUtils.parseMonolithicPackageLite(input, apkFile, flags);
        if (liteResult.isError()) {
@@ -460,8 +457,7 @@ public class ParsingPackageUtils {
    }

    private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,
            String codePath, SplitAssetLoader assetLoader, int flags)
            throws PackageParserException {
            String codePath, SplitAssetLoader assetLoader, int flags) {
        final String apkPath = apkFile.getAbsolutePath();

        String volumeUuid = null;
@@ -472,7 +468,13 @@ public class ParsingPackageUtils {

        if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);

        final AssetManager assets = assetLoader.getBaseAssetManager();
        final AssetManager assets;
        try {
            assets = assetLoader.getBaseAssetManager();
        } catch (IllegalArgumentException e) {
            return input.error(e.getCause() instanceof IOException ? INSTALL_FAILED_INVALID_APK
                    : INSTALL_PARSE_FAILED_NOT_APK, e.getMessage(), e);
        }
        final int cookie = assets.findCookieForPath(apkPath);
        if (cookie == 0) {
            return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST,
@@ -529,7 +531,12 @@ public class ParsingPackageUtils {
            pkg.setVolumeUuid(volumeUuid);

            if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
                pkg.setSigningDetails(getSigningDetails(pkg, false));
                final ParseResult<SigningDetails> ret =
                        getSigningDetails(input, pkg, false /*skipVerify*/);
                if (ret.isError()) {
                    return input.error(ret);
                }
                pkg.setSigningDetails(ret.getResult());
            } else {
                pkg.setSigningDetails(SigningDetails.UNKNOWN);
            }
@@ -635,11 +642,13 @@ public class ParsingPackageUtils {
     */
    private ParseResult<ParsingPackage> parseSplitApk(ParseInput input, ParsingPackage pkg,
            Resources res, XmlResourceParser parser, int flags, int splitIndex)
            throws XmlPullParserException, IOException, PackageParserException {
        AttributeSet attrs = parser;

            throws XmlPullParserException, IOException {
        // We parsed manifest tag earlier; just skip past it
        PackageParser.parsePackageSplitNames(parser, attrs);
        final ParseResult<Pair<String, String>> packageSplitResult =
                ApkLiteParseUtils.parsePackageSplitNames(input, parser);
        if (packageSplitResult.isError()) {
            return input.error(packageSplitResult);
        }

        int type;

@@ -2961,12 +2970,10 @@ public class ParsingPackageUtils {
     *  construct a dummy ParseInput.
     */
    @CheckResult
    public static SigningDetails getSigningDetails(ParsingPackageRead pkg, boolean skipVerify)
            throws PackageParserException {
    public static ParseResult<SigningDetails> getSigningDetails(ParseInput input,
            ParsingPackageRead pkg, boolean skipVerify) {
        SigningDetails signingDetails = SigningDetails.UNKNOWN;

        ParseInput input = ParseTypeImpl.forDefaultParsing().reset();

        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
        try {
            ParseResult<SigningDetails> result = getSigningDetails(
@@ -2978,8 +2985,7 @@ public class ParsingPackageUtils {
                    pkg.getTargetSdkVersion()
            );
            if (result.isError()) {
                throw new PackageParser.PackageParserException(result.getErrorCode(),
                        result.getErrorMessage(), result.getException());
                return input.error(result);
            }

            signingDetails = result.getResult();
@@ -2996,15 +3002,11 @@ public class ParsingPackageUtils {
                            pkg.getTargetSdkVersion()
                    );
                    if (result.isError()) {
                        throw new PackageParser.PackageParserException(result.getErrorCode(),
                                result.getErrorMessage(), result.getException());
                        return input.error(result);
                    }


                    signingDetails = result.getResult();
                }
            }
            return signingDetails;
            return result;
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
@@ -3020,30 +3022,29 @@ public class ParsingPackageUtils {
            // must use v2 signing scheme
            minSignatureScheme = SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2;
        }
        SigningDetails verified;
        try {
        final ParseResult<SigningDetails> verified;
        if (skipVerify) {
            // systemDir APKs are already trusted, save time by not verifying; since the
            // signature is not verified and some system apps can have their V2+ signatures
            // stripped allow pulling the certs from the jar signature.
                verified = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(
                        baseCodePath, SigningDetails.SignatureSchemeVersion.JAR);
            verified = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(input, baseCodePath,
                    SigningDetails.SignatureSchemeVersion.JAR);
        } else {
                verified = ApkSignatureVerifier.verify(baseCodePath, minSignatureScheme);
            verified = ApkSignatureVerifier.verify(input, baseCodePath, minSignatureScheme);
        }
        } catch (PackageParserException e) {
            return input.error(PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                    "Failed collecting certificates for " + baseCodePath, e);

        if (verified.isError()) {
            return input.error(verified);
        }

        // Verify that entries are signed consistently with the first pkg
        // we encountered. Note that for splits, certificates may have
        // already been populated during an earlier parse of a base APK.
        if (existingSigningDetails == SigningDetails.UNKNOWN) {
            return input.success(verified);
            return verified;
        } else {
            if (!Signature.areExactMatch(existingSigningDetails.getSignatures(),
                    verified.getSignatures())) {
                    verified.getResult().getSignatures())) {
                return input.error(INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
                        baseCodePath + " has mismatched certificates");
            }
+5 −11
Original line number Diff line number Diff line
@@ -15,10 +15,6 @@
 */
package android.content.pm.split;

import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;

import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.content.pm.parsing.PackageLite;
import android.content.pm.parsing.ParsingPackageUtils;
@@ -52,23 +48,21 @@ public class DefaultSplitAssetLoader implements SplitAssetLoader {
    }

    private static ApkAssets loadApkAssets(String path, @ParseFlags int flags)
            throws PackageParserException {
            throws IllegalArgumentException {
        if ((flags & ParsingPackageUtils.PARSE_MUST_BE_APK) != 0
                && !ApkLiteParseUtils.isApkPath(path)) {
            throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
                    "Invalid package file: " + path);
            throw new IllegalArgumentException("Invalid package file: " + path);
        }

        try {
            return ApkAssets.loadFromPath(path);
        } catch (IOException e) {
            throw new PackageParserException(INSTALL_FAILED_INVALID_APK,
                    "Failed to load APK at path " + path, e);
            throw new IllegalArgumentException("Failed to load APK at path " + path, e);
        }
    }

    @Override
    public AssetManager getBaseAssetManager() throws PackageParserException {
    public AssetManager getBaseAssetManager() throws IllegalArgumentException {
        if (mCachedAssetManager != null) {
            return mCachedAssetManager;
        }
@@ -97,7 +91,7 @@ public class DefaultSplitAssetLoader implements SplitAssetLoader {
    }

    @Override
    public AssetManager getSplitAssetManager(int splitIdx) throws PackageParserException {
    public AssetManager getSplitAssetManager(int splitIdx) throws IllegalArgumentException {
        return getBaseAssetManager();
    }

+7 −13
Original line number Diff line number Diff line
@@ -15,11 +15,7 @@
 */
package android.content.pm.split;

import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;

import android.annotation.NonNull;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.content.pm.parsing.PackageLite;
import android.content.pm.parsing.ParsingPackageUtils;
@@ -40,7 +36,7 @@ import java.util.Collections;
 * is to be used when an application opts-in to isolated split loading.
 * @hide
 */
public class SplitAssetDependencyLoader extends SplitDependencyLoader<PackageParserException>
public class SplitAssetDependencyLoader extends SplitDependencyLoader<IllegalArgumentException>
        implements SplitAssetLoader {
    private final String[] mSplitPaths;
    private final @ParseFlags int mFlags;
@@ -67,18 +63,16 @@ public class SplitAssetDependencyLoader extends SplitDependencyLoader<PackagePar
    }

    private static ApkAssets loadApkAssets(String path, @ParseFlags int flags)
            throws PackageParserException {
            throws IllegalArgumentException {
        if ((flags & ParsingPackageUtils.PARSE_MUST_BE_APK) != 0
                && !ApkLiteParseUtils.isApkPath(path)) {
            throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
                    "Invalid package file: " + path);
            throw new IllegalArgumentException("Invalid package file: " + path);
        }

        try {
            return ApkAssets.loadFromPath(path);
        } catch (IOException e) {
            throw new PackageParserException(PackageManager.INSTALL_FAILED_INVALID_APK,
                    "Failed to load APK at path " + path, e);
            throw new IllegalArgumentException("Failed to load APK at path " + path, e);
        }
    }

@@ -92,7 +86,7 @@ public class SplitAssetDependencyLoader extends SplitDependencyLoader<PackagePar

    @Override
    protected void constructSplit(int splitIdx, @NonNull int[] configSplitIndices,
            int parentSplitIdx) throws PackageParserException {
            int parentSplitIdx) throws IllegalArgumentException {
        final ArrayList<ApkAssets> assets = new ArrayList<>();

        // Include parent ApkAssets.
@@ -114,13 +108,13 @@ public class SplitAssetDependencyLoader extends SplitDependencyLoader<PackagePar
    }

    @Override
    public AssetManager getBaseAssetManager() throws PackageParserException {
    public AssetManager getBaseAssetManager() throws IllegalArgumentException {
        loadDependenciesForSplit(0);
        return mCachedAssetManagers[0];
    }

    @Override
    public AssetManager getSplitAssetManager(int idx) throws PackageParserException {
    public AssetManager getSplitAssetManager(int idx) throws IllegalArgumentException {
        // Since we insert the base at position 0, and PackageParser keeps splits separate from
        // the base, we need to adjust the index.
        loadDependenciesForSplit(idx + 1);
Loading