Loading core/java/android/content/pm/PackageParser.java +28 −5 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(), Loading Loading @@ -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 Loading core/java/android/content/pm/dex/DexMetadataHelper.java +20 −21 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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 { Loading @@ -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); Loading @@ -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; Loading @@ -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); } Loading @@ -276,6 +274,7 @@ public class DexMetadataHelper { Log.v(TAG, "validateDexMetadataManifest: " + dmaPath + ", " + packageName + ", " + versionCode + ": successful"); } return input.success(null); } /** Loading core/java/android/content/pm/parsing/ParsingPackageUtils.java +72 −71 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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 Loading @@ -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 { Loading @@ -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) { Loading Loading @@ -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); } Loading @@ -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()) { Loading Loading @@ -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; Loading @@ -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, Loading Loading @@ -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); } Loading Loading @@ -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; Loading Loading @@ -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( Loading @@ -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(); Loading @@ -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); } Loading @@ -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"); } Loading core/java/android/content/pm/split/DefaultSplitAssetLoader.java +5 −11 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; } Loading Loading @@ -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(); } Loading core/java/android/content/pm/split/SplitAssetDependencyLoader.java +7 −13 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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); } } Loading @@ -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. Loading @@ -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 Loading
core/java/android/content/pm/PackageParser.java +28 −5 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(), Loading Loading @@ -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 Loading
core/java/android/content/pm/dex/DexMetadataHelper.java +20 −21 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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 { Loading @@ -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); Loading @@ -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; Loading @@ -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); } Loading @@ -276,6 +274,7 @@ public class DexMetadataHelper { Log.v(TAG, "validateDexMetadataManifest: " + dmaPath + ", " + packageName + ", " + versionCode + ": successful"); } return input.success(null); } /** Loading
core/java/android/content/pm/parsing/ParsingPackageUtils.java +72 −71 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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 Loading @@ -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 { Loading @@ -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) { Loading Loading @@ -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); } Loading @@ -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()) { Loading Loading @@ -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; Loading @@ -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, Loading Loading @@ -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); } Loading Loading @@ -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; Loading Loading @@ -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( Loading @@ -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(); Loading @@ -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); } Loading @@ -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"); } Loading
core/java/android/content/pm/split/DefaultSplitAssetLoader.java +5 −11 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; } Loading Loading @@ -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(); } Loading
core/java/android/content/pm/split/SplitAssetDependencyLoader.java +7 −13 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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); } } Loading @@ -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. Loading @@ -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