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