DexPathList.java revision 19c3551836aedca51e7e016007efca18d030763b
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;
222cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstromimport java.io.IOException;
232cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstromimport java.net.MalformedURLException;
242cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstromimport java.net.URL;
252cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstromimport java.util.ArrayList;
262cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstromimport java.util.Arrays;
272cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstromimport java.util.Collections;
282cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstromimport java.util.Enumeration;
292cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstromimport java.util.List;
30384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanovimport java.util.zip.ZipEntry;
312cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstromimport libcore.io.IoUtils;
322cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstromimport libcore.io.Libcore;
332ce899fcb81707dd5447a15c29c2c137697f2f5eNeil Fullerimport libcore.io.ClassPathURLStreamHandler;
342ce899fcb81707dd5447a15c29c2c137697f2f5eNeil Fuller
352ce899fcb81707dd5447a15c29c2c137697f2f5eNeil Fullerimport static android.system.OsConstants.S_ISDIR;
362cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
372cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom/**
382cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * A pair of lists of entries, associated with a {@code ClassLoader}.
392cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * One of the lists is a dex/resource path — typically referred
402cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * to as a "class path" — list, and the other names directories
412cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * containing native code libraries. Class path entries may be any of:
422cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * a {@code .jar} or {@code .zip} file containing an optional
432cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * top-level {@code classes.dex} file as well as arbitrary resources,
442cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * or a plain {@code .dex} file (with no possibility of associated
452cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * resources).
462cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom *
472cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * <p>This class also contains methods to use these lists to look up
482cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * classes and resources.</p>
492cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom */
502cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom/*package*/ final class DexPathList {
512cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    private static final String DEX_SUFFIX = ".dex";
52384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov    private static final String zipSeparator = "!/";
532cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
542cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /** class definition context */
552cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    private final ClassLoader definingContext;
562cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
572cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
582cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * List of dex/resource (class path) elements.
592cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Should be called pathElements, but the Facebook app uses reflection
602cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * to modify 'dexElements' (http://b/7726934).
612cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
62104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy    private Element[] dexElements;
632cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
64384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov    /** List of native library path elements. */
65384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov    private final Element[] nativeLibraryPathElements;
66384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
67384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov    /** List of application native library directories. */
687694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov    private final List<File> nativeLibraryDirectories;
692cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
70384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov    /** List of system native library directories. */
71384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov    private final List<File> systemNativeLibraryDirectories;
72384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
732cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
741e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers     * Exceptions thrown during creation of the dexElements list.
751e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers     */
76104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy    private IOException[] dexElementsSuppressedExceptions;
771e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers
781e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers    /**
792cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Constructs an instance.
802cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     *
812cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * @param definingContext the context in which any as-yet unresolved
822cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * classes should be defined
832cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * @param dexPath list of dex/resource path elements, separated by
842cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * {@code File.pathSeparator}
85a2656629522f9d79e2dca7418ab5963f50d0fda8Dimitry Ivanov     * @param librarySearchPath list of native library directory path elements,
862cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * separated by {@code File.pathSeparator}
87a2656629522f9d79e2dca7418ab5963f50d0fda8Dimitry Ivanov     * @param libraryPermittedPath is path containing permitted directories for
88a2656629522f9d79e2dca7418ab5963f50d0fda8Dimitry Ivanov     * linker isolated namespaces (in addition to librarySearchPath which is allowed
89a2656629522f9d79e2dca7418ab5963f50d0fda8Dimitry Ivanov     * implicitly). Note that this path does not affect the search order for the library
90a2656629522f9d79e2dca7418ab5963f50d0fda8Dimitry Ivanov     * and intended for white-listing additional paths when loading native libraries
91a2656629522f9d79e2dca7418ab5963f50d0fda8Dimitry Ivanov     * by absolute path.
922cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * @param optimizedDirectory directory where optimized {@code .dex} files
932cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * should be found and written to, or {@code null} to use the default
942cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * system directory for same
952cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
962cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    public DexPathList(ClassLoader definingContext, String dexPath,
9719c3551836aedca51e7e016007efca18d030763bDimitry Ivanov            String librarySearchPath, File optimizedDirectory) {
98384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
992cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        if (definingContext == null) {
1002cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            throw new NullPointerException("definingContext == null");
1012cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
1022cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
1032cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        if (dexPath == null) {
1042cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            throw new NullPointerException("dexPath == null");
1052cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
1062cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
1072cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        if (optimizedDirectory != null) {
1082cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            if (!optimizedDirectory.exists())  {
1092cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                throw new IllegalArgumentException(
1102cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                        "optimizedDirectory doesn't exist: "
1112cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                        + optimizedDirectory);
1122cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
1132cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
1142cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            if (!(optimizedDirectory.canRead()
1152cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                            && optimizedDirectory.canWrite())) {
1162cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                throw new IllegalArgumentException(
1172cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                        "optimizedDirectory not readable/writable: "
1182cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                        + optimizedDirectory);
1192cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
1202cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
1212cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
1222cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        this.definingContext = definingContext;
123384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
1241e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers        ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
1257694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov        // save dexPath for BaseDexClassLoader
126384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
12770fcc38e7a03f04864499a7356ae1f1e692b7463Mathieu Chartier                                           suppressedExceptions, definingContext);
128384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
129384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        // Native libraries may exist in both the system and
130384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        // application library paths, and we use this search order:
131384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        //
132a2656629522f9d79e2dca7418ab5963f50d0fda8Dimitry Ivanov        //   1. This class loader's library path for application libraries (librarySearchPath):
133384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        //   1.1. Native library directories
134384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        //   1.2. Path to libraries in apk-files
135384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        //   2. The VM's library path from the system property for system libraries
136384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        //      also known as java.library.path
137384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        //
138384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        // This order was reversed prior to Gingerbread; see http://b/2933456.
139a2656629522f9d79e2dca7418ab5963f50d0fda8Dimitry Ivanov        this.nativeLibraryDirectories = splitPaths(librarySearchPath, false);
140384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        this.systemNativeLibraryDirectories =
141384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                splitPaths(System.getProperty("java.library.path"), true);
142384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        List<File> allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories);
143384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);
144384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
145384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        this.nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories,
14670fcc38e7a03f04864499a7356ae1f1e692b7463Mathieu Chartier                                                          suppressedExceptions,
14770fcc38e7a03f04864499a7356ae1f1e692b7463Mathieu Chartier                                                          definingContext);
148384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
1491e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers        if (suppressedExceptions.size() > 0) {
1501e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers            this.dexElementsSuppressedExceptions =
1511e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers                suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]);
1521e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers        } else {
1531e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers            dexElementsSuppressedExceptions = null;
1541e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers        }
1552cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
1562cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
1572cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    @Override public String toString() {
158384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        List<File> allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories);
159384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);
160384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
1617694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov        File[] nativeLibraryDirectoriesArray =
162384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                allNativeLibraryDirectories.toArray(
163384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                    new File[allNativeLibraryDirectories.size()]);
1647694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov
1652cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        return "DexPathList[" + Arrays.toString(dexElements) +
1667694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov            ",nativeLibraryDirectories=" + Arrays.toString(nativeLibraryDirectoriesArray) + "]";
1672cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
1682cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
1692cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
1702cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * For BaseDexClassLoader.getLdLibraryPath.
1712cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
1727694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov    public List<File> getNativeLibraryDirectories() {
1732cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        return nativeLibraryDirectories;
1742cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
1752cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
1762cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
177104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy     * Adds a new path to this instance
178104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy     * @param dexPath list of dex/resource path element, separated by
179104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy     * {@code File.pathSeparator}
180104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy     * @param optimizedDirectory directory where optimized {@code .dex} files
181104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy     * should be found and written to, or {@code null} to use the default
182104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy     * system directory for same
183104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy     */
184104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy    public void addDexPath(String dexPath, File optimizedDirectory) {
185104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy        final List<IOException> suppressedExceptionList = new ArrayList<IOException>();
186104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy        final Element[] newElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
187104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                suppressedExceptionList, definingContext);
188104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy
189104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy        if (newElements != null && newElements.length > 0) {
190104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy            final Element[] oldElements = dexElements;
191104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy            dexElements = new Element[oldElements.length + newElements.length];
192104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy            System.arraycopy(
193104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                    oldElements, 0, dexElements, 0, oldElements.length);
194104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy            System.arraycopy(
195104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                    newElements, 0, dexElements, oldElements.length, newElements.length);
196104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy        }
197104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy
198104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy        if (suppressedExceptionList.size() > 0) {
199104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy            final IOException[] newSuppressedExceptions = suppressedExceptionList.toArray(
200104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                    new IOException[suppressedExceptionList.size()]);
201104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy            if (dexElementsSuppressedExceptions != null) {
202104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                final IOException[] oldSuppressedExceptions = dexElementsSuppressedExceptions;
203104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                final int suppressedExceptionsLength = oldSuppressedExceptions.length +
204104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                        newSuppressedExceptions.length;
205104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                dexElementsSuppressedExceptions = new IOException[suppressedExceptionsLength];
206104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                System.arraycopy(oldSuppressedExceptions, 0, dexElementsSuppressedExceptions,
207104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                        0, oldSuppressedExceptions.length);
208104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                System.arraycopy(newSuppressedExceptions, 0, dexElementsSuppressedExceptions,
209104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                        oldSuppressedExceptions.length, newSuppressedExceptions.length);
210104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy            } else {
211104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                dexElementsSuppressedExceptions = newSuppressedExceptions;
212104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy            }
213104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy        }
214104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy    }
215104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy
216104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy    /**
2172cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Splits the given dex path string into elements using the path
2182cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * separator, pruning out any elements that do not refer to existing
219384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov     * and readable files.
2202cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
2217694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov    private static List<File> splitDexPath(String path) {
222384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        return splitPaths(path, false);
2232cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
2242cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
2252cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
2262cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Splits the given path strings into file elements using the path
2272cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * separator, combining the results and filtering out elements
2282cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * that don't exist, aren't readable, or aren't either a regular
2292cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * file or a directory (as specified). Either string may be empty
2302cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * or {@code null}, in which case it is ignored. If both strings
2312cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * are empty or {@code null}, or all elements get pruned out, then
2322cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * this returns a zero-element list.
2332cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
234384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov    private static List<File> splitPaths(String searchPath, boolean directoriesOnly) {
235384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        List<File> result = new ArrayList<>();
2362cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
237384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        if (searchPath != null) {
238384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov            for (String path : searchPath.split(File.pathSeparator)) {
239384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                if (directoriesOnly) {
240384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                    try {
241384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                        StructStat sb = Libcore.os.stat(path);
242384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                        if (!S_ISDIR(sb.st_mode)) {
243384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                            continue;
244384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                        }
245384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                    } catch (ErrnoException ignored) {
246384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                        continue;
247384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                    }
2482cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                }
249384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                result.add(new File(path));
2502cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
2512cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
252384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
253384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        return result;
2542cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
2552cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
2562cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
2572cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Makes an array of dex/resource path elements, one per element of
2582cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * the given array.
2592cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
2607694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov    private static Element[] makeDexElements(List<File> files, File optimizedDirectory,
26170fcc38e7a03f04864499a7356ae1f1e692b7463Mathieu Chartier                                             List<IOException> suppressedExceptions,
26270fcc38e7a03f04864499a7356ae1f1e692b7463Mathieu Chartier                                             ClassLoader loader) {
26370fcc38e7a03f04864499a7356ae1f1e692b7463Mathieu Chartier        return makeElements(files, optimizedDirectory, suppressedExceptions, false, loader);
264384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov    }
265384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
266384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov    /**
267384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov     * Makes an array of directory/zip path elements, one per element of the given array.
268384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov     */
269384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov    private static Element[] makePathElements(List<File> files,
27070fcc38e7a03f04864499a7356ae1f1e692b7463Mathieu Chartier                                              List<IOException> suppressedExceptions,
27170fcc38e7a03f04864499a7356ae1f1e692b7463Mathieu Chartier                                              ClassLoader loader) {
27270fcc38e7a03f04864499a7356ae1f1e692b7463Mathieu Chartier        return makeElements(files, null, suppressedExceptions, true, loader);
273384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov    }
274384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
275ac4309ac7ba48b85b6d301e95ae767a3bcb8f83eDmitriy Ivanov    /*
276941742a1bf0e1fcde72525371a12828267c2d6bdDimitry Ivanov     * TODO (dimitry): Revert after apps stops relying on the existence of this
277941742a1bf0e1fcde72525371a12828267c2d6bdDimitry Ivanov     * method (see http://b/21957414 and http://b/26317852 for details)
278ac4309ac7ba48b85b6d301e95ae767a3bcb8f83eDmitriy Ivanov     */
279ac4309ac7ba48b85b6d301e95ae767a3bcb8f83eDmitriy Ivanov    private static Element[] makePathElements(List<File> files, File optimizedDirectory,
280ac4309ac7ba48b85b6d301e95ae767a3bcb8f83eDmitriy Ivanov                                              List<IOException> suppressedExceptions) {
281941742a1bf0e1fcde72525371a12828267c2d6bdDimitry Ivanov        return makeElements(files, optimizedDirectory, suppressedExceptions, false, null);
282ac4309ac7ba48b85b6d301e95ae767a3bcb8f83eDmitriy Ivanov    }
283ac4309ac7ba48b85b6d301e95ae767a3bcb8f83eDmitriy Ivanov
284384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov    private static Element[] makeElements(List<File> files, File optimizedDirectory,
285384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                                          List<IOException> suppressedExceptions,
28670fcc38e7a03f04864499a7356ae1f1e692b7463Mathieu Chartier                                          boolean ignoreDexFiles,
28770fcc38e7a03f04864499a7356ae1f1e692b7463Mathieu Chartier                                          ClassLoader loader) {
288da3729f76948e5c7ce5eadbdfc03a0f9fe7d4d83Mathieu Chartier        Element[] elements = new Element[files.size()];
289da3729f76948e5c7ce5eadbdfc03a0f9fe7d4d83Mathieu Chartier        int elementsPos = 0;
2902cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        /*
2912cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom         * Open all files and load the (direct or contained) dex files
2922cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom         * up front.
2932cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom         */
2942cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        for (File file : files) {
2952cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            File zip = null;
296384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov            File dir = new File("");
2972cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            DexFile dex = null;
298384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov            String path = file.getPath();
2992cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            String name = file.getName();
3002cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
301384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov            if (path.contains(zipSeparator)) {
302384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                String split[] = path.split(zipSeparator, 2);
303384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                zip = new File(split[0]);
304384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                dir = new File(split[1]);
305384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov            } else if (file.isDirectory()) {
306384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                // We support directories for looking up resources and native libraries.
307384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                // Looking up resources in directories is useful for running libcore tests.
308da3729f76948e5c7ce5eadbdfc03a0f9fe7d4d83Mathieu Chartier                elements[elementsPos++] = new Element(file, true, null, null);
309384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov            } else if (file.isFile()) {
310384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                if (!ignoreDexFiles && name.endsWith(DEX_SUFFIX)) {
311bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu                    // Raw dex file (not inside a zip/jar).
312bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu                    try {
313da3729f76948e5c7ce5eadbdfc03a0f9fe7d4d83Mathieu Chartier                        dex = loadDexFile(file, optimizedDirectory, loader, elements);
314bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu                    } catch (IOException ex) {
315bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu                        System.logE("Unable to load dex file: " + file, ex);
316bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu                    }
317bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu                } else {
318bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu                    zip = file;
319bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu
320384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                    if (!ignoreDexFiles) {
321384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                        try {
322da3729f76948e5c7ce5eadbdfc03a0f9fe7d4d83Mathieu Chartier                            dex = loadDexFile(file, optimizedDirectory, loader, elements);
323384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                        } catch (IOException suppressed) {
324384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                            /*
325384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                             * IOException might get thrown "legitimately" by the DexFile constructor if
326384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                             * the zip file turns out to be resource-only (that is, no classes.dex file
327384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                             * in it).
328384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                             * Let dex == null and hang on to the exception to add to the tea-leaves for
329384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                             * when findClass returns null.
330384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                             */
331384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                            suppressedExceptions.add(suppressed);
332384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                        }
333bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu                    }
334bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu                }
3352cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            } else {
336bf5ea97806d25d7232a626c7603b88162a8a8c7aHui Lu                System.logW("ClassLoader referenced unknown path: " + file);
3372cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
3382cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
3392cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            if ((zip != null) || (dex != null)) {
340da3729f76948e5c7ce5eadbdfc03a0f9fe7d4d83Mathieu Chartier                elements[elementsPos++] = new Element(dir, false, zip, dex);
3412cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
3422cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
343da3729f76948e5c7ce5eadbdfc03a0f9fe7d4d83Mathieu Chartier        if (elementsPos != elements.length) {
344da3729f76948e5c7ce5eadbdfc03a0f9fe7d4d83Mathieu Chartier            elements = Arrays.copyOf(elements, elementsPos);
345da3729f76948e5c7ce5eadbdfc03a0f9fe7d4d83Mathieu Chartier        }
346da3729f76948e5c7ce5eadbdfc03a0f9fe7d4d83Mathieu Chartier        return elements;
3472cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
3482cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
3492cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
35070fcc38e7a03f04864499a7356ae1f1e692b7463Mathieu Chartier     * Constructs a {@code DexFile} instance, as appropriate depending on whether
35170fcc38e7a03f04864499a7356ae1f1e692b7463Mathieu Chartier     * {@code optimizedDirectory} is {@code null}. An application image file may be associated with
35270fcc38e7a03f04864499a7356ae1f1e692b7463Mathieu Chartier     * the {@code loader} if it is not null.
3532cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
354da3729f76948e5c7ce5eadbdfc03a0f9fe7d4d83Mathieu Chartier    private static DexFile loadDexFile(File file, File optimizedDirectory, ClassLoader loader,
355da3729f76948e5c7ce5eadbdfc03a0f9fe7d4d83Mathieu Chartier                                       Element[] elements)
3562cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            throws IOException {
3572cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        if (optimizedDirectory == null) {
358da3729f76948e5c7ce5eadbdfc03a0f9fe7d4d83Mathieu Chartier            return new DexFile(file, loader, elements);
3592cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        } else {
3602cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            String optimizedPath = optimizedPathFor(file, optimizedDirectory);
361da3729f76948e5c7ce5eadbdfc03a0f9fe7d4d83Mathieu Chartier            return DexFile.loadDex(file.getPath(), optimizedPath, 0, loader, elements);
3622cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
3632cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
3642cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
3652cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
3662cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Converts a dex/jar file path and an output directory to an
367ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom     * output file path for an associated optimized dex file.
3682cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
3692cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    private static String optimizedPathFor(File path,
3702cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            File optimizedDirectory) {
371ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom        /*
372ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * Get the filename component of the path, and replace the
373ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * suffix with ".dex" if that's not already the suffix.
374ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         *
375ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * We don't want to use ".odex", because the build system uses
376ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * that for files that are paired with resource-only jar
377ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * files. If the VM can assume that there's no classes.dex in
378ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * the matching jar, it doesn't need to open the jar to check
379ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * for updated dependencies, providing a slight performance
380ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * boost at startup. The use of ".dex" here matches the use on
381ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * files in /data/dalvik-cache.
382ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         */
383ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom        String fileName = path.getName();
384ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom        if (!fileName.endsWith(DEX_SUFFIX)) {
385ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom            int lastDot = fileName.lastIndexOf(".");
386ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom            if (lastDot < 0) {
387ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom                fileName += DEX_SUFFIX;
388ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom            } else {
389ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom                StringBuilder sb = new StringBuilder(lastDot + 4);
390ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom                sb.append(fileName, 0, lastDot);
391ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom                sb.append(DEX_SUFFIX);
392ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom                fileName = sb.toString();
393ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom            }
394ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom        }
395ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom
3962cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        File result = new File(optimizedDirectory, fileName);
3972cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        return result.getPath();
3982cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
3992cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
4002cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
4012cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Finds the named class in one of the dex files pointed at by
4022cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * this instance. This will find the one in the earliest listed
4032cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * path element. If the class is found but has not yet been
4042cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * defined, then this method will define it in the defining
4052cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * context that this instance was constructed with.
4062cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     *
4072cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * @param name of class to find
4082cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * @param suppressed exceptions encountered whilst finding the class
4092cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * @return the named class or {@code null} if the class is not
4102cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * found in any of the dex files
4112cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
4122cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    public Class findClass(String name, List<Throwable> suppressed) {
4132cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        for (Element element : dexElements) {
4142cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            DexFile dex = element.dexFile;
4152cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
4162cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            if (dex != null) {
4172cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);
4182cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                if (clazz != null) {
4192cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                    return clazz;
4202cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                }
4212cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
4222cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
4231e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers        if (dexElementsSuppressedExceptions != null) {
4241e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers            suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
4251e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers        }
4262cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        return null;
4272cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
4282cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
4292cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
4302cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Finds the named resource in one of the zip/jar files pointed at
4312cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * by this instance. This will find the one in the earliest listed
4322cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * path element.
4332cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     *
4342cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * @return a URL to the named resource or {@code null} if the
4352cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * resource is not found in any of the zip/jar files
4362cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
4372cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    public URL findResource(String name) {
4382cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        for (Element element : dexElements) {
4392cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            URL url = element.findResource(name);
4402cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            if (url != null) {
4412cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                return url;
4422cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
4432cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
4442cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
4452cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        return null;
4462cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
4472cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
4482cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
4492cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Finds all the resources with the given name, returning an
4502cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * enumeration of them. If there are no resources with the given
4512cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * name, then this method returns an empty enumeration.
4522cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
4532cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    public Enumeration<URL> findResources(String name) {
4542cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        ArrayList<URL> result = new ArrayList<URL>();
4552cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
4562cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        for (Element element : dexElements) {
4572cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            URL url = element.findResource(name);
4582cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            if (url != null) {
4592cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                result.add(url);
4602cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
4612cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
4622cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
4632cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        return Collections.enumeration(result);
4642cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
4652cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
4662cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
4672cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Finds the named native code library on any of the library
4682cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * directories pointed at by this instance. This will find the
4692cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * one in the earliest listed directory, ignoring any that are not
4702cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * readable regular files.
4712cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     *
4722cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * @return the complete path to the library or {@code null} if no
4732cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * library was found
4742cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
4752cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    public String findLibrary(String libraryName) {
4762cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        String fileName = System.mapLibraryName(libraryName);
477384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
478384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        for (Element element : nativeLibraryPathElements) {
479384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov            String path = element.findNativeLibrary(fileName);
480384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
481384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov            if (path != null) {
4822cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                return path;
4832cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
4842cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
485384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
4862cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        return null;
4872cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
4882cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
4892cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
490384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov     * Element of the dex/resource/native library path
4912cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
4922cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /*package*/ static class Element {
493384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        private final File dir;
4942cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        private final boolean isDirectory;
4952cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        private final File zip;
4962cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        private final DexFile dexFile;
4972cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
4982ce899fcb81707dd5447a15c29c2c137697f2f5eNeil Fuller        private ClassPathURLStreamHandler urlHandler;
4992cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        private boolean initialized;
5002cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
501384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        public Element(File dir, boolean isDirectory, File zip, DexFile dexFile) {
502384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov            this.dir = dir;
5032cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            this.isDirectory = isDirectory;
5042cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            this.zip = zip;
5052cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            this.dexFile = dexFile;
5062cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
5072cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
5082cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        @Override public String toString() {
5092cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            if (isDirectory) {
510384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                return "directory \"" + dir + "\"";
5112cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            } else if (zip != null) {
512384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                return "zip file \"" + zip + "\"" +
513384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                       (dir != null && !dir.getPath().isEmpty() ? ", dir \"" + dir + "\"" : "");
5142cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            } else {
5152cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                return "dex file \"" + dexFile + "\"";
5162cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
5172cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
5182cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
5192cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        public synchronized void maybeInit() {
5202cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            if (initialized) {
5212cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                return;
5222cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
5232cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
5242cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            initialized = true;
5252cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
5262cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            if (isDirectory || zip == null) {
5272cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                return;
5282cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
5292cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
5302cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            try {
5312ce899fcb81707dd5447a15c29c2c137697f2f5eNeil Fuller                urlHandler = new ClassPathURLStreamHandler(zip.getPath());
5322cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            } catch (IOException ioe) {
5332cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                /*
5342cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                 * Note: ZipException (a subclass of IOException)
5352cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                 * might get thrown by the ZipFile constructor
5362cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                 * (e.g. if the file isn't actually a zip/jar
5372cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                 * file).
5382cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                 */
539384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                System.logE("Unable to open zip file: " + zip, ioe);
540384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                urlHandler = null;
541384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov            }
542384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        }
543384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
544384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        public String findNativeLibrary(String name) {
545384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov            maybeInit();
546384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
547384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov            if (isDirectory) {
548384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                String path = new File(dir, name).getPath();
549384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                if (IoUtils.canOpenReadOnly(path)) {
550384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                    return path;
551384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                }
552384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov            } else if (urlHandler != null) {
553384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                // Having a urlHandler means the element has a zip file.
554384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                // In this case Android supports loading the library iff
555384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                // it is stored in the zip uncompressed.
556384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
557384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                String entryName = new File(dir, name).getPath();
558384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                if (urlHandler.isEntryStored(entryName)) {
559384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                  return zip.getPath() + zipSeparator + entryName;
560384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                }
5612cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
562384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
563384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov            return null;
5642cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
5652cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
5662cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        public URL findResource(String name) {
5672cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            maybeInit();
5682cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
5692cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            // We support directories so we can run tests and/or legacy code
5702cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            // that uses Class.getResource.
5712cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            if (isDirectory) {
572384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                File resourceFile = new File(dir, name);
5732cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                if (resourceFile.exists()) {
5742cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                    try {
5752cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                        return resourceFile.toURI().toURL();
5762cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                    } catch (MalformedURLException ex) {
5772cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                        throw new RuntimeException(ex);
5782cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                    }
5792cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                }
5802cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
5812cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
5822ce899fcb81707dd5447a15c29c2c137697f2f5eNeil Fuller            if (urlHandler == null) {
5832ce899fcb81707dd5447a15c29c2c137697f2f5eNeil Fuller                /* This element has no zip/jar file.
5842cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                 */
5852cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                return null;
5862cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
5872ce899fcb81707dd5447a15c29c2c137697f2f5eNeil Fuller            return urlHandler.getEntryUrlOrNull(name);
5881a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller        }
5892cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
5902cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom}
591