DexFile.java revision 643e8cb87528bb6479eb7f25120fc11de3e4a9b3
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package dalvik.system;
18
19import android.system.ErrnoException;
20import android.system.StructStat;
21import java.io.File;
22import java.io.FileNotFoundException;
23import java.io.IOException;
24import java.util.ArrayList;
25import java.util.Enumeration;
26import java.util.List;
27import libcore.io.Libcore;
28
29/**
30 * Manipulates DEX files. The class is similar in principle to
31 * {@link java.util.zip.ZipFile}. It is used primarily by class loaders.
32 * <p>
33 * Note we don't directly open and read the DEX file here. They're memory-mapped
34 * read-only by the VM.
35 */
36public final class DexFile {
37  /**
38   * If close is called, mCookie becomes null but the internal cookie is preserved if the close
39   * failed so that we can free resources in the finalizer.
40   */
41    private Object mCookie;
42    private Object mInternalCookie;
43    private final String mFileName;
44    private final CloseGuard guard = CloseGuard.get();
45
46    /**
47     * Opens a DEX file from a given File object. This will usually be a ZIP/JAR
48     * file with a "classes.dex" inside.
49     *
50     * The VM will generate the name of the corresponding file in
51     * /data/dalvik-cache and open it, possibly creating or updating
52     * it first if system permissions allow.  Don't pass in the name of
53     * a file in /data/dalvik-cache, as the named file is expected to be
54     * in its original (pre-dexopt) state.
55     *
56     * @param file
57     *            the File object referencing the actual DEX file
58     *
59     * @throws IOException
60     *             if an I/O error occurs, such as the file not being found or
61     *             access rights missing for opening it
62     */
63    public DexFile(File file) throws IOException {
64        this(file.getPath());
65    }
66    /*
67     * Private version with class loader argument.
68     *
69     * @param file
70     *            the File object referencing the actual DEX file
71     * @param loader
72     *            the class loader object creating the DEX file object
73     * @param elements
74     *            the temporary dex path list elements from DexPathList.makeElements
75     */
76    DexFile(File file, ClassLoader loader, DexPathList.Element[] elements)
77            throws IOException {
78        this(file.getPath(), loader, elements);
79    }
80
81    /**
82     * Opens a DEX file from a given filename. This will usually be a ZIP/JAR
83     * file with a "classes.dex" inside.
84     *
85     * The VM will generate the name of the corresponding file in
86     * /data/dalvik-cache and open it, possibly creating or updating
87     * it first if system permissions allow.  Don't pass in the name of
88     * a file in /data/dalvik-cache, as the named file is expected to be
89     * in its original (pre-dexopt) state.
90     *
91     * @param fileName
92     *            the filename of the DEX file
93     *
94     * @throws IOException
95     *             if an I/O error occurs, such as the file not being found or
96     *             access rights missing for opening it
97     */
98    public DexFile(String fileName) throws IOException {
99        this(fileName, null, null);
100    }
101
102    /*
103     * Private version with class loader argument.
104     *
105     * @param fileName
106     *            the filename of the DEX file
107     * @param loader
108     *            the class loader creating the DEX file object
109     * @param elements
110     *            the temporary dex path list elements from DexPathList.makeElements
111     */
112    DexFile(String fileName, ClassLoader loader, DexPathList.Element[] elements) throws IOException {
113        mCookie = openDexFile(fileName, null, 0, loader, elements);
114        mInternalCookie = mCookie;
115        mFileName = fileName;
116        guard.open("close");
117        //System.out.println("DEX FILE cookie is " + mCookie + " fileName=" + fileName);
118    }
119
120    /**
121     * Opens a DEX file from a given filename, using a specified file
122     * to hold the optimized data.
123     *
124     * @param sourceName
125     *  Jar or APK file with "classes.dex".
126     * @param outputName
127     *  File that will hold the optimized form of the DEX data.
128     * @param flags
129     *  Enable optional features.
130     * @param loader
131     *  The class loader creating the DEX file object.
132     * @param elements
133     *  The temporary dex path list elements from DexPathList.makeElements
134     */
135    private DexFile(String sourceName, String outputName, int flags, ClassLoader loader,
136            DexPathList.Element[] elements) throws IOException {
137        if (outputName != null) {
138            try {
139                String parent = new File(outputName).getParent();
140                if (Libcore.os.getuid() != Libcore.os.stat(parent).st_uid) {
141                    throw new IllegalArgumentException("Optimized data directory " + parent
142                            + " is not owned by the current user. Shared storage cannot protect"
143                            + " your application from code injection attacks.");
144                }
145            } catch (ErrnoException ignored) {
146                // assume we'll fail with a more contextual error later
147            }
148        }
149
150        mCookie = openDexFile(sourceName, outputName, flags, loader, elements);
151        mFileName = sourceName;
152        //System.out.println("DEX FILE cookie is " + mCookie + " sourceName=" + sourceName + " outputName=" + outputName);
153    }
154
155    /**
156     * Open a DEX file, specifying the file in which the optimized DEX
157     * data should be written.  If the optimized form exists and appears
158     * to be current, it will be used; if not, the VM will attempt to
159     * regenerate it.
160     *
161     * This is intended for use by applications that wish to download
162     * and execute DEX files outside the usual application installation
163     * mechanism.  This function should not be called directly by an
164     * application; instead, use a class loader such as
165     * dalvik.system.DexClassLoader.
166     *
167     * @param sourcePathName
168     *  Jar or APK file with "classes.dex".  (May expand this to include
169     *  "raw DEX" in the future.)
170     * @param outputPathName
171     *  File that will hold the optimized form of the DEX data.
172     * @param flags
173     *  Enable optional features.  (Currently none defined.)
174     * @return
175     *  A new or previously-opened DexFile.
176     * @throws IOException
177     *  If unable to open the source or output file.
178     */
179    static public DexFile loadDex(String sourcePathName, String outputPathName,
180        int flags) throws IOException {
181
182        /*
183         * TODO: we may want to cache previously-opened DexFile objects.
184         * The cache would be synchronized with close().  This would help
185         * us avoid mapping the same DEX more than once when an app
186         * decided to open it multiple times.  In practice this may not
187         * be a real issue.
188         */
189        return loadDex(sourcePathName, outputPathName, flags, null, null);
190    }
191
192    /*
193     * Private version of loadDex that also takes a class loader.
194     *
195     * @param sourcePathName
196     *  Jar or APK file with "classes.dex".  (May expand this to include
197     *  "raw DEX" in the future.)
198     * @param outputPathName
199     *  File that will hold the optimized form of the DEX data.
200     * @param flags
201     *  Enable optional features.  (Currently none defined.)
202     * @param loader
203     *  Class loader that is aloading the DEX file.
204     * @param elements
205     *  The temporary dex path list elements from DexPathList.makeElements
206     * @return
207     *  A new or previously-opened DexFile.
208     * @throws IOException
209     *  If unable to open the source or output file.
210     */
211    static DexFile loadDex(String sourcePathName, String outputPathName,
212        int flags, ClassLoader loader, DexPathList.Element[] elements) throws IOException {
213
214        /*
215         * TODO: we may want to cache previously-opened DexFile objects.
216         * The cache would be synchronized with close().  This would help
217         * us avoid mapping the same DEX more than once when an app
218         * decided to open it multiple times.  In practice this may not
219         * be a real issue.
220         */
221        return new DexFile(sourcePathName, outputPathName, flags, loader, elements);
222    }
223
224    /**
225     * Gets the name of the (already opened) DEX file.
226     *
227     * @return the file name
228     */
229    public String getName() {
230        return mFileName;
231    }
232
233    @Override public String toString() {
234        return getName();
235    }
236
237    /**
238     * Closes the DEX file.
239     * <p>
240     * This may not be able to release all of the resources. If classes from this DEX file are
241     * still resident, the DEX file can't be unmapped. In the case where we do not release all
242     * the resources, close is called again in the finalizer.
243     *
244     * @throws IOException
245     *             if an I/O error occurs during closing the file, which
246     *             normally should not happen
247     */
248    public void close() throws IOException {
249        if (mInternalCookie != null) {
250            if (closeDexFile(mInternalCookie)) {
251                mInternalCookie = null;
252            }
253            guard.close();
254            mCookie = null;
255        }
256    }
257
258    /**
259     * Loads a class. Returns the class on success, or a {@code null} reference
260     * on failure.
261     * <p>
262     * If you are not calling this from a class loader, this is most likely not
263     * going to do what you want. Use {@link Class#forName(String)} instead.
264     * <p>
265     * The method does not throw {@link ClassNotFoundException} if the class
266     * isn't found because it isn't reasonable to throw exceptions wildly every
267     * time a class is not found in the first DEX file we look at.
268     *
269     * @param name
270     *            the class name, which should look like "java/lang/String"
271     *
272     * @param loader
273     *            the class loader that tries to load the class (in most cases
274     *            the caller of the method
275     *
276     * @return the {@link Class} object representing the class, or {@code null}
277     *         if the class cannot be loaded
278     */
279    public Class loadClass(String name, ClassLoader loader) {
280        String slashName = name.replace('.', '/');
281        return loadClassBinaryName(slashName, loader, null);
282    }
283
284    /**
285     * See {@link #loadClass(String, ClassLoader)}.
286     *
287     * This takes a "binary" class name to better match ClassLoader semantics.
288     *
289     * @hide
290     */
291    public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) {
292        return defineClass(name, loader, mCookie, this, suppressed);
293    }
294
295    private static Class defineClass(String name, ClassLoader loader, Object cookie,
296                                     DexFile dexFile, List<Throwable> suppressed) {
297        Class result = null;
298        try {
299            result = defineClassNative(name, loader, cookie, dexFile);
300        } catch (NoClassDefFoundError e) {
301            if (suppressed != null) {
302                suppressed.add(e);
303            }
304        } catch (ClassNotFoundException e) {
305            if (suppressed != null) {
306                suppressed.add(e);
307            }
308        }
309        return result;
310    }
311
312    /**
313     * Enumerate the names of the classes in this DEX file.
314     *
315     * @return an enumeration of names of classes contained in the DEX file, in
316     *         the usual internal form (like "java/lang/String").
317     */
318    public Enumeration<String> entries() {
319        return new DFEnum(this);
320    }
321
322    /*
323     * Helper class.
324     */
325    private class DFEnum implements Enumeration<String> {
326        private int mIndex;
327        private String[] mNameList;
328
329        DFEnum(DexFile df) {
330            mIndex = 0;
331            mNameList = getClassNameList(mCookie);
332        }
333
334        public boolean hasMoreElements() {
335            return (mIndex < mNameList.length);
336        }
337
338        public String nextElement() {
339            return mNameList[mIndex++];
340        }
341    }
342
343    /**
344     * Called when the class is finalized. Makes sure the DEX file is closed.
345     *
346     * @throws IOException
347     *             if an I/O error occurs during closing the file, which
348     *             normally should not happen
349     */
350    @Override protected void finalize() throws Throwable {
351        try {
352            if (guard != null) {
353                guard.warnIfOpen();
354            }
355            if (mInternalCookie != null && !closeDexFile(mInternalCookie)) {
356                throw new AssertionError("Failed to close dex file in finalizer.");
357            }
358            mInternalCookie = null;
359            mCookie = null;
360        } finally {
361            super.finalize();
362        }
363    }
364
365
366    /*
367     * Open a DEX file.  The value returned is a magic VM cookie.  On
368     * failure, an IOException is thrown.
369     */
370    private static Object openDexFile(String sourceName, String outputName, int flags,
371            ClassLoader loader, DexPathList.Element[] elements) throws IOException {
372        // Use absolute paths to enable the use of relative paths when testing on host.
373        return openDexFileNative(new File(sourceName).getAbsolutePath(),
374                                 (outputName == null)
375                                     ? null
376                                     : new File(outputName).getAbsolutePath(),
377                                 flags,
378                                 loader,
379                                 elements);
380    }
381
382    /*
383     * Returns true if we managed to close the dex file.
384     */
385    private static native boolean closeDexFile(Object cookie);
386    private static native Class defineClassNative(String name, ClassLoader loader, Object cookie,
387                                                  DexFile dexFile)
388            throws ClassNotFoundException, NoClassDefFoundError;
389    private static native String[] getClassNameList(Object cookie);
390    /*
391     * Open a DEX file.  The value returned is a magic VM cookie.  On
392     * failure, an IOException is thrown.
393     */
394    private static native Object openDexFileNative(String sourceName, String outputName, int flags,
395            ClassLoader loader, DexPathList.Element[] elements);
396
397    /**
398     * Returns true if the VM believes that the apk/jar file is out of date
399     * and should be passed through "dexopt" again.
400     *
401     * @param fileName the absolute path to the apk/jar file to examine.
402     * @return true if dexopt should be called on the file, false otherwise.
403     * @throws java.io.FileNotFoundException if fileName is not readable,
404     *         not a file, or not present.
405     * @throws java.io.IOException if fileName is not a valid apk/jar file or
406     *         if problems occur while parsing it.
407     * @throws java.lang.NullPointerException if fileName is null.
408     */
409    public static native boolean isDexOptNeeded(String fileName)
410            throws FileNotFoundException, IOException;
411
412    /**
413     * See {@link #getDexOptNeeded(String, String, int)}.
414     *
415     * @hide
416     */
417    public static final int NO_DEXOPT_NEEDED = 0;
418
419    /**
420     * See {@link #getDexOptNeeded(String, String, int)}.
421     *
422     * @hide
423     */
424    public static final int DEX2OAT_NEEDED = 1;
425
426    /**
427     * See {@link #getDexOptNeeded(String, String, int)}.
428     *
429     * @hide
430     */
431    public static final int PATCHOAT_NEEDED = 2;
432
433    /**
434     * See {@link #getDexOptNeeded(String, String, int)}.
435     *
436     * @hide
437     */
438    public static final int SELF_PATCHOAT_NEEDED = 3;
439
440    /**
441     * Returns whether the given filter is a valid filter.
442     *
443     * @hide
444     */
445    public native static boolean isValidCompilerFilter(String filter);
446
447    /**
448     * Returns whether the given filter is based on profiles.
449     *
450     * @hide
451     */
452    public native static boolean isProfileGuidedCompilerFilter(String filter);
453
454    /**
455     * Returns the VM's opinion of what kind of dexopt is needed to make the
456     * apk/jar file up to date, where {@code targetMode} is used to indicate what
457     * type of compilation the caller considers up-to-date, and {@code newProfile}
458     * is used to indicate whether profile information has changed recently.
459     *
460     * @param fileName the absolute path to the apk/jar file to examine.
461     * @param compilerFilter a compiler filter to use for what a caller considers up-to-date.
462     * @param newProfile flag that describes whether a profile corresponding
463     *        to the dex file has been recently updated and should be considered
464     *        in the state of the file.
465     * @return NO_DEXOPT_NEEDED if the apk/jar is already up to date.
466     *         DEX2OAT_NEEDED if dex2oat should be called on the apk/jar file.
467     *         PATCHOAT_NEEDED if patchoat should be called on the apk/jar
468     *         file to patch the odex file along side the apk/jar.
469     *         SELF_PATCHOAT_NEEDED if selfpatchoat should be called on the
470     *         apk/jar file to patch the oat file in the dalvik cache.
471     * @throws java.io.FileNotFoundException if fileName is not readable,
472     *         not a file, or not present.
473     * @throws java.io.IOException if fileName is not a valid apk/jar file or
474     *         if problems occur while parsing it.
475     * @throws java.lang.NullPointerException if fileName is null.
476     *
477     * @hide
478     */
479    public static native int getDexOptNeeded(String fileName,
480            String instructionSet, String compilerFilter, boolean newProfile)
481            throws FileNotFoundException, IOException;
482}
483