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;
2557dfd7182e6d169ec5a195ab03900a323b27ea13Alex Lightimport java.nio.ByteBuffer;
262cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstromimport java.util.ArrayList;
272cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstromimport java.util.Arrays;
282cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstromimport java.util.Collections;
292cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstromimport java.util.Enumeration;
302cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstromimport java.util.List;
3157dfd7182e6d169ec5a195ab03900a323b27ea13Alex Lightimport libcore.io.ClassPathURLStreamHandler;
322cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstromimport libcore.io.IoUtils;
332cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstromimport libcore.io.Libcore;
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. */
65f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe    private final NativeLibraryElement[] 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    /**
7957dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light     * Construct an instance.
8057dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light     *
8157dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light     * @param definingContext the context in which any as-yet unresolved
8257dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light     * classes should be defined
8357dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light     *
8457dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light     * @param dexFiles the bytebuffers containing the dex files that we should load classes from.
8557dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light     */
8657dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light    public DexPathList(ClassLoader definingContext, ByteBuffer[] dexFiles) {
8757dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light        if (definingContext == null) {
8857dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light            throw new NullPointerException("definingContext == null");
8957dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light        }
9057dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light        if (dexFiles == null) {
9157dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light            throw new NullPointerException("dexFiles == null");
9257dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light        }
9357dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light        if (Arrays.stream(dexFiles).anyMatch(v -> v == null)) {
9457dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light            throw new NullPointerException("dexFiles contains a null Buffer!");
9557dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light        }
9657dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light
9757dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light        this.definingContext = definingContext;
9857dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light        // TODO It might be useful to let in-memory dex-paths have native libraries.
9957dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light        this.nativeLibraryDirectories = Collections.emptyList();
10057dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light        this.systemNativeLibraryDirectories =
10157dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light                splitPaths(System.getProperty("java.library.path"), true);
10257dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light        this.nativeLibraryPathElements = makePathElements(this.systemNativeLibraryDirectories);
10357dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light
10457dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light        ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
10557dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light        this.dexElements = makeInMemoryDexElements(dexFiles, suppressedExceptions);
10657dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light        if (suppressedExceptions.size() > 0) {
10757dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light            this.dexElementsSuppressedExceptions =
10857dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light                    suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]);
10957dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light        } else {
11057dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light            dexElementsSuppressedExceptions = null;
11157dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light        }
11257dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light    }
11357dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light
11457dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light    /**
1152cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Constructs an instance.
1162cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     *
1172cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * @param definingContext the context in which any as-yet unresolved
1182cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * classes should be defined
1192cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * @param dexPath list of dex/resource path elements, separated by
1202cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * {@code File.pathSeparator}
121a2656629522f9d79e2dca7418ab5963f50d0fda8Dimitry Ivanov     * @param librarySearchPath list of native library directory path elements,
1222cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * separated by {@code File.pathSeparator}
1232cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * @param optimizedDirectory directory where optimized {@code .dex} files
1242cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * should be found and written to, or {@code null} to use the default
1252cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * system directory for same
1262cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
1272cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    public DexPathList(ClassLoader definingContext, String dexPath,
12819c3551836aedca51e7e016007efca18d030763bDimitry Ivanov            String librarySearchPath, File optimizedDirectory) {
129384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
1302cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        if (definingContext == null) {
1312cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            throw new NullPointerException("definingContext == null");
1322cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
1332cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
1342cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        if (dexPath == null) {
1352cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            throw new NullPointerException("dexPath == null");
1362cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
1372cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
1382cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        if (optimizedDirectory != null) {
1392cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            if (!optimizedDirectory.exists())  {
1402cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                throw new IllegalArgumentException(
1412cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                        "optimizedDirectory doesn't exist: "
1422cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                        + optimizedDirectory);
1432cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
1442cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
1452cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            if (!(optimizedDirectory.canRead()
1462cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                            && optimizedDirectory.canWrite())) {
1472cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                throw new IllegalArgumentException(
1482cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                        "optimizedDirectory not readable/writable: "
1492cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                        + optimizedDirectory);
1502cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
1512cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
1522cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
1532cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        this.definingContext = definingContext;
154384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
1551e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers        ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
1567694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov        // save dexPath for BaseDexClassLoader
157384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
15870fcc38e7a03f04864499a7356ae1f1e692b7463Mathieu Chartier                                           suppressedExceptions, definingContext);
159384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
160384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        // Native libraries may exist in both the system and
161384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        // application library paths, and we use this search order:
162384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        //
163a2656629522f9d79e2dca7418ab5963f50d0fda8Dimitry Ivanov        //   1. This class loader's library path for application libraries (librarySearchPath):
164384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        //   1.1. Native library directories
165384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        //   1.2. Path to libraries in apk-files
166384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        //   2. The VM's library path from the system property for system libraries
167384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        //      also known as java.library.path
168384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        //
169384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        // This order was reversed prior to Gingerbread; see http://b/2933456.
170a2656629522f9d79e2dca7418ab5963f50d0fda8Dimitry Ivanov        this.nativeLibraryDirectories = splitPaths(librarySearchPath, false);
171384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        this.systemNativeLibraryDirectories =
172384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                splitPaths(System.getProperty("java.library.path"), true);
173384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        List<File> allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories);
174384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);
175384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
176f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        this.nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories);
177384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
1781e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers        if (suppressedExceptions.size() > 0) {
1791e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers            this.dexElementsSuppressedExceptions =
1801e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers                suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]);
1811e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers        } else {
1821e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers            dexElementsSuppressedExceptions = null;
1831e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers        }
1842cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
1852cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
1862cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    @Override public String toString() {
187384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        List<File> allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories);
188384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);
189384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
1907694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov        File[] nativeLibraryDirectoriesArray =
191384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                allNativeLibraryDirectories.toArray(
192384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                    new File[allNativeLibraryDirectories.size()]);
1937694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov
1942cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        return "DexPathList[" + Arrays.toString(dexElements) +
1957694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov            ",nativeLibraryDirectories=" + Arrays.toString(nativeLibraryDirectoriesArray) + "]";
1962cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
1972cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
1982cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
1992cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * For BaseDexClassLoader.getLdLibraryPath.
2002cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
2017694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov    public List<File> getNativeLibraryDirectories() {
2022cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        return nativeLibraryDirectories;
2032cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
2042cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
2052cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
206104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy     * Adds a new path to this instance
207104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy     * @param dexPath list of dex/resource path element, separated by
208104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy     * {@code File.pathSeparator}
209104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy     * @param optimizedDirectory directory where optimized {@code .dex} files
210104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy     * should be found and written to, or {@code null} to use the default
211104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy     * system directory for same
212104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy     */
213104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy    public void addDexPath(String dexPath, File optimizedDirectory) {
214104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy        final List<IOException> suppressedExceptionList = new ArrayList<IOException>();
215104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy        final Element[] newElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
216104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                suppressedExceptionList, definingContext);
217104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy
218104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy        if (newElements != null && newElements.length > 0) {
219104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy            final Element[] oldElements = dexElements;
220104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy            dexElements = new Element[oldElements.length + newElements.length];
221104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy            System.arraycopy(
222104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                    oldElements, 0, dexElements, 0, oldElements.length);
223104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy            System.arraycopy(
224104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                    newElements, 0, dexElements, oldElements.length, newElements.length);
225104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy        }
226104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy
227104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy        if (suppressedExceptionList.size() > 0) {
228104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy            final IOException[] newSuppressedExceptions = suppressedExceptionList.toArray(
229104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                    new IOException[suppressedExceptionList.size()]);
230104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy            if (dexElementsSuppressedExceptions != null) {
231104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                final IOException[] oldSuppressedExceptions = dexElementsSuppressedExceptions;
232104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                final int suppressedExceptionsLength = oldSuppressedExceptions.length +
233104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                        newSuppressedExceptions.length;
234104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                dexElementsSuppressedExceptions = new IOException[suppressedExceptionsLength];
235104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                System.arraycopy(oldSuppressedExceptions, 0, dexElementsSuppressedExceptions,
236104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                        0, oldSuppressedExceptions.length);
237104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                System.arraycopy(newSuppressedExceptions, 0, dexElementsSuppressedExceptions,
238104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                        oldSuppressedExceptions.length, newSuppressedExceptions.length);
239104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy            } else {
240104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                dexElementsSuppressedExceptions = newSuppressedExceptions;
241104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy            }
242104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy        }
243104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy    }
244104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy
245104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy    /**
2462cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Splits the given dex path string into elements using the path
2472cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * separator, pruning out any elements that do not refer to existing
248384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov     * and readable files.
2492cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
2507694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov    private static List<File> splitDexPath(String path) {
251384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        return splitPaths(path, false);
2522cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
2532cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
2542cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
2552cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Splits the given path strings into file elements using the path
2562cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * separator, combining the results and filtering out elements
2572cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * that don't exist, aren't readable, or aren't either a regular
2582cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * file or a directory (as specified). Either string may be empty
2592cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * or {@code null}, in which case it is ignored. If both strings
2602cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * are empty or {@code null}, or all elements get pruned out, then
2612cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * this returns a zero-element list.
2622cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
263384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov    private static List<File> splitPaths(String searchPath, boolean directoriesOnly) {
264384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        List<File> result = new ArrayList<>();
2652cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
266384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        if (searchPath != null) {
267384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov            for (String path : searchPath.split(File.pathSeparator)) {
268384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                if (directoriesOnly) {
269384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                    try {
270384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                        StructStat sb = Libcore.os.stat(path);
271384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                        if (!S_ISDIR(sb.st_mode)) {
272384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                            continue;
273384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                        }
274384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                    } catch (ErrnoException ignored) {
275384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                        continue;
276384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                    }
2772cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                }
278384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                result.add(new File(path));
2792cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
2802cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
281384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
282384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        return result;
2832cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
2842cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
28557dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light    private static Element[] makeInMemoryDexElements(ByteBuffer[] dexFiles,
28657dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light            List<IOException> suppressedExceptions) {
28757dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light        Element[] elements = new Element[dexFiles.length];
28857dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light        int elementPos = 0;
28957dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light        for (ByteBuffer buf : dexFiles) {
29057dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light            try {
29157dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light                DexFile dex = new DexFile(buf);
29257dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light                elements[elementPos++] = new Element(dex);
29357dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light            } catch (IOException suppressed) {
29457dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light                System.logE("Unable to load dex file: " + buf, suppressed);
29557dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light                suppressedExceptions.add(suppressed);
29657dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light            }
29757dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light        }
29857dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light        if (elementPos != elements.length) {
29957dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light            elements = Arrays.copyOf(elements, elementPos);
30057dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light        }
30157dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light        return elements;
30257dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light    }
30357dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light
3042cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
3052cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Makes an array of dex/resource path elements, one per element of
3062cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * the given array.
3072cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
3087694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov    private static Element[] makeDexElements(List<File> files, File optimizedDirectory,
309f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            List<IOException> suppressedExceptions, ClassLoader loader) {
310f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe      Element[] elements = new Element[files.size()];
311f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe      int elementsPos = 0;
312f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe      /*
313f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe       * Open all files and load the (direct or contained) dex files up front.
314f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe       */
315f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe      for (File file : files) {
316f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe          if (file.isDirectory()) {
317f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe              // We support directories for looking up resources. Looking up resources in
318f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe              // directories is useful for running libcore tests.
319f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe              elements[elementsPos++] = new Element(file);
320f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe          } else if (file.isFile()) {
321f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe              String name = file.getName();
322f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
323f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe              if (name.endsWith(DEX_SUFFIX)) {
324f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                  // Raw dex file (not inside a zip/jar).
325f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                  try {
326f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                      DexFile dex = loadDexFile(file, optimizedDirectory, loader, elements);
327f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                      if (dex != null) {
328f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                          elements[elementsPos++] = new Element(dex, null);
329f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                      }
330f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                  } catch (IOException suppressed) {
331f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                      System.logE("Unable to load dex file: " + file, suppressed);
332f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                      suppressedExceptions.add(suppressed);
333f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                  }
334f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe              } else {
335f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                  DexFile dex = null;
336f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                  try {
337f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                      dex = loadDexFile(file, optimizedDirectory, loader, elements);
338f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                  } catch (IOException suppressed) {
339f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                      /*
340f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                       * IOException might get thrown "legitimately" by the DexFile constructor if
341f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                       * the zip file turns out to be resource-only (that is, no classes.dex file
342f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                       * in it).
343f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                       * Let dex == null and hang on to the exception to add to the tea-leaves for
344f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                       * when findClass returns null.
345f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                       */
346f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                      suppressedExceptions.add(suppressed);
347f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                  }
348f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
349f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                  if (dex == null) {
350f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                      elements[elementsPos++] = new Element(file);
351f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                  } else {
352f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                      elements[elementsPos++] = new Element(dex, file);
353f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                  }
354f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe              }
355f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe          } else {
356f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe              System.logW("ClassLoader referenced unknown path: " + file);
357f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe          }
358f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe      }
359f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe      if (elementsPos != elements.length) {
360f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe          elements = Arrays.copyOf(elements, elementsPos);
361f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe      }
362f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe      return elements;
3632cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
3642cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
3652cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
36670fcc38e7a03f04864499a7356ae1f1e692b7463Mathieu Chartier     * Constructs a {@code DexFile} instance, as appropriate depending on whether
36770fcc38e7a03f04864499a7356ae1f1e692b7463Mathieu Chartier     * {@code optimizedDirectory} is {@code null}. An application image file may be associated with
36870fcc38e7a03f04864499a7356ae1f1e692b7463Mathieu Chartier     * the {@code loader} if it is not null.
3692cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
370da3729f76948e5c7ce5eadbdfc03a0f9fe7d4d83Mathieu Chartier    private static DexFile loadDexFile(File file, File optimizedDirectory, ClassLoader loader,
371da3729f76948e5c7ce5eadbdfc03a0f9fe7d4d83Mathieu Chartier                                       Element[] elements)
3722cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            throws IOException {
3732cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        if (optimizedDirectory == null) {
374da3729f76948e5c7ce5eadbdfc03a0f9fe7d4d83Mathieu Chartier            return new DexFile(file, loader, elements);
3752cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        } else {
3762cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            String optimizedPath = optimizedPathFor(file, optimizedDirectory);
377da3729f76948e5c7ce5eadbdfc03a0f9fe7d4d83Mathieu Chartier            return DexFile.loadDex(file.getPath(), optimizedPath, 0, loader, elements);
3782cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
3792cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
3802cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
3812cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
3822cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Converts a dex/jar file path and an output directory to an
383ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom     * output file path for an associated optimized dex file.
3842cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
3852cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    private static String optimizedPathFor(File path,
3862cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            File optimizedDirectory) {
387ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom        /*
388ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * Get the filename component of the path, and replace the
389ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * suffix with ".dex" if that's not already the suffix.
390ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         *
391ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * We don't want to use ".odex", because the build system uses
392ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * that for files that are paired with resource-only jar
393ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * files. If the VM can assume that there's no classes.dex in
394ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * the matching jar, it doesn't need to open the jar to check
395ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * for updated dependencies, providing a slight performance
396ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * boost at startup. The use of ".dex" here matches the use on
397ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * files in /data/dalvik-cache.
398ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         */
399ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom        String fileName = path.getName();
400ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom        if (!fileName.endsWith(DEX_SUFFIX)) {
401ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom            int lastDot = fileName.lastIndexOf(".");
402ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom            if (lastDot < 0) {
403ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom                fileName += DEX_SUFFIX;
404ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom            } else {
405ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom                StringBuilder sb = new StringBuilder(lastDot + 4);
406ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom                sb.append(fileName, 0, lastDot);
407ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom                sb.append(DEX_SUFFIX);
408ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom                fileName = sb.toString();
409ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom            }
410ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom        }
411ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom
4122cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        File result = new File(optimizedDirectory, fileName);
4132cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        return result.getPath();
4142cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
4152cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
416f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe    /*
417f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe     * TODO (dimitry): Revert after apps stops relying on the existence of this
418f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe     * method (see http://b/21957414 and http://b/26317852 for details)
419f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe     */
420f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe    @SuppressWarnings("unused")
421f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe    private static Element[] makePathElements(List<File> files, File optimizedDirectory,
422f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            List<IOException> suppressedExceptions) {
423f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        return makeDexElements(files, optimizedDirectory, suppressedExceptions, null);
424f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe    }
425f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
426f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe    /**
427f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe     * Makes an array of directory/zip path elements for the native library search path, one per
428f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe     * element of the given array.
429f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe     */
430f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe    private static NativeLibraryElement[] makePathElements(List<File> files) {
431f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        NativeLibraryElement[] elements = new NativeLibraryElement[files.size()];
432f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        int elementsPos = 0;
433f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        for (File file : files) {
434f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            String path = file.getPath();
435f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
436f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            if (path.contains(zipSeparator)) {
437f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                String split[] = path.split(zipSeparator, 2);
438f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                File zip = new File(split[0]);
439f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                String dir = split[1];
440f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                elements[elementsPos++] = new NativeLibraryElement(zip, dir);
441f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            } else if (file.isDirectory()) {
442f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                // We support directories for looking up native libraries.
443f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                elements[elementsPos++] = new NativeLibraryElement(file);
444f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            }
445f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        }
446f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        if (elementsPos != elements.length) {
447f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            elements = Arrays.copyOf(elements, elementsPos);
448f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        }
449f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        return elements;
450f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe    }
451f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
4522cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
4532cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Finds the named class in one of the dex files pointed at by
4542cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * this instance. This will find the one in the earliest listed
4552cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * path element. If the class is found but has not yet been
4562cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * defined, then this method will define it in the defining
4572cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * context that this instance was constructed with.
4582cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     *
4592cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * @param name of class to find
4602cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * @param suppressed exceptions encountered whilst finding the class
4612cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * @return the named class or {@code null} if the class is not
4622cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * found in any of the dex files
4632cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
464f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe    public Class<?> findClass(String name, List<Throwable> suppressed) {
4652cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        for (Element element : dexElements) {
466f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            Class<?> clazz = element.findClass(name, definingContext, suppressed);
467f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            if (clazz != null) {
468f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                return clazz;
4692cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
4702cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
471f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
4721e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers        if (dexElementsSuppressedExceptions != null) {
4731e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers            suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
4741e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers        }
4752cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        return null;
4762cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
4772cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
4782cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
4792cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Finds the named resource in one of the zip/jar files pointed at
4802cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * by this instance. This will find the one in the earliest listed
4812cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * path element.
4822cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     *
4832cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * @return a URL to the named resource or {@code null} if the
4842cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * resource is not found in any of the zip/jar files
4852cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
4862cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    public URL findResource(String name) {
4872cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        for (Element element : dexElements) {
4882cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            URL url = element.findResource(name);
4892cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            if (url != null) {
4902cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                return url;
4912cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
4922cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
4932cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
4942cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        return null;
4952cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
4962cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
4972cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
4982cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Finds all the resources with the given name, returning an
4992cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * enumeration of them. If there are no resources with the given
5002cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * name, then this method returns an empty enumeration.
5012cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
5022cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    public Enumeration<URL> findResources(String name) {
5032cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        ArrayList<URL> result = new ArrayList<URL>();
5042cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
5052cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        for (Element element : dexElements) {
5062cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            URL url = element.findResource(name);
5072cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            if (url != null) {
5082cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                result.add(url);
5092cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
5102cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
5112cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
5122cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        return Collections.enumeration(result);
5132cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
5142cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
5152cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
5162cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Finds the named native code library on any of the library
5172cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * directories pointed at by this instance. This will find the
5182cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * one in the earliest listed directory, ignoring any that are not
5192cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * readable regular files.
5202cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     *
5212cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * @return the complete path to the library or {@code null} if no
5222cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * library was found
5232cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
5242cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    public String findLibrary(String libraryName) {
5252cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        String fileName = System.mapLibraryName(libraryName);
526384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
527f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        for (NativeLibraryElement element : nativeLibraryPathElements) {
528384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov            String path = element.findNativeLibrary(fileName);
529384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
530384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov            if (path != null) {
5312cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                return path;
5322cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
5332cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
534384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
5352cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        return null;
5362cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
5372cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
5382cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
539a8c32796376050a8875f0aded5acf5e78941aa18Calin Juravle     * Returns the list of all individual dex files paths from the current list.
540a8c32796376050a8875f0aded5acf5e78941aa18Calin Juravle     * The list will contain only file paths (i.e. no directories).
541a8c32796376050a8875f0aded5acf5e78941aa18Calin Juravle     */
542a8c32796376050a8875f0aded5acf5e78941aa18Calin Juravle    /*package*/ List<String> getDexPaths() {
543a8c32796376050a8875f0aded5acf5e78941aa18Calin Juravle        List<String> dexPaths = new ArrayList<String>();
544a8c32796376050a8875f0aded5acf5e78941aa18Calin Juravle        for (Element e : dexElements) {
545a8c32796376050a8875f0aded5acf5e78941aa18Calin Juravle            String dexPath = e.getDexPath();
546a8c32796376050a8875f0aded5acf5e78941aa18Calin Juravle            if (dexPath != null) {
54757dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light                // Add the element to the list only if it is a file. A null dex path signals the
54857dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light                // element is a resource directory or an in-memory dex file.
549a8c32796376050a8875f0aded5acf5e78941aa18Calin Juravle                dexPaths.add(dexPath);
550a8c32796376050a8875f0aded5acf5e78941aa18Calin Juravle            }
551a8c32796376050a8875f0aded5acf5e78941aa18Calin Juravle        }
552a8c32796376050a8875f0aded5acf5e78941aa18Calin Juravle        return dexPaths;
553a8c32796376050a8875f0aded5acf5e78941aa18Calin Juravle    }
554a8c32796376050a8875f0aded5acf5e78941aa18Calin Juravle
555a8c32796376050a8875f0aded5acf5e78941aa18Calin Juravle    /**
556f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe     * Element of the dex/resource path. Note: should be called DexElement, but apps reflect on
557f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe     * this.
5582cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
5592cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /*package*/ static class Element {
560f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        /**
561f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe         * A file denoting a zip file (in case of a resource jar or a dex jar), or a directory
562f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe         * (only when dexFile is null).
563f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe         */
564f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        private final File path;
565f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
5662cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        private final DexFile dexFile;
5672cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
5682ce899fcb81707dd5447a15c29c2c137697f2f5eNeil Fuller        private ClassPathURLStreamHandler urlHandler;
5692cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        private boolean initialized;
5702cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
571f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        /**
572f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe         * Element encapsulates a dex file. This may be a plain dex file (in which case dexZipPath
57318e39e66d726255c649f7fe9ab148896dcbbbcf1Andreas Gampe         * should be null), or a jar (in which case dexZipPath should denote the zip file).
574f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe         */
575f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        public Element(DexFile dexFile, File dexZipPath) {
5762cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            this.dexFile = dexFile;
577f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            this.path = dexZipPath;
578f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        }
579f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
58057dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light        public Element(DexFile dexFile) {
58157dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light            this.dexFile = dexFile;
58257dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light            this.path = null;
58357dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light        }
58457dfd7182e6d169ec5a195ab03900a323b27ea13Alex Light
585f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        public Element(File path) {
586f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe          this.path = path;
587f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe          this.dexFile = null;
5882cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
5892cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
59018e39e66d726255c649f7fe9ab148896dcbbbcf1Andreas Gampe        /**
59118e39e66d726255c649f7fe9ab148896dcbbbcf1Andreas Gampe         * Constructor for a bit of backwards compatibility. Some apps use reflection into
59218e39e66d726255c649f7fe9ab148896dcbbbcf1Andreas Gampe         * internal APIs. Warn, and emulate old behavior if we can. See b/33399341.
59318e39e66d726255c649f7fe9ab148896dcbbbcf1Andreas Gampe         *
59418e39e66d726255c649f7fe9ab148896dcbbbcf1Andreas Gampe         * @deprecated The Element class has been split. Use new Element constructors for
59518e39e66d726255c649f7fe9ab148896dcbbbcf1Andreas Gampe         *             classes and resources, and NativeLibraryElement for the library
59618e39e66d726255c649f7fe9ab148896dcbbbcf1Andreas Gampe         *             search path.
59718e39e66d726255c649f7fe9ab148896dcbbbcf1Andreas Gampe         */
59818e39e66d726255c649f7fe9ab148896dcbbbcf1Andreas Gampe        @Deprecated
59918e39e66d726255c649f7fe9ab148896dcbbbcf1Andreas Gampe        public Element(File dir, boolean isDirectory, File zip, DexFile dexFile) {
60018e39e66d726255c649f7fe9ab148896dcbbbcf1Andreas Gampe            System.err.println("Warning: Using deprecated Element constructor. Do not use internal"
60118e39e66d726255c649f7fe9ab148896dcbbbcf1Andreas Gampe                    + " APIs, this constructor will be removed in the future.");
60218e39e66d726255c649f7fe9ab148896dcbbbcf1Andreas Gampe            if (dir != null && (zip != null || dexFile != null)) {
60318e39e66d726255c649f7fe9ab148896dcbbbcf1Andreas Gampe                throw new IllegalArgumentException("Using dir and zip|dexFile no longer"
60418e39e66d726255c649f7fe9ab148896dcbbbcf1Andreas Gampe                        + " supported.");
60518e39e66d726255c649f7fe9ab148896dcbbbcf1Andreas Gampe            }
60618e39e66d726255c649f7fe9ab148896dcbbbcf1Andreas Gampe            if (isDirectory && (zip != null || dexFile != null)) {
60718e39e66d726255c649f7fe9ab148896dcbbbcf1Andreas Gampe                throw new IllegalArgumentException("Unsupported argument combination.");
60818e39e66d726255c649f7fe9ab148896dcbbbcf1Andreas Gampe            }
60918e39e66d726255c649f7fe9ab148896dcbbbcf1Andreas Gampe            if (dir != null) {
61018e39e66d726255c649f7fe9ab148896dcbbbcf1Andreas Gampe                this.path = dir;
61118e39e66d726255c649f7fe9ab148896dcbbbcf1Andreas Gampe                this.dexFile = null;
61218e39e66d726255c649f7fe9ab148896dcbbbcf1Andreas Gampe            } else {
61318e39e66d726255c649f7fe9ab148896dcbbbcf1Andreas Gampe                this.path = zip;
61418e39e66d726255c649f7fe9ab148896dcbbbcf1Andreas Gampe                this.dexFile = dexFile;
61518e39e66d726255c649f7fe9ab148896dcbbbcf1Andreas Gampe            }
61618e39e66d726255c649f7fe9ab148896dcbbbcf1Andreas Gampe        }
61718e39e66d726255c649f7fe9ab148896dcbbbcf1Andreas Gampe
618a8c32796376050a8875f0aded5acf5e78941aa18Calin Juravle        /*
619a8c32796376050a8875f0aded5acf5e78941aa18Calin Juravle         * Returns the dex path of this element or null if the element refers to a directory.
620a8c32796376050a8875f0aded5acf5e78941aa18Calin Juravle         */
621a8c32796376050a8875f0aded5acf5e78941aa18Calin Juravle        private String getDexPath() {
622a8c32796376050a8875f0aded5acf5e78941aa18Calin Juravle            if (path != null) {
623a8c32796376050a8875f0aded5acf5e78941aa18Calin Juravle                return path.isDirectory() ? null : path.getAbsolutePath();
624a8c32796376050a8875f0aded5acf5e78941aa18Calin Juravle            } else if (dexFile != null) {
625a8c32796376050a8875f0aded5acf5e78941aa18Calin Juravle                // DexFile.getName() returns the path of the dex file.
626a8c32796376050a8875f0aded5acf5e78941aa18Calin Juravle                return dexFile.getName();
627a8c32796376050a8875f0aded5acf5e78941aa18Calin Juravle            }
628a8c32796376050a8875f0aded5acf5e78941aa18Calin Juravle            return null;
629a8c32796376050a8875f0aded5acf5e78941aa18Calin Juravle        }
630a8c32796376050a8875f0aded5acf5e78941aa18Calin Juravle
631f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        @Override
632f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        public String toString() {
633f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            if (dexFile == null) {
634f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe              return (path.isDirectory() ? "directory \"" : "zip file \"") + path + "\"";
6352cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            } else {
636f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe              if (path == null) {
6372cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                return "dex file \"" + dexFile + "\"";
638f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe              } else {
639f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                return "zip file \"" + path + "\"";
640f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe              }
6412cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
6422cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
6432cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
6442cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        public synchronized void maybeInit() {
6452cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            if (initialized) {
6462cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                return;
6472cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
6482cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
649f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            if (path == null || path.isDirectory()) {
650c88ed38242fa0c73776b7e9cd572558a5e5802ebNarayan Kamath                initialized = true;
6512cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                return;
6522cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
6532cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
6542cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            try {
655f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                urlHandler = new ClassPathURLStreamHandler(path.getPath());
6562cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            } catch (IOException ioe) {
6572cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                /*
6582cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                 * Note: ZipException (a subclass of IOException)
6592cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                 * might get thrown by the ZipFile constructor
6602cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                 * (e.g. if the file isn't actually a zip/jar
6612cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                 * file).
6622cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                 */
663f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                System.logE("Unable to open zip file: " + path, ioe);
664384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                urlHandler = null;
665384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov            }
666c88ed38242fa0c73776b7e9cd572558a5e5802ebNarayan Kamath
667c88ed38242fa0c73776b7e9cd572558a5e5802ebNarayan Kamath            // Mark this element as initialized only after we've successfully created
668c88ed38242fa0c73776b7e9cd572558a5e5802ebNarayan Kamath            // the associated ClassPathURLStreamHandler. That way, we won't leave this
669c88ed38242fa0c73776b7e9cd572558a5e5802ebNarayan Kamath            // element in an inconsistent state if an exception is thrown during initialization.
670c88ed38242fa0c73776b7e9cd572558a5e5802ebNarayan Kamath            //
671c88ed38242fa0c73776b7e9cd572558a5e5802ebNarayan Kamath            // See b/35633614.
672c88ed38242fa0c73776b7e9cd572558a5e5802ebNarayan Kamath            initialized = true;
673384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        }
674384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
675f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        public Class<?> findClass(String name, ClassLoader definingContext,
676f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                List<Throwable> suppressed) {
677f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)
678f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                    : null;
6792cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
6802cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
6812cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        public URL findResource(String name) {
6822cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            maybeInit();
6832cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
684f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            if (urlHandler != null) {
685f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe              return urlHandler.getEntryUrlOrNull(name);
686f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            }
687f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
6882cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            // We support directories so we can run tests and/or legacy code
6892cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            // that uses Class.getResource.
690f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            if (path != null && path.isDirectory()) {
691f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                File resourceFile = new File(path, name);
6922cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                if (resourceFile.exists()) {
6932cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                    try {
6942cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                        return resourceFile.toURI().toURL();
6952cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                    } catch (MalformedURLException ex) {
6962cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                        throw new RuntimeException(ex);
6972cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                    }
6982cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                }
6992cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
7002cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
701f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            return null;
702f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        }
703f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe    }
704f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
705f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe    /**
706f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe     * Element of the native library path
707f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe     */
708f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe    /*package*/ static class NativeLibraryElement {
709f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        /**
710f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe         * A file denoting a directory or zip file.
711f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe         */
712f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        private final File path;
713f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
714f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        /**
715f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe         * If path denotes a zip file, this denotes a base path inside the zip.
716f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe         */
717f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        private final String zipDir;
718f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
719f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        private ClassPathURLStreamHandler urlHandler;
720f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        private boolean initialized;
721f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
722f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        public NativeLibraryElement(File dir) {
723f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            this.path = dir;
724f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            this.zipDir = null;
725f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
726f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            // We should check whether path is a directory, but that is non-eliminatable overhead.
727f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        }
728f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
729f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        public NativeLibraryElement(File zip, String zipDir) {
730f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            this.path = zip;
731f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            this.zipDir = zipDir;
732f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
733f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            // Simple check that should be able to be eliminated by inlining. We should also
734f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            // check whether path is a file, but that is non-eliminatable overhead.
735f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            if (zipDir == null) {
736f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe              throw new IllegalArgumentException();
737f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            }
738f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        }
739f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
740f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        @Override
741f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        public String toString() {
742f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            if (zipDir == null) {
743f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                return "directory \"" + path + "\"";
744f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            } else {
745f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                return "zip file \"" + path + "\"" +
746f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                  (!zipDir.isEmpty() ? ", dir \"" + zipDir + "\"" : "");
747f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            }
748f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        }
749f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
750f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        public synchronized void maybeInit() {
751f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            if (initialized) {
752f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                return;
753f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            }
754f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
755f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            if (zipDir == null) {
756c88ed38242fa0c73776b7e9cd572558a5e5802ebNarayan Kamath                initialized = true;
757f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                return;
758f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            }
759f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
760f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            try {
761f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                urlHandler = new ClassPathURLStreamHandler(path.getPath());
762f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            } catch (IOException ioe) {
763f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                /*
764f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                 * Note: ZipException (a subclass of IOException)
765f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                 * might get thrown by the ZipFile constructor
766f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                 * (e.g. if the file isn't actually a zip/jar
767f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                 * file).
7682cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                 */
769f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                System.logE("Unable to open zip file: " + path, ioe);
770f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                urlHandler = null;
771f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            }
772c88ed38242fa0c73776b7e9cd572558a5e5802ebNarayan Kamath
773c88ed38242fa0c73776b7e9cd572558a5e5802ebNarayan Kamath            // Mark this element as initialized only after we've successfully created
774c88ed38242fa0c73776b7e9cd572558a5e5802ebNarayan Kamath            // the associated ClassPathURLStreamHandler. That way, we won't leave this
775c88ed38242fa0c73776b7e9cd572558a5e5802ebNarayan Kamath            // element in an inconsistent state if an exception is thrown during initialization.
776c88ed38242fa0c73776b7e9cd572558a5e5802ebNarayan Kamath            //
777c88ed38242fa0c73776b7e9cd572558a5e5802ebNarayan Kamath            // See b/35633614.
778c88ed38242fa0c73776b7e9cd572558a5e5802ebNarayan Kamath            initialized = true;
779f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        }
780f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
781f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        public String findNativeLibrary(String name) {
782f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            maybeInit();
783f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
784f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            if (zipDir == null) {
785f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                String entryPath = new File(path, name).getPath();
786f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                if (IoUtils.canOpenReadOnly(entryPath)) {
787f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                    return entryPath;
788f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                }
789f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            } else if (urlHandler != null) {
790f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                // Having a urlHandler means the element has a zip file.
791f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                // In this case Android supports loading the library iff
792f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                // it is stored in the zip uncompressed.
793f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                String entryName = zipDir + '/' + name;
794f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                if (urlHandler.isEntryStored(entryName)) {
795f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                  return path.getPath() + zipSeparator + entryName;
796f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                }
7972cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
798f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
799f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            return null;
8001a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller        }
8012cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
8022cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom}
803