DexPathList.java revision 1a796cbc5dfb263511f2f4e5213adbc1d396a813
12cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom/*
22cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * Copyright (C) 2011 The Android Open Source Project
32cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom *
42cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * Licensed under the Apache License, Version 2.0 (the "License");
52cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * you may not use this file except in compliance with the License.
62cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * You may obtain a copy of the License at
72cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom *
82cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom *      http://www.apache.org/licenses/LICENSE-2.0
92cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom *
102cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * Unless required by applicable law or agreed to in writing, software
112cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * distributed under the License is distributed on an "AS IS" BASIS,
122cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * See the License for the specific language governing permissions and
142cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * limitations under the License.
152cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom */
162cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
172cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrompackage dalvik.system;
182cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
195d930cadc8f62aee5f18e7921296fe66a54f18abElliott Hughesimport android.system.ErrnoException;
205d930cadc8f62aee5f18e7921296fe66a54f18abElliott Hughesimport android.system.StructStat;
212cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstromimport java.io.File;
221a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fullerimport java.io.FilterInputStream;
232cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstromimport java.io.IOException;
241a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fullerimport java.io.InputStream;
251a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fullerimport java.net.JarURLConnection;
262cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstromimport java.net.MalformedURLException;
271a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fullerimport java.net.URI;
282cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstromimport java.net.URL;
291a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fullerimport java.net.URLConnection;
301a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fullerimport java.net.URLStreamHandler;
312cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstromimport java.util.ArrayList;
322cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstromimport java.util.Arrays;
332cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstromimport java.util.Collections;
342cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstromimport java.util.Enumeration;
352cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstromimport java.util.List;
361a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fullerimport java.util.jar.JarFile;
371a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fullerimport java.util.zip.ZipEntry;
382cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstromimport java.util.zip.ZipFile;
392cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstromimport libcore.io.IoUtils;
402cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstromimport libcore.io.Libcore;
415d930cadc8f62aee5f18e7921296fe66a54f18abElliott Hughesimport static android.system.OsConstants.*;
422cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
432cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom/**
442cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * A pair of lists of entries, associated with a {@code ClassLoader}.
452cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * One of the lists is a dex/resource path — typically referred
462cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * to as a "class path" — list, and the other names directories
472cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * containing native code libraries. Class path entries may be any of:
482cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * a {@code .jar} or {@code .zip} file containing an optional
492cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * top-level {@code classes.dex} file as well as arbitrary resources,
502cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * or a plain {@code .dex} file (with no possibility of associated
512cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * resources).
522cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom *
532cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * <p>This class also contains methods to use these lists to look up
542cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * classes and resources.</p>
552cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom */
562cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom/*package*/ final class DexPathList {
572cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    private static final String DEX_SUFFIX = ".dex";
582cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
592cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /** class definition context */
602cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    private final ClassLoader definingContext;
612cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
627694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov    /** List of dexfiles. */
637694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov    private final List<File> dexFiles;
647694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov
652cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
662cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * List of dex/resource (class path) elements.
672cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Should be called pathElements, but the Facebook app uses reflection
682cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * to modify 'dexElements' (http://b/7726934).
692cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
702cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    private final Element[] dexElements;
712cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
722cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /** List of native library directories. */
737694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov    private final List<File> nativeLibraryDirectories;
742cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
752cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
761e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers     * Exceptions thrown during creation of the dexElements list.
771e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers     */
781e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers    private final IOException[] dexElementsSuppressedExceptions;
791e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers
801e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers    /**
812cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Constructs an instance.
822cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     *
832cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * @param definingContext the context in which any as-yet unresolved
842cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * classes should be defined
852cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * @param dexPath list of dex/resource path elements, separated by
862cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * {@code File.pathSeparator}
872cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * @param libraryPath list of native library directory path elements,
882cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * separated by {@code File.pathSeparator}
892cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * @param optimizedDirectory directory where optimized {@code .dex} files
902cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * should be found and written to, or {@code null} to use the default
912cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * system directory for same
922cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
932cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    public DexPathList(ClassLoader definingContext, String dexPath,
942cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            String libraryPath, File optimizedDirectory) {
952cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        if (definingContext == null) {
962cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            throw new NullPointerException("definingContext == null");
972cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
982cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
992cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        if (dexPath == null) {
1002cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            throw new NullPointerException("dexPath == null");
1012cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
1022cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
1032cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        if (optimizedDirectory != null) {
1042cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            if (!optimizedDirectory.exists())  {
1052cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                throw new IllegalArgumentException(
1062cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                        "optimizedDirectory doesn't exist: "
1072cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                        + optimizedDirectory);
1082cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
1092cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
1102cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            if (!(optimizedDirectory.canRead()
1112cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                            && optimizedDirectory.canWrite())) {
1122cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                throw new IllegalArgumentException(
1132cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                        "optimizedDirectory not readable/writable: "
1142cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                        + optimizedDirectory);
1152cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
1162cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
1172cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
1182cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        this.definingContext = definingContext;
1191e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers        ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
1207694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov        // save dexPath for BaseDexClassLoader
1217694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov        this.dexFiles = splitDexPath(dexPath);
1227694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov        this.dexElements = makeDexElements(dexFiles, optimizedDirectory,
1231e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers                                           suppressedExceptions);
1241e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers        if (suppressedExceptions.size() > 0) {
1251e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers            this.dexElementsSuppressedExceptions =
1261e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers                suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]);
1271e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers        } else {
1281e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers            dexElementsSuppressedExceptions = null;
1291e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers        }
1302cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        this.nativeLibraryDirectories = splitLibraryPath(libraryPath);
1312cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
1322cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
1332cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    @Override public String toString() {
1347694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov        File[] nativeLibraryDirectoriesArray =
1357694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov                nativeLibraryDirectories.toArray(new File[nativeLibraryDirectories.size()]);
1367694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov
1372cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        return "DexPathList[" + Arrays.toString(dexElements) +
1387694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov            ",nativeLibraryDirectories=" + Arrays.toString(nativeLibraryDirectoriesArray) + "]";
1392cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
1402cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
1412cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
1422cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * For BaseDexClassLoader.getLdLibraryPath.
1432cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
1447694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov    public List<File> getNativeLibraryDirectories() {
1452cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        return nativeLibraryDirectories;
1462cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
1472cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
1482cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
1497694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov     * For BaseDexClassLoader.getDexPath.
1507694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov     */
1517694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov    public List<File> getDexFiles() {
1527694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov        return dexFiles;
1537694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov    }
1547694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov
1557694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov    /**
1562cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Splits the given dex path string into elements using the path
1572cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * separator, pruning out any elements that do not refer to existing
1582cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * and readable files. (That is, directories are not included in the
1592cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * result.)
1602cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
1617694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov    private static List<File> splitDexPath(String path) {
1622cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        return splitPaths(path, null, false);
1632cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
1642cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
1652cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
1662cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Splits the given library directory path string into elements
1672cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * using the path separator ({@code File.pathSeparator}, which
1682cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * defaults to {@code ":"} on Android, appending on the elements
1692cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * from the system library path, and pruning out any elements that
1702cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * do not refer to existing and readable directories.
1712cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
1727694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov    private static List<File> splitLibraryPath(String path) {
173413d4592ee114eac81014af4b6347e73873ce8ceElliott Hughes        // Native libraries may exist in both the system and
174413d4592ee114eac81014af4b6347e73873ce8ceElliott Hughes        // application library paths, and we use this search order:
175413d4592ee114eac81014af4b6347e73873ce8ceElliott Hughes        //
176413d4592ee114eac81014af4b6347e73873ce8ceElliott Hughes        //   1. this class loader's library path for application libraries
177413d4592ee114eac81014af4b6347e73873ce8ceElliott Hughes        //   2. the VM's library path from the system property for system libraries
178413d4592ee114eac81014af4b6347e73873ce8ceElliott Hughes        //
179413d4592ee114eac81014af4b6347e73873ce8ceElliott Hughes        // This order was reversed prior to Gingerbread; see http://b/2933456.
1807694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov        return splitPaths(path, System.getProperty("java.library.path"), true);
1812cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
1822cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
1832cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
1842cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Splits the given path strings into file elements using the path
1852cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * separator, combining the results and filtering out elements
1862cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * that don't exist, aren't readable, or aren't either a regular
1872cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * file or a directory (as specified). Either string may be empty
1882cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * or {@code null}, in which case it is ignored. If both strings
1892cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * are empty or {@code null}, or all elements get pruned out, then
1902cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * this returns a zero-element list.
1912cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
1927694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov    private static List<File> splitPaths(String path1, String path2, boolean wantDirectories) {
1937694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov        List<File> result = new ArrayList<File>();
1942cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
1952cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        splitAndAdd(path1, wantDirectories, result);
1962cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        splitAndAdd(path2, wantDirectories, result);
1972cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        return result;
1982cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
1992cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
2002cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
2012cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Helper for {@link #splitPaths}, which does the actual splitting
2022cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * and filtering and adding to a result.
2032cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
2042cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    private static void splitAndAdd(String searchPath, boolean directoriesOnly,
2057694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov            List<File> resultList) {
2062cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        if (searchPath == null) {
2072cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            return;
2082cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
2092cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        for (String path : searchPath.split(":")) {
2102cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            try {
2112cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                StructStat sb = Libcore.os.stat(path);
2122cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                if (!directoriesOnly || S_ISDIR(sb.st_mode)) {
2132cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                    resultList.add(new File(path));
2142cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                }
2152cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            } catch (ErrnoException ignored) {
2162cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
2172cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
2182cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
2192cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
2202cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
2212cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Makes an array of dex/resource path elements, one per element of
2222cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * the given array.
2232cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
2247694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov    private static Element[] makeDexElements(List<File> files, File optimizedDirectory,
2257694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov                                             List<IOException> suppressedExceptions) {
2262cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        ArrayList<Element> elements = new ArrayList<Element>();
2272cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        /*
2282cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom         * Open all files and load the (direct or contained) dex files
2292cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom         * up front.
2302cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom         */
2312cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        for (File file : files) {
2322cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            File zip = null;
2332cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            DexFile dex = null;
2342cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            String name = file.getName();
2352cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
236bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu            if (file.isDirectory()) {
2372cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                // We support directories for looking up resources.
2382cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                // This is only useful for running libcore tests.
2392cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                elements.add(new Element(file, true, null, null));
240bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu            } else if (file.isFile()){
241bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu                if (name.endsWith(DEX_SUFFIX)) {
242bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu                    // Raw dex file (not inside a zip/jar).
243bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu                    try {
244bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu                        dex = loadDexFile(file, optimizedDirectory);
245bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu                    } catch (IOException ex) {
246bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu                        System.logE("Unable to load dex file: " + file, ex);
247bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu                    }
248bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu                } else {
249bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu                    zip = file;
250bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu
251bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu                    try {
252bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu                        dex = loadDexFile(file, optimizedDirectory);
253bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu                    } catch (IOException suppressed) {
254bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu                        /*
255bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu                         * IOException might get thrown "legitimately" by the DexFile constructor if
256bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu                         * the zip file turns out to be resource-only (that is, no classes.dex file
257bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu                         * in it).
258bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu                         * Let dex == null and hang on to the exception to add to the tea-leaves for
259bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu                         * when findClass returns null.
260bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu                         */
261bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu                        suppressedExceptions.add(suppressed);
262bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu                    }
263bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu                }
2642cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            } else {
265bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu                System.logW("ClassLoader referenced unknown path: " + file);
2662cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
2672cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
2682cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            if ((zip != null) || (dex != null)) {
2692cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                elements.add(new Element(file, false, zip, dex));
2702cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
2712cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
2722cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
2732cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        return elements.toArray(new Element[elements.size()]);
2742cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
2752cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
2762cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
2772cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Constructs a {@code DexFile} instance, as appropriate depending
2782cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * on whether {@code optimizedDirectory} is {@code null}.
2792cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
2802cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    private static DexFile loadDexFile(File file, File optimizedDirectory)
2812cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            throws IOException {
2822cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        if (optimizedDirectory == null) {
2832cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            return new DexFile(file);
2842cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        } else {
2852cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            String optimizedPath = optimizedPathFor(file, optimizedDirectory);
2862cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            return DexFile.loadDex(file.getPath(), optimizedPath, 0);
2872cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
2882cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
2892cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
2902cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
2912cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Converts a dex/jar file path and an output directory to an
292ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom     * output file path for an associated optimized dex file.
2932cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
2942cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    private static String optimizedPathFor(File path,
2952cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            File optimizedDirectory) {
296ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom        /*
297ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * Get the filename component of the path, and replace the
298ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * suffix with ".dex" if that's not already the suffix.
299ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         *
300ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * We don't want to use ".odex", because the build system uses
301ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * that for files that are paired with resource-only jar
302ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * files. If the VM can assume that there's no classes.dex in
303ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * the matching jar, it doesn't need to open the jar to check
304ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * for updated dependencies, providing a slight performance
305ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * boost at startup. The use of ".dex" here matches the use on
306ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * files in /data/dalvik-cache.
307ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         */
308ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom        String fileName = path.getName();
309ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom        if (!fileName.endsWith(DEX_SUFFIX)) {
310ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom            int lastDot = fileName.lastIndexOf(".");
311ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom            if (lastDot < 0) {
312ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom                fileName += DEX_SUFFIX;
313ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom            } else {
314ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom                StringBuilder sb = new StringBuilder(lastDot + 4);
315ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom                sb.append(fileName, 0, lastDot);
316ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom                sb.append(DEX_SUFFIX);
317ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom                fileName = sb.toString();
318ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom            }
319ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom        }
320ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom
3212cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        File result = new File(optimizedDirectory, fileName);
3222cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        return result.getPath();
3232cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
3242cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
3252cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
3262cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Finds the named class in one of the dex files pointed at by
3272cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * this instance. This will find the one in the earliest listed
3282cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * path element. If the class is found but has not yet been
3292cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * defined, then this method will define it in the defining
3302cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * context that this instance was constructed with.
3312cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     *
3322cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * @param name of class to find
3332cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * @param suppressed exceptions encountered whilst finding the class
3342cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * @return the named class or {@code null} if the class is not
3352cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * found in any of the dex files
3362cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
3372cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    public Class findClass(String name, List<Throwable> suppressed) {
3382cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        for (Element element : dexElements) {
3392cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            DexFile dex = element.dexFile;
3402cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
3412cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            if (dex != null) {
3422cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);
3432cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                if (clazz != null) {
3442cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                    return clazz;
3452cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                }
3462cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
3472cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
3481e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers        if (dexElementsSuppressedExceptions != null) {
3491e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers            suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
3501e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers        }
3512cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        return null;
3522cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
3532cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
3542cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
3552cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Finds the named resource in one of the zip/jar files pointed at
3562cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * by this instance. This will find the one in the earliest listed
3572cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * path element.
3582cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     *
3592cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * @return a URL to the named resource or {@code null} if the
3602cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * resource is not found in any of the zip/jar files
3612cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
3622cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    public URL findResource(String name) {
3632cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        for (Element element : dexElements) {
3642cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            URL url = element.findResource(name);
3652cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            if (url != null) {
3662cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                return url;
3672cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
3682cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
3692cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
3702cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        return null;
3712cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
3722cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
3732cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
3742cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Finds all the resources with the given name, returning an
3752cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * enumeration of them. If there are no resources with the given
3762cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * name, then this method returns an empty enumeration.
3772cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
3782cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    public Enumeration<URL> findResources(String name) {
3792cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        ArrayList<URL> result = new ArrayList<URL>();
3802cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
3812cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        for (Element element : dexElements) {
3822cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            URL url = element.findResource(name);
3832cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            if (url != null) {
3842cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                result.add(url);
3852cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
3862cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
3872cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
3882cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        return Collections.enumeration(result);
3892cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
3902cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
3912cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
3922cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Finds the named native code library on any of the library
3932cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * directories pointed at by this instance. This will find the
3942cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * one in the earliest listed directory, ignoring any that are not
3952cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * readable regular files.
3962cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     *
3972cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * @return the complete path to the library or {@code null} if no
3982cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * library was found
3992cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
4002cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    public String findLibrary(String libraryName) {
4012cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        String fileName = System.mapLibraryName(libraryName);
4022cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        for (File directory : nativeLibraryDirectories) {
4032cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            String path = new File(directory, fileName).getPath();
4042cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            if (IoUtils.canOpenReadOnly(path)) {
4052cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                return path;
4062cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
4072cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
4082cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        return null;
4092cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
4102cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
4112cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
4122cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Element of the dex/resource file path
4132cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
4142cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /*package*/ static class Element {
4152cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        private final File file;
4162cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        private final boolean isDirectory;
4172cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        private final File zip;
4182cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        private final DexFile dexFile;
4192cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
4202cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        private ZipFile zipFile;
4211a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller        private URLStreamHandler urlHandler;
4222cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        private boolean initialized;
4232cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
4242cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        public Element(File file, boolean isDirectory, File zip, DexFile dexFile) {
4252cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            this.file = file;
4262cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            this.isDirectory = isDirectory;
4272cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            this.zip = zip;
4282cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            this.dexFile = dexFile;
4292cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
4302cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
4312cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        @Override public String toString() {
4322cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            if (isDirectory) {
4332cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                return "directory \"" + file + "\"";
4342cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            } else if (zip != null) {
4352cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                return "zip file \"" + zip + "\"";
4362cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            } else {
4372cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                return "dex file \"" + dexFile + "\"";
4382cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
4392cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
4402cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
4412cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        public synchronized void maybeInit() {
4422cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            if (initialized) {
4432cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                return;
4442cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
4452cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
4462cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            initialized = true;
4472cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
4482cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            if (isDirectory || zip == null) {
4492cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                return;
4502cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
4512cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
4522cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            try {
4532cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                zipFile = new ZipFile(zip);
4541a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                urlHandler = new ElementURLStreamHandler(zipFile);
4552cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            } catch (IOException ioe) {
4562cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                /*
4572cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                 * Note: ZipException (a subclass of IOException)
4582cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                 * might get thrown by the ZipFile constructor
4592cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                 * (e.g. if the file isn't actually a zip/jar
4602cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                 * file).
4612cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                 */
4622cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                System.logE("Unable to open zip file: " + file, ioe);
4632cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                zipFile = null;
4642cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
4652cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
4662cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
4672cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        public URL findResource(String name) {
4682cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            maybeInit();
4692cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
4702cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            // We support directories so we can run tests and/or legacy code
4712cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            // that uses Class.getResource.
4722cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            if (isDirectory) {
4732cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                File resourceFile = new File(file, name);
4742cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                if (resourceFile.exists()) {
4752cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                    try {
4762cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                        return resourceFile.toURI().toURL();
4772cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                    } catch (MalformedURLException ex) {
4782cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                        throw new RuntimeException(ex);
4792cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                    }
4802cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                }
4812cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
4822cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
4832cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            if (zipFile == null || zipFile.getEntry(name) == null) {
4842cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                /*
4852cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                 * Either this element has no zip/jar file (first
4862cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                 * clause), or the zip/jar file doesn't have an entry
4872cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                 * for the given name (second clause).
4882cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                 */
4892cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                return null;
4902cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
4912cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
4922cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            try {
4932cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                /*
4941a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                 * File.toURI() is compliant with RFC 1738 in
4952cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                 * always creating absolute path names. If we
4962cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                 * construct the URL by concatenating strings, we
4972cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                 * might end up with illegal URLs for relative
4982cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                 * names.
4992cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                 */
5001a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                URI fileUri = file.toURI();
5011a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                return new URL("jar", null, -1, fileUri.toString() + "!/" + name, urlHandler);
5022cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            } catch (MalformedURLException ex) {
5032cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                throw new RuntimeException(ex);
5042cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
5052cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
5061a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller
5071a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller        /**
5081a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller         * URLStreamHandler for an Element. Avoids the need to open a .jar file again to read
5091a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller         * resources.
5101a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller         */
5111a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller        private static class ElementURLStreamHandler extends URLStreamHandler {
5121a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller
5131a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller            private final ZipFile zipFile;
5141a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller
5151a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller            public ElementURLStreamHandler(ZipFile zipFile) {
5161a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                this.zipFile = zipFile;
5171a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller            }
5181a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller
5191a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller            @Override
5201a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller            protected URLConnection openConnection(URL url) throws IOException {
5211a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                return new ElementJarURLConnection(url, zipFile);
5221a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller            }
5231a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller        }
5241a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller
5251a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller        /**
5261a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller         * A JarURLConnection that is backed by a ZipFile held open by an {@link Element}. For
5271a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller         * backwards compatibility it extends JarURLConnection even though it's not actually backed
5281a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller         * by a {@link JarFile}.
5291a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller         */
5301a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller        private static class ElementJarURLConnection extends JarURLConnection {
5311a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller
5321a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller            private final ZipFile zipFile;
5331a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller            private final ZipEntry zipEntry;
5341a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller
5351a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller            private InputStream jarInput;
5361a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller            private boolean closed;
5371a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller            private JarFile jarFile;
5381a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller
5391a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller            public ElementJarURLConnection(URL url, ZipFile zipFile) throws MalformedURLException {
5401a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                super(url);
5411a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                this.zipFile = zipFile;
5421a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                this.zipEntry = zipFile.getEntry(getEntryName());
5431a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                if (zipEntry == null) {
5441a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                    throw new MalformedURLException(
5451a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                            "URL does not correspond to an entry in the zip file. URL=" + url
5461a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                                    + ", zipfile=" + zipFile.getName());
5471a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                }
5481a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller            }
5491a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller
5501a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller            @Override
5511a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller            public void connect() {
5521a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                connected = true;
5531a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller            }
5541a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller
5551a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller            @Override
5561a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller            public JarFile getJarFile() throws IOException {
5571a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                // This is expensive because we only pretend that we wrap a JarFile.
5581a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                if (jarFile == null) {
5591a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                    jarFile = new JarFile(zipFile.getName());
5601a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                }
5611a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                return jarFile;
5621a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller            }
5631a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller
5641a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller            @Override
5651a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller            public InputStream getInputStream() throws IOException {
5661a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                if (closed) {
5671a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                    throw new IllegalStateException("JarURLConnection InputStream has been closed");
5681a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                }
5691a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                connect();
5701a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                if (jarInput != null) {
5711a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                    return jarInput;
5721a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                }
5731a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                return jarInput = new FilterInputStream(zipFile.getInputStream(zipEntry)) {
5741a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                    @Override
5751a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                    public void close() throws IOException {
5761a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                        super.close();
5771a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                        closed = true;
5781a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                    }
5791a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                };
5801a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller            }
5811a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller
5821a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller            /**
5831a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller             * Returns the content type of the entry based on the name of the entry. Returns
5841a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller             * non-null results ("content/unknown" for unknown types).
5851a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller             *
5861a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller             * @return the content type
5871a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller             */
5881a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller            @Override
5891a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller            public String getContentType() {
5901a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                String cType = guessContentTypeFromName(getEntryName());
5911a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                if (cType == null) {
5921a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                    cType = "content/unknown";
5931a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                }
5941a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                return cType;
5951a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller            }
5961a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller
5971a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller            @Override
5981a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller            public int getContentLength() {
5991a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                connect();
6001a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller                return (int) zipEntry.getSize();
6011a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller            }
6021a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller        }
6032cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
6042cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom}
605