DexPathList.java revision f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05
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;
302cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstromimport libcore.io.IoUtils;
312cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstromimport libcore.io.Libcore;
322ce899fcb81707dd5447a15c29c2c137697f2f5eNeil Fullerimport libcore.io.ClassPathURLStreamHandler;
332ce899fcb81707dd5447a15c29c2c137697f2f5eNeil Fuller
342ce899fcb81707dd5447a15c29c2c137697f2f5eNeil Fullerimport static android.system.OsConstants.S_ISDIR;
352cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
362cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom/**
372cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * A pair of lists of entries, associated with a {@code ClassLoader}.
382cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * One of the lists is a dex/resource path — typically referred
392cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * to as a "class path" — list, and the other names directories
402cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * containing native code libraries. Class path entries may be any of:
412cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * a {@code .jar} or {@code .zip} file containing an optional
422cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * top-level {@code classes.dex} file as well as arbitrary resources,
432cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * or a plain {@code .dex} file (with no possibility of associated
442cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * resources).
452cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom *
462cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * <p>This class also contains methods to use these lists to look up
472cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom * classes and resources.</p>
482cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom */
492cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom/*package*/ final class DexPathList {
502cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    private static final String DEX_SUFFIX = ".dex";
51384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov    private static final String zipSeparator = "!/";
522cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
532cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /** class definition context */
542cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    private final ClassLoader definingContext;
552cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
562cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
572cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * List of dex/resource (class path) elements.
582cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Should be called pathElements, but the Facebook app uses reflection
592cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * to modify 'dexElements' (http://b/7726934).
602cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
61104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy    private Element[] dexElements;
622cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
63384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov    /** List of native library path elements. */
64f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe    private final NativeLibraryElement[] nativeLibraryPathElements;
65384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
66384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov    /** List of application native library directories. */
677694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov    private final List<File> nativeLibraryDirectories;
682cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
69384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov    /** List of system native library directories. */
70384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov    private final List<File> systemNativeLibraryDirectories;
71384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
722cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
731e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers     * Exceptions thrown during creation of the dexElements list.
741e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers     */
75104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy    private IOException[] dexElementsSuppressedExceptions;
761e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers
771e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers    /**
782cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Constructs an instance.
792cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     *
802cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * @param definingContext the context in which any as-yet unresolved
812cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * classes should be defined
822cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * @param dexPath list of dex/resource path elements, separated by
832cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * {@code File.pathSeparator}
84a2656629522f9d79e2dca7418ab5963f50d0fda8Dimitry Ivanov     * @param librarySearchPath list of native library directory path elements,
852cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * separated by {@code File.pathSeparator}
862cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * @param optimizedDirectory directory where optimized {@code .dex} files
872cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * should be found and written to, or {@code null} to use the default
882cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * system directory for same
892cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
902cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    public DexPathList(ClassLoader definingContext, String dexPath,
9119c3551836aedca51e7e016007efca18d030763bDimitry Ivanov            String librarySearchPath, File optimizedDirectory) {
92384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
932cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        if (definingContext == null) {
942cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            throw new NullPointerException("definingContext == null");
952cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
962cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
972cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        if (dexPath == null) {
982cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            throw new NullPointerException("dexPath == null");
992cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
1002cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
1012cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        if (optimizedDirectory != null) {
1022cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            if (!optimizedDirectory.exists())  {
1032cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                throw new IllegalArgumentException(
1042cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                        "optimizedDirectory doesn't exist: "
1052cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                        + optimizedDirectory);
1062cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
1072cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
1082cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            if (!(optimizedDirectory.canRead()
1092cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                            && optimizedDirectory.canWrite())) {
1102cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                throw new IllegalArgumentException(
1112cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                        "optimizedDirectory not readable/writable: "
1122cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                        + optimizedDirectory);
1132cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
1142cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
1152cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
1162cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        this.definingContext = definingContext;
117384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
1181e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers        ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
1197694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov        // save dexPath for BaseDexClassLoader
120384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
12170fcc38e7a03f04864499a7356ae1f1e692b7463Mathieu Chartier                                           suppressedExceptions, definingContext);
122384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
123384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        // Native libraries may exist in both the system and
124384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        // application library paths, and we use this search order:
125384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        //
126a2656629522f9d79e2dca7418ab5963f50d0fda8Dimitry Ivanov        //   1. This class loader's library path for application libraries (librarySearchPath):
127384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        //   1.1. Native library directories
128384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        //   1.2. Path to libraries in apk-files
129384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        //   2. The VM's library path from the system property for system libraries
130384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        //      also known as java.library.path
131384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        //
132384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        // This order was reversed prior to Gingerbread; see http://b/2933456.
133a2656629522f9d79e2dca7418ab5963f50d0fda8Dimitry Ivanov        this.nativeLibraryDirectories = splitPaths(librarySearchPath, false);
134384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        this.systemNativeLibraryDirectories =
135384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                splitPaths(System.getProperty("java.library.path"), true);
136384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        List<File> allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories);
137384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);
138384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
139f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        this.nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories);
140384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
1411e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers        if (suppressedExceptions.size() > 0) {
1421e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers            this.dexElementsSuppressedExceptions =
1431e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers                suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]);
1441e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers        } else {
1451e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers            dexElementsSuppressedExceptions = null;
1461e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers        }
1472cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
1482cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
1492cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    @Override public String toString() {
150384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        List<File> allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories);
151384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);
152384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
1537694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov        File[] nativeLibraryDirectoriesArray =
154384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                allNativeLibraryDirectories.toArray(
155384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                    new File[allNativeLibraryDirectories.size()]);
1567694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov
1572cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        return "DexPathList[" + Arrays.toString(dexElements) +
1587694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov            ",nativeLibraryDirectories=" + Arrays.toString(nativeLibraryDirectoriesArray) + "]";
1592cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
1602cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
1612cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
1622cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * For BaseDexClassLoader.getLdLibraryPath.
1632cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
1647694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov    public List<File> getNativeLibraryDirectories() {
1652cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        return nativeLibraryDirectories;
1662cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
1672cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
1682cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
169104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy     * Adds a new path to this instance
170104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy     * @param dexPath list of dex/resource path element, separated by
171104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy     * {@code File.pathSeparator}
172104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy     * @param optimizedDirectory directory where optimized {@code .dex} files
173104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy     * should be found and written to, or {@code null} to use the default
174104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy     * system directory for same
175104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy     */
176104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy    public void addDexPath(String dexPath, File optimizedDirectory) {
177104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy        final List<IOException> suppressedExceptionList = new ArrayList<IOException>();
178104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy        final Element[] newElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
179104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                suppressedExceptionList, definingContext);
180104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy
181104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy        if (newElements != null && newElements.length > 0) {
182104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy            final Element[] oldElements = dexElements;
183104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy            dexElements = new Element[oldElements.length + newElements.length];
184104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy            System.arraycopy(
185104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                    oldElements, 0, dexElements, 0, oldElements.length);
186104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy            System.arraycopy(
187104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                    newElements, 0, dexElements, oldElements.length, newElements.length);
188104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy        }
189104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy
190104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy        if (suppressedExceptionList.size() > 0) {
191104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy            final IOException[] newSuppressedExceptions = suppressedExceptionList.toArray(
192104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                    new IOException[suppressedExceptionList.size()]);
193104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy            if (dexElementsSuppressedExceptions != null) {
194104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                final IOException[] oldSuppressedExceptions = dexElementsSuppressedExceptions;
195104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                final int suppressedExceptionsLength = oldSuppressedExceptions.length +
196104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                        newSuppressedExceptions.length;
197104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                dexElementsSuppressedExceptions = new IOException[suppressedExceptionsLength];
198104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                System.arraycopy(oldSuppressedExceptions, 0, dexElementsSuppressedExceptions,
199104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                        0, oldSuppressedExceptions.length);
200104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                System.arraycopy(newSuppressedExceptions, 0, dexElementsSuppressedExceptions,
201104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                        oldSuppressedExceptions.length, newSuppressedExceptions.length);
202104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy            } else {
203104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy                dexElementsSuppressedExceptions = newSuppressedExceptions;
204104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy            }
205104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy        }
206104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy    }
207104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy
208104fb10863f65fe04af2b40526d0d0abe3ba3e92Todd Kennedy    /**
2092cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Splits the given dex path string into elements using the path
2102cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * separator, pruning out any elements that do not refer to existing
211384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov     * and readable files.
2122cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
2137694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov    private static List<File> splitDexPath(String path) {
214384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        return splitPaths(path, false);
2152cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
2162cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
2172cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
2182cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Splits the given path strings into file elements using the path
2192cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * separator, combining the results and filtering out elements
2202cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * that don't exist, aren't readable, or aren't either a regular
2212cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * file or a directory (as specified). Either string may be empty
2222cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * or {@code null}, in which case it is ignored. If both strings
2232cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * are empty or {@code null}, or all elements get pruned out, then
2242cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * this returns a zero-element list.
2252cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
226384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov    private static List<File> splitPaths(String searchPath, boolean directoriesOnly) {
227384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        List<File> result = new ArrayList<>();
2282cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
229384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        if (searchPath != null) {
230384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov            for (String path : searchPath.split(File.pathSeparator)) {
231384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                if (directoriesOnly) {
232384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                    try {
233384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                        StructStat sb = Libcore.os.stat(path);
234384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                        if (!S_ISDIR(sb.st_mode)) {
235384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                            continue;
236384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                        }
237384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                    } catch (ErrnoException ignored) {
238384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                        continue;
239384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                    }
2402cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                }
241384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                result.add(new File(path));
2422cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
2432cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
244384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
245384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        return result;
2462cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
2472cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
2482cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
2492cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Makes an array of dex/resource path elements, one per element of
2502cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * the given array.
2512cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
2527694b783f48e2cc57928b61c84fd90311cb0c35aDmitriy Ivanov    private static Element[] makeDexElements(List<File> files, File optimizedDirectory,
253f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            List<IOException> suppressedExceptions, ClassLoader loader) {
254f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe      Element[] elements = new Element[files.size()];
255f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe      int elementsPos = 0;
256f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe      /*
257f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe       * Open all files and load the (direct or contained) dex files up front.
258f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe       */
259f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe      for (File file : files) {
260f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe          if (file.isDirectory()) {
261f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe              // We support directories for looking up resources. Looking up resources in
262f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe              // directories is useful for running libcore tests.
263f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe              elements[elementsPos++] = new Element(file);
264f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe          } else if (file.isFile()) {
265f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe              String name = file.getName();
266f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
267f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe              if (name.endsWith(DEX_SUFFIX)) {
268f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                  // Raw dex file (not inside a zip/jar).
269f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                  try {
270f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                      DexFile dex = loadDexFile(file, optimizedDirectory, loader, elements);
271f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                      if (dex != null) {
272f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                          elements[elementsPos++] = new Element(dex, null);
273f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                      }
274f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                  } catch (IOException suppressed) {
275f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                      System.logE("Unable to load dex file: " + file, suppressed);
276f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                      suppressedExceptions.add(suppressed);
277f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                  }
278f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe              } else {
279f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                  DexFile dex = null;
280f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                  try {
281f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                      dex = loadDexFile(file, optimizedDirectory, loader, elements);
282f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                  } catch (IOException suppressed) {
283f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                      /*
284f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                       * IOException might get thrown "legitimately" by the DexFile constructor if
285f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                       * the zip file turns out to be resource-only (that is, no classes.dex file
286f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                       * in it).
287f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                       * Let dex == null and hang on to the exception to add to the tea-leaves for
288f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                       * when findClass returns null.
289f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                       */
290f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                      suppressedExceptions.add(suppressed);
291f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                  }
292f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
293f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                  if (dex == null) {
294f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                      elements[elementsPos++] = new Element(file);
295f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                  } else {
296f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                      elements[elementsPos++] = new Element(dex, file);
297f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                  }
298f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe              }
299f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe          } else {
300f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe              System.logW("ClassLoader referenced unknown path: " + file);
301f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe          }
302f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe      }
303f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe      if (elementsPos != elements.length) {
304f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe          elements = Arrays.copyOf(elements, elementsPos);
305f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe      }
306f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe      return elements;
3072cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
3082cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
3092cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
31070fcc38e7a03f04864499a7356ae1f1e692b7463Mathieu Chartier     * Constructs a {@code DexFile} instance, as appropriate depending on whether
31170fcc38e7a03f04864499a7356ae1f1e692b7463Mathieu Chartier     * {@code optimizedDirectory} is {@code null}. An application image file may be associated with
31270fcc38e7a03f04864499a7356ae1f1e692b7463Mathieu Chartier     * the {@code loader} if it is not null.
3132cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
314da3729f76948e5c7ce5eadbdfc03a0f9fe7d4d83Mathieu Chartier    private static DexFile loadDexFile(File file, File optimizedDirectory, ClassLoader loader,
315da3729f76948e5c7ce5eadbdfc03a0f9fe7d4d83Mathieu Chartier                                       Element[] elements)
3162cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            throws IOException {
3172cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        if (optimizedDirectory == null) {
318da3729f76948e5c7ce5eadbdfc03a0f9fe7d4d83Mathieu Chartier            return new DexFile(file, loader, elements);
3192cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        } else {
3202cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            String optimizedPath = optimizedPathFor(file, optimizedDirectory);
321da3729f76948e5c7ce5eadbdfc03a0f9fe7d4d83Mathieu Chartier            return DexFile.loadDex(file.getPath(), optimizedPath, 0, loader, elements);
3222cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
3232cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
3242cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
3252cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
3262cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Converts a dex/jar file path and an output directory to an
327ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom     * output file path for an associated optimized dex file.
3282cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
3292cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    private static String optimizedPathFor(File path,
3302cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            File optimizedDirectory) {
331ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom        /*
332ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * Get the filename component of the path, and replace the
333ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * suffix with ".dex" if that's not already the suffix.
334ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         *
335ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * We don't want to use ".odex", because the build system uses
336ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * that for files that are paired with resource-only jar
337ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * files. If the VM can assume that there's no classes.dex in
338ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * the matching jar, it doesn't need to open the jar to check
339ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * for updated dependencies, providing a slight performance
340ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * boost at startup. The use of ".dex" here matches the use on
341ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         * files in /data/dalvik-cache.
342ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom         */
343ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom        String fileName = path.getName();
344ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom        if (!fileName.endsWith(DEX_SUFFIX)) {
345ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom            int lastDot = fileName.lastIndexOf(".");
346ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom            if (lastDot < 0) {
347ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom                fileName += DEX_SUFFIX;
348ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom            } else {
349ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom                StringBuilder sb = new StringBuilder(lastDot + 4);
350ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom                sb.append(fileName, 0, lastDot);
351ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom                sb.append(DEX_SUFFIX);
352ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom                fileName = sb.toString();
353ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom            }
354ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom        }
355ca2c58ceaf2d35d30fe06b1676cc1436a24c4d30Brian Carlstrom
3562cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        File result = new File(optimizedDirectory, fileName);
3572cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        return result.getPath();
3582cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
3592cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
360f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe    /*
361f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe     * TODO (dimitry): Revert after apps stops relying on the existence of this
362f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe     * method (see http://b/21957414 and http://b/26317852 for details)
363f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe     */
364f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe    @SuppressWarnings("unused")
365f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe    private static Element[] makePathElements(List<File> files, File optimizedDirectory,
366f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            List<IOException> suppressedExceptions) {
367f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        return makeDexElements(files, optimizedDirectory, suppressedExceptions, null);
368f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe    }
369f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
370f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe    /**
371f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe     * Makes an array of directory/zip path elements for the native library search path, one per
372f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe     * element of the given array.
373f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe     */
374f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe    private static NativeLibraryElement[] makePathElements(List<File> files) {
375f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        NativeLibraryElement[] elements = new NativeLibraryElement[files.size()];
376f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        int elementsPos = 0;
377f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        for (File file : files) {
378f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            String path = file.getPath();
379f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
380f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            if (path.contains(zipSeparator)) {
381f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                String split[] = path.split(zipSeparator, 2);
382f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                File zip = new File(split[0]);
383f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                String dir = split[1];
384f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                elements[elementsPos++] = new NativeLibraryElement(zip, dir);
385f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            } else if (file.isDirectory()) {
386f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                // We support directories for looking up native libraries.
387f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                elements[elementsPos++] = new NativeLibraryElement(file);
388f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            } else {
389f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                System.logW("ClassLoader referenced unknown path: " + file);
390f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            }
391f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        }
392f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        if (elementsPos != elements.length) {
393f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            elements = Arrays.copyOf(elements, elementsPos);
394f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        }
395f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        return elements;
396f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe    }
397f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
3982cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
3992cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Finds the named class in one of the dex files pointed at by
4002cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * this instance. This will find the one in the earliest listed
4012cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * path element. If the class is found but has not yet been
4022cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * defined, then this method will define it in the defining
4032cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * context that this instance was constructed with.
4042cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     *
4052cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * @param name of class to find
4062cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * @param suppressed exceptions encountered whilst finding the class
4072cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * @return the named class or {@code null} if the class is not
4082cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * found in any of the dex files
4092cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
410f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe    public Class<?> findClass(String name, List<Throwable> suppressed) {
4112cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        for (Element element : dexElements) {
412f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            Class<?> clazz = element.findClass(name, definingContext, suppressed);
413f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            if (clazz != null) {
414f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                return clazz;
4152cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
4162cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
417f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
4181e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers        if (dexElementsSuppressedExceptions != null) {
4191e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers            suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
4201e93f6737ef1a3540c8ce19c4fe3fa2efcdc1c78Ian Rogers        }
4212cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        return null;
4222cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
4232cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
4242cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
4252cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Finds the named resource in one of the zip/jar files pointed at
4262cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * by this instance. This will find the one in the earliest listed
4272cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * path element.
4282cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     *
4292cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * @return a URL to the named resource or {@code null} if the
4302cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * resource is not found in any of the zip/jar files
4312cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
4322cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    public URL findResource(String name) {
4332cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        for (Element element : dexElements) {
4342cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            URL url = element.findResource(name);
4352cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            if (url != null) {
4362cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                return url;
4372cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
4382cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
4392cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
4402cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        return null;
4412cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
4422cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
4432cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
4442cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Finds all the resources with the given name, returning an
4452cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * enumeration of them. If there are no resources with the given
4462cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * name, then this method returns an empty enumeration.
4472cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
4482cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    public Enumeration<URL> findResources(String name) {
4492cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        ArrayList<URL> result = new ArrayList<URL>();
4502cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
4512cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        for (Element element : dexElements) {
4522cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            URL url = element.findResource(name);
4532cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            if (url != null) {
4542cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                result.add(url);
4552cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
4562cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
4572cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
4582cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        return Collections.enumeration(result);
4592cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
4602cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
4612cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
4622cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * Finds the named native code library on any of the library
4632cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * directories pointed at by this instance. This will find the
4642cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * one in the earliest listed directory, ignoring any that are not
4652cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * readable regular files.
4662cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     *
4672cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * @return the complete path to the library or {@code null} if no
4682cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     * library was found
4692cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
4702cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    public String findLibrary(String libraryName) {
4712cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        String fileName = System.mapLibraryName(libraryName);
472384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
473f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        for (NativeLibraryElement element : nativeLibraryPathElements) {
474384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov            String path = element.findNativeLibrary(fileName);
475384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
476384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov            if (path != null) {
4772cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                return path;
4782cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
4792cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
480384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
4812cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        return null;
4822cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
4832cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
4842cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /**
485f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe     * Element of the dex/resource path. Note: should be called DexElement, but apps reflect on
486f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe     * this.
4872cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom     */
4882cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    /*package*/ static class Element {
489f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        /**
490f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe         * A file denoting a zip file (in case of a resource jar or a dex jar), or a directory
491f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe         * (only when dexFile is null).
492f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe         */
493f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        private final File path;
494f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
4952cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        private final DexFile dexFile;
4962cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
4972ce899fcb81707dd5447a15c29c2c137697f2f5eNeil Fuller        private ClassPathURLStreamHandler urlHandler;
4982cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        private boolean initialized;
4992cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
500f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        /**
501f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe         * Element encapsulates a dex file. This may be a plain dex file (in which case dexZipPath
502f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe         * should be null), or a jar (in which case dexZipPath should denote the zup file).
503f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe         */
504f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        public Element(DexFile dexFile, File dexZipPath) {
5052cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            this.dexFile = dexFile;
506f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            this.path = dexZipPath;
507f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        }
508f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
509f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        public Element(File path) {
510f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe          this.path = path;
511f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe          this.dexFile = null;
5122cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
5132cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
514f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        @Override
515f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        public String toString() {
516f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            if (dexFile == null) {
517f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe              return (path.isDirectory() ? "directory \"" : "zip file \"") + path + "\"";
5182cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            } else {
519f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe              if (path == null) {
5202cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                return "dex file \"" + dexFile + "\"";
521f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe              } else {
522f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                return "zip file \"" + path + "\"";
523f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe              }
5242cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
5252cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
5262cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
5272cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        public synchronized void maybeInit() {
5282cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            if (initialized) {
5292cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                return;
5302cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
5312cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            initialized = true;
5322cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
533f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            if (path == null || path.isDirectory()) {
5342cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                return;
5352cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
5362cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
5372cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            try {
538f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                urlHandler = new ClassPathURLStreamHandler(path.getPath());
5392cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            } catch (IOException ioe) {
5402cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                /*
5412cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                 * Note: ZipException (a subclass of IOException)
5422cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                 * might get thrown by the ZipFile constructor
5432cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                 * (e.g. if the file isn't actually a zip/jar
5442cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                 * file).
5452cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                 */
546f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                System.logE("Unable to open zip file: " + path, ioe);
547384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov                urlHandler = null;
548384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov            }
549384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov        }
550384730cb57f41235f09829355d7ce67132625f7fDmitriy Ivanov
551f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        public Class<?> findClass(String name, ClassLoader definingContext,
552f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                List<Throwable> suppressed) {
553f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)
554f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                    : null;
5552cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        }
5562cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
5572cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom        public URL findResource(String name) {
5582cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            maybeInit();
5592cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
560f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            if (urlHandler != null) {
561f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe              return urlHandler.getEntryUrlOrNull(name);
562f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            }
563f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
5642cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            // We support directories so we can run tests and/or legacy code
5652cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            // that uses Class.getResource.
566f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            if (path != null && path.isDirectory()) {
567f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                File resourceFile = new File(path, name);
5682cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                if (resourceFile.exists()) {
5692cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                    try {
5702cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                        return resourceFile.toURI().toURL();
5712cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                    } catch (MalformedURLException ex) {
5722cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                        throw new RuntimeException(ex);
5732cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                    }
5742cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                }
5752cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
5762cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom
577f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            return null;
578f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        }
579f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe    }
580f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
581f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe    /**
582f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe     * Element of the native library path
583f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe     */
584f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe    /*package*/ static class NativeLibraryElement {
585f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        /**
586f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe         * A file denoting a directory or zip file.
587f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe         */
588f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        private final File path;
589f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
590f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        /**
591f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe         * If path denotes a zip file, this denotes a base path inside the zip.
592f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe         */
593f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        private final String zipDir;
594f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
595f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        private ClassPathURLStreamHandler urlHandler;
596f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        private boolean initialized;
597f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
598f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        public NativeLibraryElement(File dir) {
599f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            this.path = dir;
600f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            this.zipDir = null;
601f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
602f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            // We should check whether path is a directory, but that is non-eliminatable overhead.
603f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        }
604f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
605f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        public NativeLibraryElement(File zip, String zipDir) {
606f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            this.path = zip;
607f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            this.zipDir = zipDir;
608f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
609f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            // Simple check that should be able to be eliminated by inlining. We should also
610f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            // check whether path is a file, but that is non-eliminatable overhead.
611f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            if (zipDir == null) {
612f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe              throw new IllegalArgumentException();
613f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            }
614f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        }
615f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
616f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        @Override
617f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        public String toString() {
618f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            if (zipDir == null) {
619f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                return "directory \"" + path + "\"";
620f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            } else {
621f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                return "zip file \"" + path + "\"" +
622f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                  (!zipDir.isEmpty() ? ", dir \"" + zipDir + "\"" : "");
623f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            }
624f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        }
625f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
626f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        public synchronized void maybeInit() {
627f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            if (initialized) {
628f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                return;
629f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            }
630f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            initialized = true;
631f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
632f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            if (zipDir == null) {
633f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                return;
634f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            }
635f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
636f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            try {
637f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                urlHandler = new ClassPathURLStreamHandler(path.getPath());
638f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            } catch (IOException ioe) {
639f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                /*
640f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                 * Note: ZipException (a subclass of IOException)
641f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                 * might get thrown by the ZipFile constructor
642f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                 * (e.g. if the file isn't actually a zip/jar
643f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                 * file).
6442cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom                 */
645f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                System.logE("Unable to open zip file: " + path, ioe);
646f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                urlHandler = null;
647f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            }
648f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        }
649f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
650f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe        public String findNativeLibrary(String name) {
651f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            maybeInit();
652f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
653f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            if (zipDir == null) {
654f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                String entryPath = new File(path, name).getPath();
655f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                if (IoUtils.canOpenReadOnly(entryPath)) {
656f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                    return entryPath;
657f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                }
658f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            } else if (urlHandler != null) {
659f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                // Having a urlHandler means the element has a zip file.
660f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                // In this case Android supports loading the library iff
661f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                // it is stored in the zip uncompressed.
662f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                String entryName = zipDir + '/' + name;
663f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                if (urlHandler.isEntryStored(entryName)) {
664f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                  return path.getPath() + zipSeparator + entryName;
665f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe                }
6662cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom            }
667f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe
668f2e5d9ce27779b4c047f1c1ba8af32fd26d2ae05Andreas Gampe            return null;
6691a796cbc5dfb263511f2f4e5213adbc1d396a813Neil Fuller        }
6702cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom    }
6712cf03dc15c40b92634ff606694af5a6e9aa4db09Brian Carlstrom}
672