Snap for 4560043 from b9693ede38ec67d5bcfb2efd413aa3ab11b870eb to pi-release

Change-Id: Ib82dc17180127d98506012b3096b63ac347d9997
diff --git a/library/src/android/support/multidex/MultiDex.java b/library/src/android/support/multidex/MultiDex.java
index 646e023..2b681db 100644
--- a/library/src/android/support/multidex/MultiDex.java
+++ b/library/src/android/support/multidex/MultiDex.java
@@ -28,6 +28,7 @@
 import java.io.File;
 import java.io.IOException;
 import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
@@ -202,7 +203,8 @@
             String secondaryFolderName, String prefsKeyPrefix,
             boolean reinstallOnPatchRecoverableException) throws IOException,
                 IllegalArgumentException, IllegalAccessException, NoSuchFieldException,
-                InvocationTargetException, NoSuchMethodException {
+                InvocationTargetException, NoSuchMethodException, SecurityException,
+                ClassNotFoundException, InstantiationException {
         synchronized (installedApk) {
             if (installedApk.contains(sourceApk)) {
                 return;
@@ -340,12 +342,13 @@
     private static void installSecondaryDexes(ClassLoader loader, File dexDir,
         List<? extends File> files)
             throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException,
-            InvocationTargetException, NoSuchMethodException, IOException {
+            InvocationTargetException, NoSuchMethodException, IOException, SecurityException,
+            ClassNotFoundException, InstantiationException {
         if (!files.isEmpty()) {
             if (Build.VERSION.SDK_INT >= 19) {
                 V19.install(loader, files, dexDir);
             } else if (Build.VERSION.SDK_INT >= 14) {
-                V14.install(loader, files, dexDir);
+                V14.install(loader, files);
             } else {
                 V4.install(loader, files);
             }
@@ -495,7 +498,7 @@
      */
     private static final class V19 {
 
-        private static void install(ClassLoader loader,
+        static void install(ClassLoader loader,
                 List<? extends File> additionalClassPathEntries,
                 File optimizedDirectory)
                         throws IllegalArgumentException, IllegalAccessException,
@@ -566,11 +569,16 @@
      */
     private static final class V14 {
 
-        private static void install(ClassLoader loader,
-                List<? extends File> additionalClassPathEntries,
-                File optimizedDirectory)
-                        throws IllegalArgumentException, IllegalAccessException,
-                        NoSuchFieldException, InvocationTargetException, NoSuchMethodException {
+        private static final int EXTRACTED_SUFFIX_LENGTH =
+                MultiDexExtractor.EXTRACTED_SUFFIX.length();
+
+        private final Constructor<?> elementConstructor;
+
+        static void install(ClassLoader loader,
+                List<? extends File> additionalClassPathEntries)
+                        throws  IOException, SecurityException, IllegalArgumentException,
+                        ClassNotFoundException, NoSuchMethodException, InstantiationException,
+                        IllegalAccessException, InvocationTargetException, NoSuchFieldException {
             /* The patched class loader is expected to be a descendant of
              * dalvik.system.BaseDexClassLoader. We modify its
              * dalvik.system.DexPathList pathList field to append additional DEX
@@ -578,22 +586,52 @@
              */
             Field pathListField = findField(loader, "pathList");
             Object dexPathList = pathListField.get(loader);
-            expandFieldArray(dexPathList, "dexElements", makeDexElements(dexPathList,
-                    new ArrayList<File>(additionalClassPathEntries), optimizedDirectory));
+            expandFieldArray(dexPathList, "dexElements",
+                    new V14().makeDexElements(additionalClassPathEntries));
+        }
+
+        private  V14() throws ClassNotFoundException, SecurityException, NoSuchMethodException {
+            Class<?> elementClass = Class.forName("dalvik.system.DexPathList$Element");
+            elementConstructor =
+                    elementClass.getConstructor(File.class, ZipFile.class, DexFile.class);
+            elementConstructor.setAccessible(true);
         }
 
         /**
-         * A wrapper around
-         * {@code private static final dalvik.system.DexPathList#makeDexElements}.
+         * An emulation of {@code private static final dalvik.system.DexPathList#makeDexElements}
+         * accepting only extracted secondary dex files.
+         * OS version is catching IOException and just logging some of them, this version is letting
+         * them through.
          */
-        private static Object[] makeDexElements(
-                Object dexPathList, ArrayList<File> files, File optimizedDirectory)
-                        throws IllegalAccessException, InvocationTargetException,
-                        NoSuchMethodException {
-            Method makeDexElements =
-                    findMethod(dexPathList, "makeDexElements", ArrayList.class, File.class);
+        private Object[] makeDexElements(List<? extends File> files)
+                throws IOException, SecurityException, IllegalArgumentException,
+                InstantiationException, IllegalAccessException, InvocationTargetException {
+            Object[] elements = new Object[files.size()];
+            for (int i = 0; i < elements.length; i++) {
+                File file = files.get(i);
+                elements[i] = elementConstructor.newInstance(
+                        file,
+                        new ZipFile(file),
+                        DexFile.loadDex(file.getPath(), optimizedPathFor(file), 0));
+            }
+            return elements;
+        }
 
-            return (Object[]) makeDexElements.invoke(dexPathList, files, optimizedDirectory);
+        /**
+         * Converts a zip file path of an extracted secondary dex to an output file path for an
+         * associated optimized dex file.
+         */
+        private static String optimizedPathFor(File path) {
+            // Any reproducible name ending with ".dex" should do but lets keep the same name
+            // as DexPathList.optimizedPathFor
+
+            File optimizedDirectory = path.getParentFile();
+            String fileName = path.getName();
+            String optimizedFileName =
+                    fileName.substring(0, fileName.length() - EXTRACTED_SUFFIX_LENGTH)
+                    + MultiDexExtractor.DEX_SUFFIX;
+            File result = new File(optimizedDirectory, optimizedFileName);
+            return result.getPath();
         }
     }
 
@@ -601,7 +639,7 @@
      * Installer for platform versions 4 to 13.
      */
     private static final class V4 {
-        private static void install(ClassLoader loader,
+        static void install(ClassLoader loader,
                 List<? extends File> additionalClassPathEntries)
                         throws IllegalArgumentException, IllegalAccessException,
                         NoSuchFieldException, IOException {
diff --git a/library/src/android/support/multidex/MultiDexExtractor.java b/library/src/android/support/multidex/MultiDexExtractor.java
index ed3c125..f0fd6d4 100644
--- a/library/src/android/support/multidex/MultiDexExtractor.java
+++ b/library/src/android/support/multidex/MultiDexExtractor.java
@@ -63,10 +63,10 @@
      * {@code classes3.dex}, etc.
      */
     private static final String DEX_PREFIX = "classes";
-    private static final String DEX_SUFFIX = ".dex";
+    static final String DEX_SUFFIX = ".dex";
 
     private static final String EXTRACTED_NAME_EXT = ".classes";
-    private static final String EXTRACTED_SUFFIX = ".zip";
+    static final String EXTRACTED_SUFFIX = ".zip";
     private static final int MAX_EXTRACT_ATTEMPTS = 3;
 
     private static final String PREFS_FILE = "multidex.version";