OSDN Git Service

Always perform has code check
authorTodd Kennedy <toddke@google.com>
Fri, 15 Apr 2016 16:44:36 +0000 (09:44 -0700)
committerTodd Kennedy <toddke@google.com>
Tue, 19 Apr 2016 19:40:54 +0000 (12:40 -0700)
Move code checking from the package parser and into the block
where we implement policy.

Bug: 28132476
Change-Id: Ie5cacacbf80289ff8d85acc5b57e58ea7216859c

core/java/android/content/pm/PackageParser.java
services/core/java/com/android/server/pm/PackageManagerService.java

index 4108f6d..bb8bca4 100644 (file)
@@ -131,12 +131,6 @@ public class PackageParser {
     /** 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/";
 
@@ -1137,13 +1131,11 @@ public class PackageParser {
 
         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 {
@@ -1151,10 +1143,8 @@ public class PackageParser {
         }
     }
 
-    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.
@@ -1202,7 +1192,6 @@ public class PackageParser {
             }
         }
 
-        boolean codeFound = false;
         StrictJarFile jarFile = null;
         try {
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "strictJarFileCtor");
@@ -1226,10 +1215,6 @@ public class PackageParser {
 
             // 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;
             }
 
@@ -1249,19 +1234,11 @@ public class PackageParser {
                     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.
@@ -1349,7 +1326,7 @@ public class PackageParser {
                 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);
                 }
index 4819de5..950c3df 100644 (file)
@@ -216,6 +216,7 @@ import android.util.SparseArray;
 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;
@@ -7643,6 +7644,52 @@ public class PackageManagerService extends IPackageManager.Stub {
         }
     }
 
+    /**
+     * 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 {
@@ -7687,6 +7734,10 @@ public class PackageManagerService extends IPackageManager.Stub {
             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);