Pass class loader down to openDexFiles if possible

App image needs the class loader when it gets loaded.

Bug: 22858531
Change-Id: I315919d91822db0c73cf16b21d660d5870d5746f
diff --git a/dalvik/src/main/java/dalvik/system/DexFile.java b/dalvik/src/main/java/dalvik/system/DexFile.java
index d176b30..5620851 100644
--- a/dalvik/src/main/java/dalvik/system/DexFile.java
+++ b/dalvik/src/main/java/dalvik/system/DexFile.java
@@ -63,6 +63,17 @@
     public DexFile(File file) throws IOException {
         this(file.getPath());
     }
+    /*
+     * Private version with class loader argument.
+     *
+     * @param file
+     *            the File object referencing the actual DEX file
+     * @param loader
+     *            the class loader object creating the DEX file object
+     */
+    DexFile(File file, ClassLoader loader) throws IOException {
+        this(file.getPath(), loader);
+    }
 
     /**
      * Opens a DEX file from a given filename. This will usually be a ZIP/JAR
@@ -82,7 +93,19 @@
      *             access rights missing for opening it
      */
     public DexFile(String fileName) throws IOException {
-        mCookie = openDexFile(fileName, null, 0);
+        this(fileName, null);
+    }
+
+    /*
+     * Private version with class loader argument.
+     *
+     * @param fileName
+     *            the filename of the DEX file
+     * @param loader
+     *            the class loader creating the DEX file object
+     */
+    DexFile(String fileName, ClassLoader loader) throws IOException {
+        mCookie = openDexFile(fileName, null, 0, loader);
         mInternalCookie = mCookie;
         mFileName = fileName;
         guard.open("close");
@@ -99,8 +122,11 @@
      *  File that will hold the optimized form of the DEX data.
      * @param flags
      *  Enable optional features.
+     * @param loader
+     *  The class loader creating the DEX file object.
      */
-    private DexFile(String sourceName, String outputName, int flags) throws IOException {
+    private DexFile(String sourceName, String outputName, int flags, ClassLoader loader)
+            throws IOException {
         if (outputName != null) {
             try {
                 String parent = new File(outputName).getParent();
@@ -114,7 +140,7 @@
             }
         }
 
-        mCookie = openDexFile(sourceName, outputName, flags);
+        mCookie = openDexFile(sourceName, outputName, flags, loader);
         mFileName = sourceName;
         //System.out.println("DEX FILE cookie is " + mCookie + " sourceName=" + sourceName + " outputName=" + outputName);
     }
@@ -153,7 +179,37 @@
          * decided to open it multiple times.  In practice this may not
          * be a real issue.
          */
-        return new DexFile(sourcePathName, outputPathName, flags);
+        return loadDex(sourcePathName, outputPathName, flags, null);
+    }
+
+    /*
+     * Private version of loadDex that also takes a class loader.
+     *
+     * @param sourcePathName
+     *  Jar or APK file with "classes.dex".  (May expand this to include
+     *  "raw DEX" in the future.)
+     * @param outputPathName
+     *  File that will hold the optimized form of the DEX data.
+     * @param flags
+     *  Enable optional features.  (Currently none defined.)
+     * @param loader
+     *  Class loader that is aloading the DEX file.
+     * @return
+     *  A new or previously-opened DexFile.
+     * @throws IOException
+     *  If unable to open the source or output file.
+     */
+    static DexFile loadDex(String sourcePathName, String outputPathName,
+        int flags, ClassLoader loader) throws IOException {
+
+        /*
+         * TODO: we may want to cache previously-opened DexFile objects.
+         * The cache would be synchronized with close().  This would help
+         * us avoid mapping the same DEX more than once when an app
+         * decided to open it multiple times.  In practice this may not
+         * be a real issue.
+         */
+        return new DexFile(sourcePathName, outputPathName, flags, loader);
     }
 
     /**
@@ -302,11 +358,15 @@
      * Open a DEX file.  The value returned is a magic VM cookie.  On
      * failure, an IOException is thrown.
      */
-    private static Object openDexFile(String sourceName, String outputName, int flags) throws IOException {
+    private static Object openDexFile(String sourceName, String outputName, int flags,
+            ClassLoader loader) throws IOException {
         // Use absolute paths to enable the use of relative paths when testing on host.
         return openDexFileNative(new File(sourceName).getAbsolutePath(),
-                                 (outputName == null) ? null : new File(outputName).getAbsolutePath(),
-                                 flags);
+                                 (outputName == null)
+                                     ? null
+                                     : new File(outputName).getAbsolutePath(),
+                                 flags,
+                                 loader);
     }
 
     /*
@@ -321,7 +381,8 @@
      * Open a DEX file.  The value returned is a magic VM cookie.  On
      * failure, an IOException is thrown.
      */
-    private static native Object openDexFileNative(String sourceName, String outputName, int flags);
+    private static native Object openDexFileNative(String sourceName, String outputName, int flags,
+            ClassLoader loader);
 
     /**
      * Returns true if the VM believes that the apk/jar file is out of date
diff --git a/dalvik/src/main/java/dalvik/system/DexPathList.java b/dalvik/src/main/java/dalvik/system/DexPathList.java
index 5552b1c..49da901 100644
--- a/dalvik/src/main/java/dalvik/system/DexPathList.java
+++ b/dalvik/src/main/java/dalvik/system/DexPathList.java
@@ -119,7 +119,7 @@
         ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
         // save dexPath for BaseDexClassLoader
         this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
-                                           suppressedExceptions);
+                                           suppressedExceptions, definingContext);
 
         // Native libraries may exist in both the system and
         // application library paths, and we use this search order:
@@ -138,7 +138,8 @@
         allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);
 
         this.nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories,
-                                                          suppressedExceptions);
+                                                          suppressedExceptions,
+                                                          definingContext);
 
         if (suppressedExceptions.size() > 0) {
             this.dexElementsSuppressedExceptions =
@@ -212,16 +213,18 @@
      * the given array.
      */
     private static Element[] makeDexElements(List<File> files, File optimizedDirectory,
-                                             List<IOException> suppressedExceptions) {
-        return makeElements(files, optimizedDirectory, suppressedExceptions, false);
+                                             List<IOException> suppressedExceptions,
+                                             ClassLoader loader) {
+        return makeElements(files, optimizedDirectory, suppressedExceptions, false, loader);
     }
 
     /**
      * Makes an array of directory/zip path elements, one per element of the given array.
      */
     private static Element[] makePathElements(List<File> files,
-                                              List<IOException> suppressedExceptions) {
-        return makeElements(files, null, suppressedExceptions, true);
+                                              List<IOException> suppressedExceptions,
+                                              ClassLoader loader) {
+        return makeElements(files, null, suppressedExceptions, true, loader);
     }
 
     /*
@@ -230,12 +233,13 @@
      */
     private static Element[] makePathElements(List<File> files, File optimizedDirectory,
                                               List<IOException> suppressedExceptions) {
-        return makeElements(files, null, suppressedExceptions, true);
+        return makeElements(files, null, suppressedExceptions, true, null);
     }
 
     private static Element[] makeElements(List<File> files, File optimizedDirectory,
                                           List<IOException> suppressedExceptions,
-                                          boolean ignoreDexFiles) {
+                                          boolean ignoreDexFiles,
+                                          ClassLoader loader) {
         List<Element> elements = new ArrayList<>();
         /*
          * Open all files and load the (direct or contained) dex files
@@ -260,7 +264,7 @@
                 if (!ignoreDexFiles && name.endsWith(DEX_SUFFIX)) {
                     // Raw dex file (not inside a zip/jar).
                     try {
-                        dex = loadDexFile(file, optimizedDirectory);
+                        dex = loadDexFile(file, optimizedDirectory, loader);
                     } catch (IOException ex) {
                         System.logE("Unable to load dex file: " + file, ex);
                     }
@@ -269,7 +273,7 @@
 
                     if (!ignoreDexFiles) {
                         try {
-                            dex = loadDexFile(file, optimizedDirectory);
+                            dex = loadDexFile(file, optimizedDirectory, loader);
                         } catch (IOException suppressed) {
                             /*
                              * IOException might get thrown "legitimately" by the DexFile constructor if
@@ -295,16 +299,17 @@
     }
 
     /**
-     * Constructs a {@code DexFile} instance, as appropriate depending
-     * on whether {@code optimizedDirectory} is {@code null}.
+     * Constructs a {@code DexFile} instance, as appropriate depending on whether
+     * {@code optimizedDirectory} is {@code null}. An application image file may be associated with
+     * the {@code loader} if it is not null.
      */
-    private static DexFile loadDexFile(File file, File optimizedDirectory)
+    private static DexFile loadDexFile(File file, File optimizedDirectory, ClassLoader loader)
             throws IOException {
         if (optimizedDirectory == null) {
-            return new DexFile(file);
+            return new DexFile(file, loader);
         } else {
             String optimizedPath = optimizedPathFor(file, optimizedDirectory);
-            return DexFile.loadDex(file.getPath(), optimizedPath, 0);
+            return DexFile.loadDex(file.getPath(), optimizedPath, 0, loader);
         }
     }