/** File name in an APK for the Android manifest. */
private static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
- /**
- * File name in an APK for bytecode. There may be additional bytecode files
- * but this one is always required for an APK that has code.
- */
- private static final String BYTECODE_FILENAME = "classes.dex";
-
/** Path prefix for apps on expanded storage */
private static final String MNT_EXPAND = "/mnt/expand/";
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
try {
- collectCertificates(
- pkg, new File(pkg.baseCodePath), pkg.applicationInfo.flags, parseFlags);
+ collectCertificates(pkg, new File(pkg.baseCodePath), parseFlags);
if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
for (int i = 0; i < pkg.splitCodePaths.length; i++) {
- collectCertificates(
- pkg, new File(pkg.splitCodePaths[i]), pkg.splitFlags[i], parseFlags);
+ collectCertificates(pkg, new File(pkg.splitCodePaths[i]), parseFlags);
}
}
} finally {
}
}
- private static void collectCertificates(Package pkg, File apkFile, int apkFlags, int parseFlags)
+ private static void collectCertificates(Package pkg, File apkFile, int parseFlags)
throws PackageParserException {
- final boolean hasCode = (apkFlags & ApplicationInfo.FLAG_HAS_CODE) != 0;
- final boolean requireCode = ((parseFlags & PARSE_ENFORCE_CODE) != 0) && hasCode;
final String apkPath = apkFile.getAbsolutePath();
// Try to verify the APK using APK Signature Scheme v2.
}
}
- boolean codeFound = false;
StrictJarFile jarFile = null;
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "strictJarFileCtor");
// Optimization: early termination when APK already verified
if (verified) {
- if ((requireCode) && (jarFile.findEntry(BYTECODE_FILENAME) == null)) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "Package " + apkPath + " code is missing");
- }
return;
}
final String entryName = entry.getName();
if (entryName.startsWith("META-INF/")) continue;
if (entryName.equals(ANDROID_MANIFEST_FILENAME)) continue;
- if (entryName.equals(BYTECODE_FILENAME)) {
- codeFound = true;
- }
toVerify.add(entry);
}
}
- if (!codeFound && requireCode) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "Package " + apkPath + " code is missing");
- }
-
// Verify that entries are signed consistently with the first entry
// we encountered. Note that for splits, certificates may have
// already been populated during an earlier parse of a base APK.
final Package tempPkg = new Package(null);
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
try {
- collectCertificates(tempPkg, apkFile, 0 /*apkFlags*/, 0 /*flags*/);
+ collectCertificates(tempPkg, apkFile, 0 /*parseFlags*/);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.Xml;
+import android.util.jar.StrictJarFile;
import android.view.Display;
import com.android.internal.R;
}
}
+ /**
+ * Returns {@code true} if the given file contains code. Otherwise {@code false}.
+ */
+ private static boolean apkHasCode(String fileName) {
+ StrictJarFile jarFile = null;
+ try {
+ jarFile = new StrictJarFile(fileName,
+ false /*verify*/, false /*signatureSchemeRollbackProtectionsEnforced*/);
+ return jarFile.findEntry("classes.dex") != null;
+ } catch (IOException ignore) {
+ } finally {
+ try {
+ jarFile.close();
+ } catch (IOException ignore) {}
+ }
+ return false;
+ }
+
+ /**
+ * Enforces code policy for the package. This ensures that if an APK has
+ * declared hasCode="true" in its manifest that the APK actually contains
+ * code.
+ *
+ * @throws PackageManagerException If bytecode could not be found when it should exist
+ */
+ private static void enforceCodePolicy(PackageParser.Package pkg)
+ throws PackageManagerException {
+ final boolean shouldHaveCode =
+ (pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0;
+ if (shouldHaveCode && !apkHasCode(pkg.baseCodePath)) {
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+ "Package " + pkg.baseCodePath + " code is missing");
+ }
+
+ if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
+ for (int i = 0; i < pkg.splitCodePaths.length; i++) {
+ final boolean splitShouldHaveCode =
+ (pkg.splitFlags[i] & ApplicationInfo.FLAG_HAS_CODE) != 0;
+ if (splitShouldHaveCode && !apkHasCode(pkg.splitCodePaths[i])) {
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+ "Package " + pkg.splitCodePaths[i] + " code is missing");
+ }
+ }
+ }
+ }
+
private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg,
final int policyFlags, final int scanFlags, long currentTime, UserHandle user)
throws PackageManagerException {
pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
}
+ if ((policyFlags & PackageParser.PARSE_ENFORCE_CODE) != 0) {
+ enforceCodePolicy(pkg);
+ }
+
if (mCustomResolverComponentName != null &&
mCustomResolverComponentName.getPackageName().equals(pkg.packageName)) {
setUpCustomResolverActivity(pkg);