Mark resource-only splits as hasCode=false.

PackageManagerService now skips dexopt for split APKs that don't
declare they have code.  Also surface more detailed error messages
in logs.

Bug: 14975160
Change-Id: Ie6078dba724815020cee59b7fc52317e88ca097a
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index d41cca6..43c2b15 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -846,7 +846,7 @@
             throw e;
         } catch (Exception e) {
             throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
-                    "Unable to read AndroidManifest.xml of " + apkPath);
+                    "Failed to read manifest from " + apkPath, e);
         } finally {
             IoUtils.closeQuietly(parser);
             IoUtils.closeQuietly(assets);
@@ -895,7 +895,7 @@
             throw e;
         } catch (Exception e) {
             throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
-                    "Unable to read AndroidManifest.xml of " + apkPath);
+                    "Failed to read manifest from " + apkPath, e);
         } finally {
             IoUtils.closeQuietly(parser);
             IoUtils.closeQuietly(assets);
@@ -910,9 +910,13 @@
      * omitted here.
      */
     private Package parseSplitApk(Package pkg, Resources res, XmlResourceParser parser, int flags,
-            int splitIndex, String[] outError) throws XmlPullParserException, IOException {
+            int splitIndex, String[] outError) throws XmlPullParserException, IOException,
+            PackageParserException {
         AttributeSet attrs = parser;
 
+        // We parsed manifest tag earlier; just skip past it
+        parsePackageSplitNames(parser, attrs, flags);
+
         mParseInstrumentationArgs = null;
         mParseActivityArgs = null;
         mParseServiceArgs = null;
diff --git a/core/java/android/util/ExceptionUtils.java b/core/java/android/util/ExceptionUtils.java
index 6aae84d..f5d515d 100644
--- a/core/java/android/util/ExceptionUtils.java
+++ b/core/java/android/util/ExceptionUtils.java
@@ -39,4 +39,20 @@
             throw new IOException(e.getMessage().substring(PREFIX_IO.length()));
         }
     }
+
+    public static String getCompleteMessage(String msg, Throwable t) {
+        final StringBuilder builder = new StringBuilder();
+        if (msg != null) {
+            builder.append(msg).append(": ");
+        }
+        builder.append(t.getMessage());
+        while ((t = t.getCause()) != null) {
+            builder.append(": ").append(t.getMessage());
+        }
+        return builder.toString();
+    }
+
+    public static String getCompleteMessage(Throwable t) {
+        return getCompleteMessage(null, t);
+    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e17dacb..a8732dd 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -165,6 +165,7 @@
 import android.util.AtomicFile;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
+import android.util.ExceptionUtils;
 import android.util.Log;
 import android.util.LogPrinter;
 import android.util.PrintStreamPrinter;
@@ -9855,6 +9856,18 @@
             Slog.w(TAG, msg);
         }
 
+        public void setError(String msg, PackageParserException e) {
+            returnCode = e.error;
+            returnMsg = ExceptionUtils.getCompleteMessage(msg, e);
+            Slog.w(TAG, msg, e);
+        }
+
+        public void setError(String msg, PackageManagerException e) {
+            returnCode = e.error;
+            returnMsg = ExceptionUtils.getCompleteMessage(msg, e);
+            Slog.w(TAG, msg, e);
+        }
+
         // In some error cases we want to convey more info back to the observer
         String origPackage;
         String origPermission;
@@ -9908,8 +9921,7 @@
             }
 
         } catch (PackageManagerException e) {
-            res.setError(e.error,
-                    "Package couldn't be installed in " + pkg.codePath + ": " + e.getMessage());
+            res.setError("Package couldn't be installed in " + pkg.codePath, e);
         }
     }
 
@@ -10007,8 +10019,7 @@
                 updateSettingsLI(newPackage, installerPackageName, allUsers, perUserInstalled, res);
                 updatedSettings = true;
             } catch (PackageManagerException e) {
-                res.setError(e.error,
-                        "Package couldn't be installed in " + pkg.codePath + ": " + e.getMessage());
+                res.setError("Package couldn't be installed in " + pkg.codePath, e);
             }
         }
 
@@ -10139,8 +10150,7 @@
             }
 
         } catch (PackageManagerException e) {
-            res.setError(e.error,
-                    "Package couldn't be installed in " + pkg.codePath + ": " + e.getMessage());
+            res.setError("Package couldn't be installed in " + pkg.codePath, e);
         }
 
         if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
@@ -10278,7 +10288,7 @@
         try {
             pkg = pp.parsePackage(tmpPackageFile, parseFlags);
         } catch (PackageParserException e) {
-            res.setError(e.error, "Failed parse during installPackageLI: " + e.getMessage());
+            res.setError("Failed parse during installPackageLI", e);
             return;
         }
 
@@ -10294,7 +10304,7 @@
             pp.collectCertificates(pkg, parseFlags);
             pp.collectManifestDigest(pkg);
         } catch (PackageParserException e) {
-            res.setError(e.error, "Failed collect during installPackageLI: " + e.getMessage());
+            res.setError("Failed collect during installPackageLI", e);
             return;
         }
 
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 963c796..4f1d15e 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -931,6 +931,13 @@
 
     // Build an empty <application> tag (required).
     sp<XMLNode> app = XMLNode::newElement(filename, String16(), String16("application"));
+
+    // Add the 'hasCode' attribute which is never true for resource splits.
+    if (!addTagAttribute(app, RESOURCES_ANDROID_NAMESPACE, "hasCode",
+            "false", true, true)) {
+        return UNKNOWN_ERROR;
+    }
+
     manifest->addChild(app);
     root->addChild(manifest);