[go: nahoru, domu]

MultiDex.java revision 66f379f774e06e650375750d18b1695ddb853371
1667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu/*
2667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * Copyright (C) 2013 The Android Open Source Project
3667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu *
4667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * Licensed under the Apache License, Version 2.0 (the "License");
5667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * you may not use this file except in compliance with the License.
6667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * You may obtain a copy of the License at
7667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu *
8667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu *      http://www.apache.org/licenses/LICENSE-2.0
9667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu *
10667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * Unless required by applicable law or agreed to in writing, software
11667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * distributed under the License is distributed on an "AS IS" BASIS,
12667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * See the License for the specific language governing permissions and
14667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * limitations under the License.
15667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu */
16667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
17667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chupackage android.support.multidex;
18667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
1966f379f774e06e650375750d18b1695ddb853371Maurice Chuimport dalvik.system.DexFile;
2066f379f774e06e650375750d18b1695ddb853371Maurice Chu
21667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport android.content.Context;
22667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport android.content.pm.ApplicationInfo;
23667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport android.content.pm.PackageManager;
24667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport android.os.Build;
25667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport android.util.Log;
26667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
27667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport java.io.File;
28667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport java.io.IOException;
29667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport java.lang.reflect.Array;
30667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport java.lang.reflect.Field;
31667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport java.lang.reflect.InvocationTargetException;
32667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport java.lang.reflect.Method;
33667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport java.util.ArrayList;
34667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport java.util.Arrays;
35667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport java.util.HashSet;
36667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport java.util.List;
37667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport java.util.ListIterator;
38667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport java.util.Set;
3966f379f774e06e650375750d18b1695ddb853371Maurice Chuimport java.util.zip.ZipFile;
40667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
41667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu/**
42667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * Monkey patches {@link Context#getClassLoader() the application context class
43667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * loader} in order to load classes from more than one dex file. The primary
44667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * {@code classes.dex} file necessary for calling this class methods. secondary
45667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * dex files named classes2.dex, classes".dex... found in the application apk
46667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * will be added to the classloader after first call to
47667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * {@link #install(Context)}.
48667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu *
49667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * <p/>
50667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * <strong>IMPORTANT:</strong>This library provides compatibility for platforms
51667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * with API level 4 through 19. This library does nothing on newer versions of
52667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * the platform which provide built-in support for secondary dex files.
53667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu */
54667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chupublic final class MultiDex {
55667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
56667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu    static final String TAG = "MultiDex";
57667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
58667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu    private static final String SECONDARY_FOLDER_NAME = "secondary-dexes";
59667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
60667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu    private static final int SUPPORTED_MULTIDEX_SDK_VERSION = 20;
61667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
62667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu    private static final int MIN_SDK_VERSION = 4;
63667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
64667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu    private static final Set<String> installedApk = new HashSet<String>();
65667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
66667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu    private MultiDex() {}
67667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
68667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu    /**
69667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     * Patches the application context class loader by appending extra dex files
70667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     * loaded from the application apk. Call this method first thing in your
71667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     * {@code Application#OnCreate}, {@code Instrumentation#OnCreate},
72667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     * {@code BackupAgent#OnCreate}, {@code Service#OnCreate},
73667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     * {@code BroadcastReceiver#onReceive}, {@code Activity#OnCreate} and
74667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     * {@code ContentProvider#OnCreate} .
75667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     *
76667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     * @param context application context.
77667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     * @throws RuntimeException if an error occurred preventing the classloader
78667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     *         extension.
79667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     */
80667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu    public static void install(Context context) {
81667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
82667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu        if (Build.VERSION.SDK_INT < MIN_SDK_VERSION) {
83667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            throw new RuntimeException("Multi dex installation failed. SDK " + Build.VERSION.SDK_INT
84667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                    + " is unsupported. Min SDK version is " + MIN_SDK_VERSION + ".");
85667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu        }
86667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
87667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
88667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu        try {
89667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            PackageManager pm;
90667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            String packageName;
91667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            try {
92667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                pm = context.getPackageManager();
93667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                packageName = context.getPackageName();
94667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            } catch (RuntimeException e) {
95667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                /* Ignore those exceptions so that we don't break tests relying on Context like
96667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                 * a android.test.mock.MockContext or a android.content.ContextWrapper with a null
97667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                 * base Context.
98667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                 */
99667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                Log.w(TAG, "Failure while trying to obtain ApplicationInfo from Context. " +
100667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                        "Must be running in test mode. Skip patching.", e);
101667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                return;
102667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            }
103667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            if (pm == null || packageName == null) {
104667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                // This is most likely a mock context, so just return without patching.
105667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                return;
106667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            }
107667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            ApplicationInfo applicationInfo =
108667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                    pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
109667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            if (applicationInfo == null) {
110667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                // This is from a mock context, so just return without patching.
111667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                return;
112667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            }
113667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
114667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            synchronized (installedApk) {
115667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                String apkPath = applicationInfo.sourceDir;
116667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                if (installedApk.contains(apkPath)) {
117667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                    return;
118667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                }
119667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                installedApk.add(apkPath);
120667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
121667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                if (Build.VERSION.SDK_INT >= SUPPORTED_MULTIDEX_SDK_VERSION) {
122667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                    // STOPSHIP: Any app that uses this class needs approval before being released
123667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                    // as well as figuring out what the right behavior should be here.
124667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                    throw new RuntimeException("Platform support of multidex for SDK " +
125667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                            Build.VERSION.SDK_INT + " has not been confirmed yet.");
126667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                }
127667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
128667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                /* The patched class loader is expected to be a descendant of
129667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                 * dalvik.system.BaseDexClassLoader. We modify its
130667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                 * dalvik.system.DexPathList pathList field to append additional DEX
131667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                 * file entries.
132667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                 */
133667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                ClassLoader loader;
134667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                try {
135667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                    loader = context.getClassLoader();
136667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                } catch (RuntimeException e) {
137667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                    /* Ignore those exceptions so that we don't break tests relying on Context like
138667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                     * a android.test.mock.MockContext or a android.content.ContextWrapper with a
139667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                     * null base Context.
140667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                     */
141667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                    Log.w(TAG, "Failure while trying to obtain Context class loader. " +
142667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                            "Must be running in test mode. Skip patching.", e);
143667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                    return;
144667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                }
145667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                if (loader == null) {
146667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                    // Note, the context class loader is null when running Robolectric tests.
147667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                    Log.e(TAG,
148667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                            "Context class loader is null. Must be running in test mode. "
149667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                            + "Skip patching.");
150667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                    return;
151667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                }
152667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
153667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                File dexDir = new File(context.getFilesDir(), SECONDARY_FOLDER_NAME);
154667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                List<File> files = MultiDexExtractor.load(context, applicationInfo, dexDir);
155667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                if (!files.isEmpty()) {
156667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                    if (Build.VERSION.SDK_INT >= 19) {
157667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                        V19.install(loader, files, dexDir);
158667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                    } else if (Build.VERSION.SDK_INT >= 14) {
159667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                        V14.install(loader, files, dexDir);
160667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                    } else {
161667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                        V4.install(loader, files, dexDir);
162667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                    }
163667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                }
164667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            }
165667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
166667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu        } catch (Exception e) {
167667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            Log.e(TAG, "Multidex installation failure", e);
168667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            throw new RuntimeException("Multi dex installation failed (" + e.getMessage() + ").");
169667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu        }
170667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu    }
171667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
172667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu    /**
173667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     * Locates a given field anywhere in the class inheritance hierarchy.
174667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     *
175667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     * @param instance an object to search the field into.
176667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     * @param name field name
177667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     * @return a field object
178667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     * @throws NoSuchFieldException if the field cannot be located
179667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     */
180667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu    private static Field findField(Object instance, String name) throws NoSuchFieldException {
181667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu        for (Class<?> clazz = instance.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
182667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            try {
183667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                Field field = clazz.getDeclaredField(name);
184667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
185667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
186667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                if (!field.isAccessible()) {
187667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                    field.setAccessible(true);
188667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                }
189667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
190667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                return field;
191667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            } catch (NoSuchFieldException e) {
192667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                // ignore and search next
193667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            }
194667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu        }
195667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
196667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu        throw new NoSuchFieldException("Field " + name + " not found in " + instance.getClass());
197667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu    }
198667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
199667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu    /**
200667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     * Locates a given method anywhere in the class inheritance hierarchy.
201667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     *
202667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     * @param instance an object to search the method into.
203667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     * @param name method name
204667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     * @param parameterTypes method parameter types
205667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     * @return a method object
206667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     * @throws NoSuchMethodException if the method cannot be located
207667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     */
208667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu    private static Method findMethod(Object instance, String name, Class<?>... parameterTypes)
209667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            throws NoSuchMethodException {
210667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu        for (Class<?> clazz = instance.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
211667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            try {
212667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                Method method = clazz.getDeclaredMethod(name, parameterTypes);
213667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
214667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
215667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                if (!method.isAccessible()) {
216667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                    method.setAccessible(true);
217667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                }
218667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
219667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                return method;
220667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            } catch (NoSuchMethodException e) {
221667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                // ignore and search next
222667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            }
223667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu        }
224667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
225667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu        throw new NoSuchMethodException("Method " + name + " with parameters " +
226667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                Arrays.asList(parameterTypes) + " not found in " + instance.getClass());
227667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu    }
228667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
229667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu    /**
230667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     * Replace the value of a field containing a non null array, by a new array containing the
231667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     * elements of the original array plus the elements of extraElements.
232667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     * @param instance the instance whose field is to be modified.
233667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     * @param fieldName the field to modify.
234667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     * @param extraElements elements to append at the end of the array.
235667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     */
236667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu    private static void expandFieldArray(Object instance, String fieldName,
237667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            Object[] extraElements) throws NoSuchFieldException, IllegalArgumentException,
238667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            IllegalAccessException {
239667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu        Field jlrField = findField(instance, fieldName);
240667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu        Object[] original = (Object[]) jlrField.get(instance);
241667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu        Object[] combined = (Object[]) Array.newInstance(
242667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                original.getClass().getComponentType(), original.length + extraElements.length);
243667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu        System.arraycopy(original, 0, combined, 0, original.length);
244667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu        System.arraycopy(extraElements, 0, combined, original.length, extraElements.length);
245667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu        jlrField.set(instance, combined);
246667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu    }
247667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
248667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu    /**
249667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     * Installer for platform versions 19.
250667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     */
251667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu    private static final class V19 {
252667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
253667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu        private static void install(ClassLoader loader, List<File> additionalClassPathEntries,
254667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                File optimizedDirectory)
255667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                        throws IllegalArgumentException, IllegalAccessException,
256667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                        NoSuchFieldException, InvocationTargetException, NoSuchMethodException {
257667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            /* The patched class loader is expected to be a descendant of
258667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu             * dalvik.system.BaseDexClassLoader. We modify its
259667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu             * dalvik.system.DexPathList pathList field to append additional DEX
260667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu             * file entries.
261667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu             */
262667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            Field pathListField = findField(loader, "pathList");
263667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            Object dexPathList = pathListField.get(loader);
264667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
265667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            expandFieldArray(dexPathList, "dexElements", makeDexElements(dexPathList,
266667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                    new ArrayList<File>(additionalClassPathEntries), optimizedDirectory,
267667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                    suppressedExceptions));
268667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            if (suppressedExceptions.size() > 0) {
269667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                Field suppressedExceptionsField =
270667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                        findField(loader, "dexElementsSuppressedExceptions");
271667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                IOException[] dexElementsSuppressedExceptions =
272667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                        (IOException[]) suppressedExceptionsField.get(loader);
273667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
274667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                if (dexElementsSuppressedExceptions == null) {
275667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                    dexElementsSuppressedExceptions =
276667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                            suppressedExceptions.toArray(
277667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                                    new IOException[suppressedExceptions.size()]);
278667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                } else {
279667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                    IOException[] combined =
280667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                            new IOException[suppressedExceptions.size() +
281667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                                            dexElementsSuppressedExceptions.length];
282667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                    suppressedExceptions.toArray(combined);
283667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                    System.arraycopy(dexElementsSuppressedExceptions, 0, combined,
284667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                            suppressedExceptions.size(), dexElementsSuppressedExceptions.length);
285667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                    dexElementsSuppressedExceptions = combined;
286667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                }
287667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
288667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                suppressedExceptionsField.set(loader, dexElementsSuppressedExceptions);
289667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            }
290667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu        }
291667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
292667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu        /**
293667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu         * A wrapper around
294667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu         * {@code private static final dalvik.system.DexPathList#makeDexElements}.
295667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu         */
296667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu        private static Object[] makeDexElements(
297667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                Object dexPathList, ArrayList<File> files, File optimizedDirectory,
298667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                ArrayList<IOException> suppressedExceptions)
299667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                        throws IllegalAccessException, InvocationTargetException,
300667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                        NoSuchMethodException {
301667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            Method makeDexElements =
302667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                    findMethod(dexPathList, "makeDexElements", ArrayList.class, File.class,
303667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                            ArrayList.class);
304667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
305667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            return (Object[]) makeDexElements.invoke(dexPathList, files, optimizedDirectory,
306667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                    suppressedExceptions);
307667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu        }
308667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu    }
309667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
310667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu    /**
311667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     * Installer for platform versions 14, 15, 16, 17 and 18.
312667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     */
313667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu    private static final class V14 {
314667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
315667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu        private static void install(ClassLoader loader, List<File> additionalClassPathEntries,
316667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                File optimizedDirectory)
317667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                        throws IllegalArgumentException, IllegalAccessException,
318667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                        NoSuchFieldException, InvocationTargetException, NoSuchMethodException {
319667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            /* The patched class loader is expected to be a descendant of
320667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu             * dalvik.system.BaseDexClassLoader. We modify its
321667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu             * dalvik.system.DexPathList pathList field to append additional DEX
322667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu             * file entries.
323667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu             */
324667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            Field pathListField = findField(loader, "pathList");
325667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            Object dexPathList = pathListField.get(loader);
326667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            expandFieldArray(dexPathList, "dexElements", makeDexElements(dexPathList,
327667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                    new ArrayList<File>(additionalClassPathEntries), optimizedDirectory));
328667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu        }
329667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
330667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu        /**
331667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu         * A wrapper around
332667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu         * {@code private static final dalvik.system.DexPathList#makeDexElements}.
333667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu         */
334667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu        private static Object[] makeDexElements(
335667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                Object dexPathList, ArrayList<File> files, File optimizedDirectory)
336667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                        throws IllegalAccessException, InvocationTargetException,
337667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                        NoSuchMethodException {
338667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            Method makeDexElements =
339667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                    findMethod(dexPathList, "makeDexElements", ArrayList.class, File.class);
340667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
341667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            return (Object[]) makeDexElements.invoke(dexPathList, files, optimizedDirectory);
342667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu        }
343667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu    }
344667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
345667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu    /**
346667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     * Installer for platform versions 4 to 13.
347667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu     */
348667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu    private static final class V4 {
349667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu        private static void install(ClassLoader loader, List<File> additionalClassPathEntries,
350667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                File optimizedDirectory)
351667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                        throws IllegalArgumentException, IllegalAccessException,
352667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                        NoSuchFieldException, IOException {
353667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            /* The patched class loader is expected to be a descendant of
354667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu             * dalvik.system.DexClassLoader. We modify its
355667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu             * dalvik.system.DexPathList pathList field to append additional DEX
356667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu             * file entries.
357667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu             */
358667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            int extraSize = additionalClassPathEntries.size();
359667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
360667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            Field pathField = findField(loader, "path");
361667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
362667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            StringBuilder path = new StringBuilder((String) pathField.get(loader));
363667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            String[] extraPaths = new String[extraSize];
364667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            File[] extraFiles = new File[extraSize];
36566f379f774e06e650375750d18b1695ddb853371Maurice Chu            ZipFile[] extraZips = new ZipFile[extraSize];
366667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            DexFile[] extraDexs = new DexFile[extraSize];
367667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            for (ListIterator<File> iterator = additionalClassPathEntries.listIterator();
368667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                    iterator.hasNext();) {
369667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                File additionalEntry = iterator.next();
370667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                String entryPath = additionalEntry.getAbsolutePath();
371667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                path.append(':').append(entryPath);
372667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                int index = iterator.previousIndex();
373667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                extraPaths[index] = entryPath;
374667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                extraFiles[index] = additionalEntry;
37566f379f774e06e650375750d18b1695ddb853371Maurice Chu                extraZips[index] = new ZipFile(additionalEntry);
376667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu                extraDexs[index] = DexFile.loadDex(entryPath, entryPath + ".dex", 0);
377667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            }
378667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
379667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            pathField.set(loader, path.toString());
380667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            expandFieldArray(loader, "mPaths", extraPaths);
381667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            expandFieldArray(loader, "mFiles", extraFiles);
38266f379f774e06e650375750d18b1695ddb853371Maurice Chu            expandFieldArray(loader, "mZips", extraZips);
383667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu            expandFieldArray(loader, "mDexs", extraDexs);
384667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu        }
385667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu    }
386667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu
387667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu}
388