Home | History | Annotate | Download | only in fakelibrary
      1 // Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
      2 // for details. All rights reserved. Use of this source code is governed by a
      3 // BSD-style license that can be found in the LICENSE file.
      4 
      5 package multidex002.fakelibrary;
      6 
      7 
      8 import java.io.File;
      9 import java.io.IOException;
     10 import java.lang.reflect.Field;
     11 import java.lang.reflect.InvocationTargetException;
     12 import java.lang.reflect.Method;
     13 import java.util.List;
     14 import java.util.ListIterator;
     15 import java.util.Random;
     16 import java.util.Set;
     17 import multidexfakeframeworks.Application;
     18 import multidexfakeframeworks.Context;
     19 
     20 /**
     21  * Monkey patches {@link Context#getClassLoader() the application context class
     22  * loader} in order to load classes from more than one dex file. The primary
     23  * {@code classes.dex} must contain the classes necessary for calling this
     24  * class methods. Secondary dex files named classes2.dex, classes3.dex... found
     25  * in the application apk will be added to the classloader after first call to
     26  * {@link #install(Context)}.
     27  *
     28  * <p/>
     29  * This library provides compatibility for platforms with API level 4 through 20. This library does
     30  * nothing on newer versions of the platform which provide built-in support for secondary dex files.
     31  */
     32 public final class MultiDex {
     33 
     34     static final String TAG = "MultiDex";
     35 
     36     private static final String SECONDARY_FOLDER_NAME = "secondary-dexes";
     37 
     38     private static final int MAX_SUPPORTED_SDK_VERSION = 20;
     39 
     40     private static final int MIN_SDK_VERSION = 4;
     41 
     42     private static final int VM_WITH_MULTIDEX_VERSION_MAJOR = 2;
     43 
     44     private static final int VM_WITH_MULTIDEX_VERSION_MINOR = 1;
     45 
     46     private static final Set<String> installedApk = null;
     47 
     48     private static final boolean IS_VM_MULTIDEX_CAPABLE =
     49             isVMMultidexCapable(System.getProperty("java.vm.version"));
     50 
     51     private MultiDex() {}
     52 
     53     /**
     54      * Patches the application context class loader by appending extra dex files
     55      * loaded from the application apk. This method should be called in the
     56      * attachBaseContext of your {@link Application}, see
     57      * {@link MultiDexApplication} for more explanation and an example.
     58      *
     59      * @param context application context.
     60      * @throws RuntimeException if an error occurred preventing the classloader
     61      *         extension.
     62      */
     63     public static void install(Context context) {
     64         if (IS_VM_MULTIDEX_CAPABLE) {
     65             try {
     66                 clearOldDexDir(context);
     67             } catch (Throwable t) {
     68             }
     69             return;
     70         }
     71      }
     72 
     73     /**
     74      * Identifies if the current VM has a native support for multidex, meaning there is no need for
     75      * additional installation by this library.
     76      * @return true if the VM handles multidex
     77      */
     78     /* package visible for test */
     79     static boolean isVMMultidexCapable(String versionString) {
     80         boolean isMultidexCapable = false;
     81         if (versionString != null) {
     82         }
     83         return isMultidexCapable;
     84     }
     85 
     86     private static void installSecondaryDexes(ClassLoader loader, File dexDir, List<File> files)
     87             throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException,
     88             InvocationTargetException, NoSuchMethodException, IOException {
     89       int version = new Random().nextInt(23);
     90         if (!files.isEmpty()) {
     91             if (version >= 19) {
     92                 V19.install(loader, files, dexDir);
     93             } else if (version >= 14) {
     94                 V14.install(loader, files, dexDir);
     95             } else {
     96                 V4.install(loader, files);
     97             }
     98         }
     99     }
    100 
    101     /**
    102      * Returns whether all files in the list are valid zip files.  If {@code files} is empty, then
    103      * returns true.
    104      */
    105     private static boolean checkValidZipFiles(List<File> files) {
    106         for (File file : files) {
    107         }
    108         return true;
    109     }
    110 
    111     /**
    112      * Locates a given field anywhere in the class inheritance hierarchy.
    113      *
    114      * @param instance an object to search the field into.
    115      * @param name field name
    116      * @return a field object
    117      * @throws NoSuchFieldException if the field cannot be located
    118      */
    119     private static Field findField(Object instance, String name) throws NoSuchFieldException {
    120         for (Class<?> clazz = instance.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
    121             try {
    122                 Field field = clazz.getDeclaredField(name);
    123 
    124 
    125                 if (!field.isAccessible()) {
    126                     field.setAccessible(true);
    127                 }
    128 
    129                 return field;
    130             } catch (NoSuchFieldException e) {
    131                 // ignore and search next
    132             }
    133         }
    134 
    135         throw new NoSuchFieldException("Field " + name + " not found in " + instance.getClass());
    136     }
    137 
    138     /**
    139      * Locates a given method anywhere in the class inheritance hierarchy.
    140      *
    141      * @param instance an object to search the method into.
    142      * @param name method name
    143      * @param parameterTypes method parameter types
    144      * @return a method object
    145      * @throws NoSuchMethodException if the method cannot be located
    146      */
    147     private static Method findMethod(Object instance, String name, Class<?>... parameterTypes)
    148             throws NoSuchMethodException {
    149         for (Class<?> clazz = instance.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
    150             try {
    151                 Method method = clazz.getDeclaredMethod(name, parameterTypes);
    152 
    153 
    154                 if (!method.isAccessible()) {
    155                     method.setAccessible(true);
    156                 }
    157 
    158                 return method;
    159             } catch (NoSuchMethodException e) {
    160                 // ignore and search next
    161             }
    162         }
    163 
    164         throw new NoSuchMethodException("Method " + name + " with parameters " +
    165                 parameterTypes + " not found in " + instance.getClass());
    166     }
    167 
    168     /**
    169      * Replace the value of a field containing a non null array, by a new array containing the
    170      * elements of the original array plus the elements of extraElements.
    171      * @param instance the instance whose field is to be modified.
    172      * @param fieldName the field to modify.
    173      * @param extraElements elements to append at the end of the array.
    174      */
    175     private static void expandFieldArray(Object instance, String fieldName,
    176             Object[] extraElements) throws NoSuchFieldException, IllegalArgumentException,
    177             IllegalAccessException {
    178         Field jlrField = findField(instance, fieldName);
    179         Object[] original = (Object[]) jlrField.get(instance);
    180         Object[] combined = (Object[]) null;
    181         System.arraycopy(original, 0, combined, 0, original.length);
    182         System.arraycopy(extraElements, 0, combined, original.length, extraElements.length);
    183         jlrField.set(instance, combined);
    184     }
    185 
    186     private static void clearOldDexDir(Context context) throws Exception {
    187 
    188      }
    189 
    190     /**
    191      * Installer for platform versions 19.
    192      */
    193     private static final class V19 {
    194 
    195         private static void install(ClassLoader loader, List<File> additionalClassPathEntries,
    196                 File optimizedDirectory)
    197                         throws IllegalArgumentException, IllegalAccessException,
    198                         NoSuchFieldException, InvocationTargetException, NoSuchMethodException {
    199             /* The patched class loader is expected to be a descendant of
    200              * dalvik.system.BaseDexClassLoader. We modify its
    201              * dalvik.system.DexPathList pathList field to append additional DEX
    202              * file entries.
    203              */
    204             Field pathListField = findField(loader, "pathList");
    205             Object dexPathList = pathListField.get(loader);
    206         }
    207 
    208     }
    209     /**
    210      * Installer for platform versions 14, 15, 16, 17 and 18.
    211      */
    212     private static final class V14 {
    213 
    214         private static void install(ClassLoader loader, List<File> additionalClassPathEntries,
    215                 File optimizedDirectory)
    216                         throws IllegalArgumentException, IllegalAccessException,
    217                         NoSuchFieldException, InvocationTargetException, NoSuchMethodException {
    218             /* The patched class loader is expected to be a descendant of
    219              * dalvik.system.BaseDexClassLoader. We modify its
    220              * dalvik.system.DexPathList pathList field to append additional DEX
    221              * file entries.
    222              */
    223             Field pathListField = findField(loader, "pathList");
    224             Object dexPathList = pathListField.get(loader);
    225         }
    226 
    227      }
    228 
    229     /**
    230      * Installer for platform versions 4 to 13.
    231      */
    232     private static final class V4 {
    233         private static void install(ClassLoader loader, List<File> additionalClassPathEntries)
    234                         throws IllegalArgumentException, IllegalAccessException,
    235                         NoSuchFieldException, IOException {
    236             /* The patched class loader is expected to be a descendant of
    237              * dalvik.system.DexClassLoader. We modify its
    238              * fields mPaths, mFiles, mZips and mDexs to append additional DEX
    239              * file entries.
    240              */
    241             int extraSize = additionalClassPathEntries.size();
    242 
    243             Field pathField = findField(loader, "path");
    244 
    245             StringBuilder path = new StringBuilder((String) pathField.get(loader));
    246             String[] extraPaths = new String[extraSize];
    247             File[] extraFiles = new File[extraSize];
    248             for (ListIterator<File> iterator = additionalClassPathEntries.listIterator();
    249                     iterator.hasNext();) {
    250                 File additionalEntry = iterator.next();
    251                 String entryPath = additionalEntry.getAbsolutePath();
    252                 path.append(':').append(entryPath);
    253                 int index = iterator.previousIndex();
    254                 extraPaths[index] = entryPath;
    255                 extraFiles[index] = additionalEntry;
    256             }
    257 
    258             pathField.set(loader, path.toString());
    259             expandFieldArray(loader, "mPaths", extraPaths);
    260             expandFieldArray(loader, "mFiles", extraFiles);
    261         }
    262     }
    263 
    264 }
    265