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