1ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein/*
2ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein * Copyright (C) 2011 The Android Open Source Project
3ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein *
4ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein * Licensed under the Apache License, Version 2.0 (the "License");
5ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein * you may not use this file except in compliance with the License.
6ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein * You may obtain a copy of the License at
7ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein *
8ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein *      http://www.apache.org/licenses/LICENSE-2.0
9ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein *
10ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein * Unless required by applicable law or agreed to in writing, software
11ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein * distributed under the License is distributed on an "AS IS" BASIS,
12ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein * See the License for the specific language governing permissions and
14ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein * limitations under the License.
15ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein */
16ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
17ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornsteinpackage dalvik.system;
18ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
19ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornsteinimport java.io.File;
20ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornsteinimport java.io.IOException;
21ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornsteinimport java.net.MalformedURLException;
22ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornsteinimport java.net.URL;
23ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornsteinimport java.util.ArrayList;
24ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornsteinimport java.util.Collections;
25ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornsteinimport java.util.Enumeration;
26ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornsteinimport java.util.regex.Pattern;
27ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornsteinimport java.util.zip.ZipFile;
28ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
29ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein/**
30ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein * A pair of lists of entries, associated with a {@code ClassLoader}.
31ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein * One of the lists is a dex/resource path — typically referred
32ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein * to as a "class path" — list, and the other names directories
33ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein * containing native code libraries. Class path entries may be any of:
34ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein * a {@code .jar} or {@code .zip} file containing an optional
35ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein * top-level {@code classes.dex} file as well as arbitrary resources,
36ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein * or a plain {@code .dex} file (with no possibility of associated
37ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein * resources).
38ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein *
39ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein * <p>This class also contains methods to use these lists to look up
40ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein * classes and resources.</p>
41ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein */
42ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein/*package*/ final class DexPathList {
43ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    private static final String DEX_SUFFIX = ".dex";
44ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    private static final String JAR_SUFFIX = ".jar";
45ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    private static final String ZIP_SUFFIX = ".zip";
46ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    private static final String APK_SUFFIX = ".apk";
47ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
48ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    /** class definition context */
49ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    private final ClassLoader definingContext;
50ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
51ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    /** list of dex/resource (class path) elements */
52ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    private final Element[] dexElements;
53ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
54ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    /** list of native library directory elements */
55ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    private final File[] nativeLibraryDirectories;
56ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
57ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    /**
58ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * Constructs an instance.
59ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     *
60ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * @param definingContext the context in which any as-yet unresolved
61ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * classes should be defined
62ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * @param dexPath list of dex/resource path elements, separated by
63ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * {@code File.pathSeparator}
64ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * @param libraryPath list of native library directory path elements,
65ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * separated by {@code File.pathSeparator}
66ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * @param optimizedDirectory directory where optimized {@code .dex} files
67ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * should be found and written to, or {@code null} to use the default
68ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * system directory for same
69ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     */
70ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    public DexPathList(ClassLoader definingContext, String dexPath,
71ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            String libraryPath, File optimizedDirectory) {
72ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        if (definingContext == null) {
73ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            throw new NullPointerException("definingContext == null");
74ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        }
75ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
76ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        if (dexPath == null) {
77ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            throw new NullPointerException("dexPath == null");
78ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        }
79ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
80ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        if (optimizedDirectory != null) {
81ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            if (!optimizedDirectory.exists())  {
82ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                throw new IllegalArgumentException(
83ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                        "optimizedDirectory doesn't exist: "
84ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                        + optimizedDirectory);
85ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            }
86ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
87ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            if (!(optimizedDirectory.canRead()
88ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                            && optimizedDirectory.canWrite())) {
89ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                throw new IllegalArgumentException(
90ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                        "optimizedDirectory not readable/writable: "
91ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                        + optimizedDirectory);
92ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            }
93ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        }
94ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
95ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        this.definingContext = definingContext;
96ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        this.dexElements =
97ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            makeDexElements(splitDexPath(dexPath), optimizedDirectory);
98ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        this.nativeLibraryDirectories = splitLibraryPath(libraryPath);
99ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    }
100ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
101ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    /**
102ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * Splits the given dex path string into elements using the path
103ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * separator, pruning out any elements that do not refer to existing
104ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * and readable files. (That is, directories are not included in the
105ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * result.)
106ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     */
107ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    private static ArrayList<File> splitDexPath(String path) {
108ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        return splitPaths(path, null, false);
109ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    }
110ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
111ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    /**
112ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * Splits the given library directory path string into elements
113ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * using the path separator ({@code File.pathSeparator}, which
114ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * defaults to {@code ":"} on Android, appending on the elements
115ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * from the system library path, and pruning out any elements that
116ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * do not refer to existing and readable directories.
117ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     */
118ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    private static File[] splitLibraryPath(String path) {
119ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        /*
120ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein         * Native libraries may exist in both the system and
121ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein         * application library paths, and we use this search order:
122ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein         *
123ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein         *   1. this class loader's library path for application
124ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein         *      libraries
125ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein         *   2. the VM's library path from the system
126ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein         *      property for system libraries
127ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein         *
128ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein         * This order was reversed prior to Gingerbread; see http://b/2933456.
129ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein         */
130ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        ArrayList<File> result = splitPaths(
131ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                path, System.getProperty("java.library.path", "."), true);
132ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        return result.toArray(new File[result.size()]);
133ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    }
134ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
135ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    /**
136ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * Splits the given path strings into file elements using the path
137ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * separator, combining the results and filtering out elements
138ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * that don't exist, aren't readable, or aren't either a regular
139ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * file or a directory (as specified). Either string may be empty
140ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * or {@code null}, in which case it is ignored. If both strings
141ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * are empty or {@code null}, or all elements get pruned out, then
142ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * this returns a zero-element list.
143ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     */
144ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    private static ArrayList<File> splitPaths(String path1, String path2,
145ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            boolean wantDirectories) {
146ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        ArrayList<File> result = new ArrayList<File>();
147ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
148ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        splitAndAdd(path1, wantDirectories, result);
149ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        splitAndAdd(path2, wantDirectories, result);
150ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        return result;
151ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    }
152ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
153ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    /**
154ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * Helper for {@link #splitPaths}, which does the actual splitting
155ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * and filtering and adding to a result.
156ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     */
157ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    private static void splitAndAdd(String path, boolean wantDirectories,
158ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            ArrayList<File> resultList) {
159ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        if (path == null) {
160ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            return;
161ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        }
162ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
163ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        String[] strings = path.split(Pattern.quote(File.pathSeparator));
164ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
165ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        for (String s : strings) {
166ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            File file = new File(s);
167ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
168ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            if (! (file.exists() && file.canRead())) {
169ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                continue;
170ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            }
171ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
172ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            /*
173ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein             * Note: There are other entities in filesystems than
174ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein             * regular files and directories.
175ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein             */
176ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            if (wantDirectories) {
177ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                if (!file.isDirectory()) {
178ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                    continue;
179ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                }
180ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            } else {
181ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                if (!file.isFile()) {
182ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                    continue;
183ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                }
184ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            }
185ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
186ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            resultList.add(file);
187ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        }
188ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    }
189ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
190ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    /**
191ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * Makes an array of dex/resource path elements, one per element of
192ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * the given array.
193ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     */
194ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    private static Element[] makeDexElements(ArrayList<File> files,
195ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            File optimizedDirectory) {
196ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        ArrayList<Element> elements = new ArrayList<Element>();
197ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
198ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        /*
199ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein         * Open all files and load the (direct or contained) dex files
200ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein         * up front.
201ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein         */
202ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        for (File file : files) {
2038dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath            File zip = null;
204ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            DexFile dex = null;
205ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            String name = file.getName();
206ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
207ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            if (name.endsWith(DEX_SUFFIX)) {
208ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                // Raw dex file (not inside a zip/jar).
209ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                try {
210ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                    dex = loadDexFile(file, optimizedDirectory);
211ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                } catch (IOException ex) {
212a7ef55258ac71153487357b861c7639d627df82fElliott Hughes                    System.logE("Unable to load dex file: " + file, ex);
213ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                }
214ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            } else if (name.endsWith(APK_SUFFIX) || name.endsWith(JAR_SUFFIX)
215ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                    || name.endsWith(ZIP_SUFFIX)) {
2168dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath                zip = file;
217ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
218ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                try {
219ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                    dex = loadDexFile(file, optimizedDirectory);
220ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                } catch (IOException ignored) {
221ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                    /*
222ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                     * IOException might get thrown "legitimately" by
223ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                     * the DexFile constructor if the zip file turns
224ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                     * out to be resource-only (that is, no
225ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                     * classes.dex file in it). Safe to just ignore
226ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                     * the exception here, and let dex == null.
227ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                     */
228ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                }
229ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            } else {
230a7ef55258ac71153487357b861c7639d627df82fElliott Hughes                System.logW("Unknown file type for: " + file);
231ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            }
232ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
233ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            if ((zip != null) || (dex != null)) {
234ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                elements.add(new Element(file, zip, dex));
235ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            }
236ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        }
237ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
238ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        return elements.toArray(new Element[elements.size()]);
239ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    }
240ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
241ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    /**
242ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * Constructs a {@code DexFile} instance, as appropriate depending
243ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * on whether {@code optimizedDirectory} is {@code null}.
244ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     */
245ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    private static DexFile loadDexFile(File file, File optimizedDirectory)
246ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            throws IOException {
247ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        if (optimizedDirectory == null) {
248ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            return new DexFile(file);
249ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        } else {
250ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            String optimizedPath = optimizedPathFor(file, optimizedDirectory);
251ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            return DexFile.loadDex(file.getPath(), optimizedPath, 0);
252ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        }
253ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    }
254ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
255ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    /**
256ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * Converts a dex/jar file path and an output directory to an
257ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * output file path for an associated optimized dex file.
258ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     */
259ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    private static String optimizedPathFor(File path,
260ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            File optimizedDirectory) {
261ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        /*
262ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein         * Get the filename component of the path, and replace the
263ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein         * suffix with ".dex" if that's not already the suffix.
264ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein         *
265ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein         * We don't want to use ".odex", because the build system uses
266ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein         * that for files that are paired with resource-only jar
267ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein         * files. If the VM can assume that there's no classes.dex in
268ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein         * the matching jar, it doesn't need to open the jar to check
269ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein         * for updated dependencies, providing a slight performance
270ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein         * boost at startup. The use of ".dex" here matches the use on
271ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein         * files in /data/dalvik-cache.
272ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein         */
273ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        String fileName = path.getName();
274ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        if (!fileName.endsWith(DEX_SUFFIX)) {
275ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            int lastDot = fileName.lastIndexOf(".");
276ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            if (lastDot < 0) {
277ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                fileName += DEX_SUFFIX;
278ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            } else {
279ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                StringBuilder sb = new StringBuilder(lastDot + 4);
280ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                sb.append(fileName, 0, lastDot);
281ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                sb.append(DEX_SUFFIX);
282ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                fileName = sb.toString();
283ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            }
284ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        }
285ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
286ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        File result = new File(optimizedDirectory, fileName);
287ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        return result.getPath();
288ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    }
289ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
290ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    /**
291ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * Finds the named class in one of the dex files pointed at by
292ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * this instance. This will find the one in the earliest listed
293ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * path element. If the class is found but has not yet been
294ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * defined, then this method will define it in the defining
295ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * context that this instance was constructed with.
296ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     *
297ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * @return the named class or {@code null} if the class is not
298ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * found in any of the dex files
299ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     */
300ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    public Class findClass(String name) {
301ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        for (Element element : dexElements) {
302ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            DexFile dex = element.dexFile;
303ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
304ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            if (dex != null) {
305ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                Class clazz = dex.loadClassBinaryName(name, definingContext);
306ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                if (clazz != null) {
307ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                    return clazz;
308ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                }
309ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            }
310ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        }
311ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
312ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        return null;
313ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    }
314ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
315ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    /**
316ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * Finds the named resource in one of the zip/jar files pointed at
317ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * by this instance. This will find the one in the earliest listed
318ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * path element.
319ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     *
320ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * @return a URL to the named resource or {@code null} if the
321ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * resource is not found in any of the zip/jar files
322ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     */
323ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    public URL findResource(String name) {
324ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        for (Element element : dexElements) {
325ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            URL url = element.findResource(name);
326ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            if (url != null) {
327ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                return url;
328ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            }
329ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        }
330ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
331ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        return null;
332ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    }
333ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
334ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    /**
335ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * Finds all the resources with the given name, returning an
336ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * enumeration of them. If there are no resources with the given
337ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * name, then this method returns an empty enumeration.
338ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     */
339ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    public Enumeration<URL> findResources(String name) {
340ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        ArrayList<URL> result = new ArrayList<URL>();
341ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
342ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        for (Element element : dexElements) {
343ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            URL url = element.findResource(name);
344ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            if (url != null) {
345ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                result.add(url);
346ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            }
347ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        }
348ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
349ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        return Collections.enumeration(result);
350ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    }
351ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
352ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    /**
353ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * Finds the named native code library on any of the library
354ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * directories pointed at by this instance. This will find the
355ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * one in the earliest listed directory, ignoring any that are not
356ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * readable regular files.
357ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     *
358ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * @return the complete path to the library or {@code null} if no
359ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * library was found
360ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     */
361ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    public String findLibrary(String libraryName) {
362ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        String fileName = System.mapLibraryName(libraryName);
363ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
364ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        for (File directory : nativeLibraryDirectories) {
365ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            File file = new File(directory, fileName);
366ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            if (file.exists() && file.isFile() && file.canRead()) {
367ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                return file.getPath();
368ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            }
369ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        }
370ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
371ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        return null;
372ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    }
373ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
374ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    /**
375ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     * Element of the dex/resource file path
376ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein     */
377ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    /*package*/ static class Element {
3788dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath        private final File file;
3798dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath        private final File zip;
3808dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath        private final DexFile dexFile;
381ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
3828dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath        private ZipFile zipFile;
3838dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath        private boolean init;
3848dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath
3858dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath        public Element(File file, File zip, DexFile dexFile) {
386ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            this.file = file;
3878dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath            this.zip = zip;
388ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            this.dexFile = dexFile;
389ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        }
390ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
3918dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath        public synchronized void maybeInit() {
3928dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath            if (init) {
3938dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath                return;
3948dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath            }
3958dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath
3968dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath            init = true;
3978dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath
3988dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath            if (zip == null) {
399ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                /*
400ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                 * Either this element has no zip/jar file (first
401ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                 * clause), or the zip/jar file doesn't have an entry
402ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                 * for the given name (second clause).
403ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                 */
4048dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath                return;
4058dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath            }
4068dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath
4078dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath            try {
4088dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath                zipFile = new ZipFile(zip);
4098dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath            } catch (IOException ioe) {
4108dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath                /*
4118dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath                 * Note: ZipException (a subclass of IOException)
4128dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath                 * might get thrown by the ZipFile constructor
4138dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath                 * (e.g. if the file isn't actually a zip/jar
4148dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath                 * file).
4158dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath                 */
4168dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath                System.logE("Unable to open zip file: " + file, ioe);
4178dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath                zipFile = null;
4188dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath            }
4198dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath        }
4208dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath
4218dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath        public URL findResource(String name) {
4228dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath            maybeInit();
4238dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath
4248dd90ae77922eb05d46c797937620a4c69f758f7Narayan Kamath            if (zipFile == null || zipFile.getEntry(name) == null) {
425ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                return null;
426ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            }
427ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein
428ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            try {
429ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                /*
430ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                 * File.toURL() is compliant with RFC 1738 in
431ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                 * always creating absolute path names. If we
432ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                 * construct the URL by concatenating strings, we
433ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                 * might end up with illegal URLs for relative
434ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                 * names.
435ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                 */
436ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                return new URL("jar:" + file.toURL() + "!/" + name);
437ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            } catch (MalformedURLException ex) {
438ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein                throw new RuntimeException(ex);
439ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein            }
440ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein        }
441ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein    }
442ea52753a0f80fcd70acfe9150ecb854511ff38dbDan Bornstein}
443