MultiDex.java revision 7e267a38525afac2a571da186e770a2b86a01846
11935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov/*
21935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov * Copyright (C) 2013 The Android Open Source Project
31935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov *
41935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov * Licensed under the Apache License, Version 2.0 (the "License");
51935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov * you may not use this file except in compliance with the License.
61935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov * You may obtain a copy of the License at
71935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov *
81935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov *      http://www.apache.org/licenses/LICENSE-2.0
91935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov *
101935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov * Unless required by applicable law or agreed to in writing, software
111935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov * distributed under the License is distributed on an "AS IS" BASIS,
121935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov * See the License for the specific language governing permissions and
141935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov * limitations under the License.
151935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov */
161935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov
171935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganovpackage android.support.multidex;
181935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov
1944e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackbornimport dalvik.system.DexFile;
2044e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn
211935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganovimport android.content.Context;
221935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganovimport android.content.pm.ApplicationInfo;
231935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganovimport android.content.pm.PackageManager;
2444e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackbornimport android.os.Build;
251935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganovimport android.util.Log;
261935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov
270574ca37da4619afe4e26753f5a1b4de314b6565Svetoslav Ganovimport java.io.File;
280574ca37da4619afe4e26753f5a1b4de314b6565Svetoslav Ganovimport java.io.IOException;
291935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganovimport java.lang.reflect.Array;
301935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganovimport java.lang.reflect.Field;
311935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganovimport java.lang.reflect.InvocationTargetException;
321935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganovimport java.lang.reflect.Method;
331935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganovimport java.util.ArrayList;
3444e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackbornimport java.util.Arrays;
3544e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackbornimport java.util.HashSet;
3644e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackbornimport java.util.List;
371935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganovimport java.util.ListIterator;
381935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganovimport java.util.Set;
3944e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackbornimport java.util.zip.ZipFile;
4044e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn
4144e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn/**
4244e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn * Monkey patches {@link Context#getClassLoader() the application context class
4344e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn * loader} in order to load classes from more than one dex file. The primary
4444e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn * {@code classes.dex} file necessary for calling this class methods. secondary
4544e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn * dex files named classes2.dex, classes".dex... found in the application apk
4644e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn * will be added to the classloader after first call to
4744e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn * {@link #install(Context)}.
4844e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn *
4944e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn * <p/>
5044e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn * <strong>IMPORTANT:</strong>This library provides compatibility for platforms
511935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov * with API level 4 through 19. This library does nothing on newer versions of
521935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov * the platform which provide built-in support for secondary dex files.
531935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov */
541935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganovpublic final class MultiDex {
551935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov
561935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov    static final String TAG = "MultiDex";
571935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov
581935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov    private static final String SECONDARY_FOLDER_NAME = "secondary-dexes";
591935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov
601935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov    private static final int SUPPORTED_MULTIDEX_SDK_VERSION = 20;
6144e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn
6244e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn    private static final int MIN_SDK_VERSION = 4;
6344e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn
6444e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn    private static final Set<String> installedApk = new HashSet<String>();
6544e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn
6644e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn    private MultiDex() {}
6744e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn
6844e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn    /**
6944e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn     * Patches the application context class loader by appending extra dex files
7044e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn     * loaded from the application apk. Call this method first thing in your
7144e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn     * {@code Application#OnCreate}, {@code Instrumentation#OnCreate},
7244e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn     * {@code BackupAgent#OnCreate}, {@code Service#OnCreate},
731935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov     * {@code BroadcastReceiver#onReceive}, {@code Activity#OnCreate} and
741935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov     * {@code ContentProvider#OnCreate} .
751935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov     *
761935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov     * @param context application context.
771935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov     * @throws RuntimeException if an error occurred preventing the classloader
781935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov     *         extension.
7944e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn     */
8044e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn    public static void install(Context context) {
8144e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn
8244e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn        if (Build.VERSION.SDK_INT < MIN_SDK_VERSION) {
8344e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            throw new RuntimeException("Multi dex installation failed. SDK " + Build.VERSION.SDK_INT
8444e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                    + " is unsupported. Min SDK version is " + MIN_SDK_VERSION + ".");
8544e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn        }
8644e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn
8744e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn
8844e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn        try {
8944e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            PackageManager pm;
9044e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            String packageName;
9144e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            try {
9244e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                pm = context.getPackageManager();
9344e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                packageName = context.getPackageName();
941935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov            } catch (RuntimeException e) {
9544e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                /* Ignore those exceptions so that we don't break tests relying on Context like
9644e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                 * a android.test.mock.MockContext or a android.content.ContextWrapper with a null
9744e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                 * base Context.
9844e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                 */
9944e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                Log.w(TAG, "Failure while trying to obtain ApplicationInfo from Context. " +
10044e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                        "Must be running in test mode. Skip patching.", e);
10144e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                return;
10244e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            }
10344e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            if (pm == null || packageName == null) {
10444e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                // This is most likely a mock context, so just return without patching.
10544e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                return;
10644e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            }
10744e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            ApplicationInfo applicationInfo =
10844e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                    pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
10944e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            if (applicationInfo == null) {
11044e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                // This is from a mock context, so just return without patching.
11144e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                return;
11244e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            }
11344e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn
11444e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            synchronized (installedApk) {
11544e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                String apkPath = applicationInfo.sourceDir;
11644e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                if (installedApk.contains(apkPath)) {
11744e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                    return;
11844e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                }
11944e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                installedApk.add(apkPath);
12044e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn
12144e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                if (Build.VERSION.SDK_INT >= SUPPORTED_MULTIDEX_SDK_VERSION) {
12244e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                    // STOPSHIP: Any app that uses this class needs approval before being released
12344e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                    // as well as figuring out what the right behavior should be here.
12444e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                    throw new RuntimeException("Platform support of multidex for SDK " +
12544e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                            Build.VERSION.SDK_INT + " has not been confirmed yet.");
12644e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                }
12744e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn
12844e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                /* The patched class loader is expected to be a descendant of
12944e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                 * dalvik.system.BaseDexClassLoader. We modify its
13044e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                 * dalvik.system.DexPathList pathList field to append additional DEX
13144e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                 * file entries.
1321935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                 */
1331935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                ClassLoader loader;
1341935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                try {
1351935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                    loader = context.getClassLoader();
1361935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                } catch (RuntimeException e) {
1371935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                    /* Ignore those exceptions so that we don't break tests relying on Context like
1381935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                     * a android.test.mock.MockContext or a android.content.ContextWrapper with a
1391935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                     * null base Context.
1401935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                     */
1411935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                    Log.w(TAG, "Failure while trying to obtain Context class loader. " +
1421935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                            "Must be running in test mode. Skip patching.", e);
14344e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                    return;
14444e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                }
14544e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                if (loader == null) {
14644e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                    // Note, the context class loader is null when running Robolectric tests.
14744e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                    Log.e(TAG,
1481935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                            "Context class loader is null. Must be running in test mode. "
1491935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                            + "Skip patching.");
1501935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                    return;
1511935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                }
1521935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov
1531935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                File dexDir = new File(context.getFilesDir(), SECONDARY_FOLDER_NAME);
1541935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                List<File> files = MultiDexExtractor.load(context, applicationInfo, dexDir, false);
1551935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                if (checkValidZipFiles(files)) {
1561935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                    installSecondaryDexes(loader, dexDir, files);
1571935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                } else {
1581935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                    Log.w(TAG, "Files were not valid zip files.  Forcing a reload.");
1591935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                    // Try again, but this time force a reload of the zip file.
1601935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                    files = MultiDexExtractor.load(context, applicationInfo, dexDir, true);
1611935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                    if (checkValidZipFiles(files)) {
1621935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                        installSecondaryDexes(loader, dexDir, files);
1631935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                    } else {
1641935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                        // Second time didn't work, give up
1651935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                        throw new RuntimeException("Zip files were not valid.");
16644e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                    }
16744e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                }
16844e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            }
16944e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn
17044e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn        } catch (Exception e) {
17144e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            Log.e(TAG, "Multidex installation failure", e);
17244e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            throw new RuntimeException("Multi dex installation failed (" + e.getMessage() + ").");
17344e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn        }
17444e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn    }
17544e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn
17644e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn    private static void installSecondaryDexes(ClassLoader loader, File dexDir, List<File> files)
17744e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException,
17844e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            InvocationTargetException, NoSuchMethodException, IOException {
17944e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn        if (!files.isEmpty()) {
18044e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            if (Build.VERSION.SDK_INT >= 19) {
18144e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                V19.install(loader, files, dexDir);
18244e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            } else if (Build.VERSION.SDK_INT >= 14) {
18344e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                V14.install(loader, files, dexDir);
18444e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            } else {
18544e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                V4.install(loader, files);
18644e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            }
18744e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn        }
18844e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn    }
18944e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn
19044e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn    /**
19144e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn     * Returns whether all files in the list are valid zip files.  If {@code files} is empty, then
19244e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn     * returns true.
19344e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn     */
19444e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn    private static boolean checkValidZipFiles(List<File> files) {
19544e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn        for (File file : files) {
19644e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            if (!MultiDexExtractor.verifyZipFile(file)) {
19744e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                return false;
19844e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            }
19944e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn        }
20044e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn        return true;
20144e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn    }
20244e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn
20344e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn    /**
20444e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn     * Locates a given field anywhere in the class inheritance hierarchy.
20544e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn     *
20644e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn     * @param instance an object to search the field into.
20744e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn     * @param name field name
20844e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn     * @return a field object
20944e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn     * @throws NoSuchFieldException if the field cannot be located
21044e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn     */
21144e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn    private static Field findField(Object instance, String name) throws NoSuchFieldException {
21244e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn        for (Class<?> clazz = instance.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
21344e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            try {
21444e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                Field field = clazz.getDeclaredField(name);
21544e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn
21644e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn
21744e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                if (!field.isAccessible()) {
21844e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                    field.setAccessible(true);
21944e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                }
22044e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn
22144e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                return field;
22244e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            } catch (NoSuchFieldException e) {
22344e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                // ignore and search next
22444e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            }
22544e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn        }
22644e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn
22744e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn        throw new NoSuchFieldException("Field " + name + " not found in " + instance.getClass());
22844e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn    }
22944e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn
23044e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn    /**
23144e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn     * Locates a given method anywhere in the class inheritance hierarchy.
23244e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn     *
23344e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn     * @param instance an object to search the method into.
23444e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn     * @param name method name
23544e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn     * @param parameterTypes method parameter types
23644e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn     * @return a method object
23744e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn     * @throws NoSuchMethodException if the method cannot be located
23844e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn     */
23944e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn    private static Method findMethod(Object instance, String name, Class<?>... parameterTypes)
24044e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            throws NoSuchMethodException {
24144e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn        for (Class<?> clazz = instance.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
24244e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            try {
24344e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                Method method = clazz.getDeclaredMethod(name, parameterTypes);
24444e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn
24544e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn
24644e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                if (!method.isAccessible()) {
24744e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                    method.setAccessible(true);
24844e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                }
24944e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn
2501935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                return method;
2511935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov            } catch (NoSuchMethodException e) {
2521935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                // ignore and search next
2531935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov            }
2541935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov        }
25544e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn
25644e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn        throw new NoSuchMethodException("Method " + name + " with parameters " +
25744e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                Arrays.asList(parameterTypes) + " not found in " + instance.getClass());
2581935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov    }
2591935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov
2601935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov    /**
2611935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov     * Replace the value of a field containing a non null array, by a new array containing the
2621935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov     * elements of the original array plus the elements of extraElements.
2631935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov     * @param instance the instance whose field is to be modified.
2641935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov     * @param fieldName the field to modify.
2651935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov     * @param extraElements elements to append at the end of the array.
2661935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov     */
2671935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov    private static void expandFieldArray(Object instance, String fieldName,
2681935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov            Object[] extraElements) throws NoSuchFieldException, IllegalArgumentException,
269fe32563fd610767a2d3eea8dbd96e6bae87739d5Svetoslav Ganov            IllegalAccessException {
2701935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov        Field jlrField = findField(instance, fieldName);
2711935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov        Object[] original = (Object[]) jlrField.get(instance);
2721935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov        Object[] combined = (Object[]) Array.newInstance(
2731935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                original.getClass().getComponentType(), original.length + extraElements.length);
2741935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov        System.arraycopy(original, 0, combined, 0, original.length);
2751935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov        System.arraycopy(extraElements, 0, combined, original.length, extraElements.length);
2761935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov        jlrField.set(instance, combined);
2771935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov    }
2781935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov
2791935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov    /**
28044e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn     * Installer for platform versions 19.
28144e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn     */
28244e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn    private static final class V19 {
28344e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn
28444e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn        private static void install(ClassLoader loader, List<File> additionalClassPathEntries,
28571249413434c56006d2cb5d689198a2814c8e6b7Dianne Hackborn                File optimizedDirectory)
28644e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                        throws IllegalArgumentException, IllegalAccessException,
28744e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                        NoSuchFieldException, InvocationTargetException, NoSuchMethodException {
28844e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            /* The patched class loader is expected to be a descendant of
28944e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn             * dalvik.system.BaseDexClassLoader. We modify its
29044e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn             * dalvik.system.DexPathList pathList field to append additional DEX
29144e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn             * file entries.
29244e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn             */
29344e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            Field pathListField = findField(loader, "pathList");
29444e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            Object dexPathList = pathListField.get(loader);
29544e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
29644e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            expandFieldArray(dexPathList, "dexElements", makeDexElements(dexPathList,
29744e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                    new ArrayList<File>(additionalClassPathEntries), optimizedDirectory,
29844e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                    suppressedExceptions));
29944e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            if (suppressedExceptions.size() > 0) {
30044e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                for (IOException e : suppressedExceptions) {
30144e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                    Log.w(TAG, "Exception in makeDexElement", e);
30244e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                }
30344e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                Field suppressedExceptionsField =
30444e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                        findField(loader, "dexElementsSuppressedExceptions");
30544e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                IOException[] dexElementsSuppressedExceptions =
30644e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                        (IOException[]) suppressedExceptionsField.get(loader);
30744e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn
30844e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                if (dexElementsSuppressedExceptions == null) {
30944e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                    dexElementsSuppressedExceptions =
31044e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                            suppressedExceptions.toArray(
31144e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                                    new IOException[suppressedExceptions.size()]);
31244e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                } else {
31344e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                    IOException[] combined =
31444e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                            new IOException[suppressedExceptions.size() +
31544e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                                            dexElementsSuppressedExceptions.length];
31644e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                    suppressedExceptions.toArray(combined);
31744e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                    System.arraycopy(dexElementsSuppressedExceptions, 0, combined,
31844e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                            suppressedExceptions.size(), dexElementsSuppressedExceptions.length);
31944e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                    dexElementsSuppressedExceptions = combined;
3201935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                }
3211935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov
3221935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                suppressedExceptionsField.set(loader, dexElementsSuppressedExceptions);
3231935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov            }
3241935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov        }
3251935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov
3261935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov        /**
3271935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov         * A wrapper around
3281935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov         * {@code private static final dalvik.system.DexPathList#makeDexElements}.
3291935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov         */
3301935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov        private static Object[] makeDexElements(
3311935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                Object dexPathList, ArrayList<File> files, File optimizedDirectory,
3321935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                ArrayList<IOException> suppressedExceptions)
3331935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                        throws IllegalAccessException, InvocationTargetException,
3341935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                        NoSuchMethodException {
3351935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov            Method makeDexElements =
3361935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                    findMethod(dexPathList, "makeDexElements", ArrayList.class, File.class,
3371935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                            ArrayList.class);
3381935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov
3391935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov            return (Object[]) makeDexElements.invoke(dexPathList, files, optimizedDirectory,
3401935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                    suppressedExceptions);
3411935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov        }
3421935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov    }
3431935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov
3441935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov    /**
3451935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov     * Installer for platform versions 14, 15, 16, 17 and 18.
3461935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov     */
3471935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov    private static final class V14 {
3481935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov
3491935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov        private static void install(ClassLoader loader, List<File> additionalClassPathEntries,
3501935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                File optimizedDirectory)
3511935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                        throws IllegalArgumentException, IllegalAccessException,
3521935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                        NoSuchFieldException, InvocationTargetException, NoSuchMethodException {
3531935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov            /* The patched class loader is expected to be a descendant of
3541935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov             * dalvik.system.BaseDexClassLoader. We modify its
3551935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov             * dalvik.system.DexPathList pathList field to append additional DEX
3561935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov             * file entries.
3571935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov             */
3581935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov            Field pathListField = findField(loader, "pathList");
3591935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov            Object dexPathList = pathListField.get(loader);
3601935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov            expandFieldArray(dexPathList, "dexElements", makeDexElements(dexPathList,
3611935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov                    new ArrayList<File>(additionalClassPathEntries), optimizedDirectory));
3621935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov        }
3631935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov
3641935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov        /**
3651935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov         * A wrapper around
3661935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov         * {@code private static final dalvik.system.DexPathList#makeDexElements}.
3671935ed3af7c6545bc38adfdc6026d87a3249222fSvetoslav Ganov         */
36844e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn        private static Object[] makeDexElements(
36944e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                Object dexPathList, ArrayList<File> files, File optimizedDirectory)
37044e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                        throws IllegalAccessException, InvocationTargetException,
37144e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                        NoSuchMethodException {
37244e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            Method makeDexElements =
37344e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                    findMethod(dexPathList, "makeDexElements", ArrayList.class, File.class);
37444e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn
37544e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            return (Object[]) makeDexElements.invoke(dexPathList, files, optimizedDirectory);
37644e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn        }
37744e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn    }
37844e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn
37944e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn    /**
38044e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn     * Installer for platform versions 4 to 13.
38144e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn     */
38244e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn    private static final class V4 {
38344e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn        private static void install(ClassLoader loader, List<File> additionalClassPathEntries)
38444e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                        throws IllegalArgumentException, IllegalAccessException,
38544e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                        NoSuchFieldException, IOException {
38644e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            /* The patched class loader is expected to be a descendant of
38744e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn             * dalvik.system.DexClassLoader. We modify its
38844e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn             * fields mPaths, mFiles, mZips and mDexs to append additional DEX
38944e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn             * file entries.
39044e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn             */
39144e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            int extraSize = additionalClassPathEntries.size();
39244e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn
39344e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            Field pathField = findField(loader, "path");
39444e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn
39544e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            StringBuilder path = new StringBuilder((String) pathField.get(loader));
39644e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            String[] extraPaths = new String[extraSize];
39744e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            File[] extraFiles = new File[extraSize];
39844e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            ZipFile[] extraZips = new ZipFile[extraSize];
39944e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            DexFile[] extraDexs = new DexFile[extraSize];
40044e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            for (ListIterator<File> iterator = additionalClassPathEntries.listIterator();
40144e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                    iterator.hasNext();) {
40244e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                File additionalEntry = iterator.next();
40344e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                String entryPath = additionalEntry.getAbsolutePath();
40444e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                path.append(':').append(entryPath);
40544e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                int index = iterator.previousIndex();
40644e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                extraPaths[index] = entryPath;
40744e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                extraFiles[index] = additionalEntry;
40844e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                extraZips[index] = new ZipFile(additionalEntry);
40944e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn                extraDexs[index] = DexFile.loadDex(entryPath, entryPath + ".dex", 0);
41044e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            }
41144e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn
41244e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            pathField.set(loader, path.toString());
41344e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            expandFieldArray(loader, "mPaths", extraPaths);
41444e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            expandFieldArray(loader, "mFiles", extraFiles);
41544e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            expandFieldArray(loader, "mZips", extraZips);
41644e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn            expandFieldArray(loader, "mDexs", extraDexs);
41744e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn        }
41844e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn    }
41944e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn
42044e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn}
42144e3a52be44809a8018fb170e3130cc0ae164366Dianne Hackborn