Home | History | Annotate | Download | only in system
      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 
     17 package dalvik.system;
     18 
     19 import android.system.ErrnoException;
     20 import dalvik.annotation.optimization.ReachabilitySensitive;
     21 import java.io.File;
     22 import java.io.FileNotFoundException;
     23 import java.io.IOException;
     24 import java.nio.ByteBuffer;
     25 import java.util.Arrays;
     26 import java.util.Enumeration;
     27 import java.util.List;
     28 import libcore.io.Libcore;
     29 
     30 /**
     31  * Loads DEX files. This class is meant for internal use and should not be used
     32  * by applications.
     33  *
     34  * @deprecated This class should not be used directly by applications. It will hurt
     35  *     performance in most cases and will lead to incorrect execution of bytecode in
     36  *     the worst case. Applications should use one of the standard classloaders such
     37  *     as {@link dalvik.system.PathClassLoader} instead. <b>This API will be removed
     38  *     in a future Android release</b>.
     39  */
     40 @Deprecated
     41 public final class DexFile {
     42   /**
     43    * If close is called, mCookie becomes null but the internal cookie is preserved if the close
     44    * failed so that we can free resources in the finalizer.
     45    */
     46     @ReachabilitySensitive
     47     private Object mCookie;
     48 
     49     private Object mInternalCookie;
     50     private final String mFileName;
     51 
     52     /**
     53      * Opens a DEX file from a given File object.
     54      *
     55      * @deprecated Applications should use one of the standard classloaders such
     56      *     as {@link dalvik.system.PathClassLoader} instead. <b>This API will be removed
     57      *     in a future Android release</b>.
     58      */
     59     @Deprecated
     60     public DexFile(File file) throws IOException {
     61         this(file.getPath());
     62     }
     63     /*
     64      * Private version with class loader argument.
     65      *
     66      * @param file
     67      *            the File object referencing the actual DEX file
     68      * @param loader
     69      *            the class loader object creating the DEX file object
     70      * @param elements
     71      *            the temporary dex path list elements from DexPathList.makeElements
     72      */
     73     DexFile(File file, ClassLoader loader, DexPathList.Element[] elements)
     74             throws IOException {
     75         this(file.getPath(), loader, elements);
     76     }
     77 
     78     /**
     79      * Opens a DEX file from a given filename.
     80      *
     81      * @deprecated Applications should use one of the standard classloaders such
     82      *     as {@link dalvik.system.PathClassLoader} instead. <b>This API will be removed
     83      *     in a future Android release</b>.
     84      */
     85     @Deprecated
     86     public DexFile(String fileName) throws IOException {
     87         this(fileName, null, null);
     88     }
     89 
     90     /*
     91      * Private version with class loader argument.
     92      *
     93      * @param fileName
     94      *            the filename of the DEX file
     95      * @param loader
     96      *            the class loader creating the DEX file object
     97      * @param elements
     98      *            the temporary dex path list elements from DexPathList.makeElements
     99      */
    100     DexFile(String fileName, ClassLoader loader, DexPathList.Element[] elements) throws IOException {
    101         mCookie = openDexFile(fileName, null, 0, loader, elements);
    102         mInternalCookie = mCookie;
    103         mFileName = fileName;
    104         //System.out.println("DEX FILE cookie is " + mCookie + " fileName=" + fileName);
    105     }
    106 
    107     DexFile(ByteBuffer buf) throws IOException {
    108         mCookie = openInMemoryDexFile(buf);
    109         mInternalCookie = mCookie;
    110         mFileName = null;
    111     }
    112 
    113     /**
    114      * Opens a DEX file from a given filename, using a specified file
    115      * to hold the optimized data.
    116      *
    117      * @param sourceName
    118      *  Jar or APK file with "classes.dex".
    119      * @param outputName
    120      *  File that will hold the optimized form of the DEX data.
    121      * @param flags
    122      *  Enable optional features.
    123      * @param loader
    124      *  The class loader creating the DEX file object.
    125      * @param elements
    126      *  The temporary dex path list elements from DexPathList.makeElements
    127      */
    128     private DexFile(String sourceName, String outputName, int flags, ClassLoader loader,
    129             DexPathList.Element[] elements) throws IOException {
    130         if (outputName != null) {
    131             try {
    132                 String parent = new File(outputName).getParent();
    133                 if (Libcore.os.getuid() != Libcore.os.stat(parent).st_uid) {
    134                     throw new IllegalArgumentException("Optimized data directory " + parent
    135                             + " is not owned by the current user. Shared storage cannot protect"
    136                             + " your application from code injection attacks.");
    137                 }
    138             } catch (ErrnoException ignored) {
    139                 // assume we'll fail with a more contextual error later
    140             }
    141         }
    142 
    143         mCookie = openDexFile(sourceName, outputName, flags, loader, elements);
    144         mInternalCookie = mCookie;
    145         mFileName = sourceName;
    146         //System.out.println("DEX FILE cookie is " + mCookie + " sourceName=" + sourceName + " outputName=" + outputName);
    147     }
    148 
    149     /**
    150      * Open a DEX file, specifying the file in which the optimized DEX
    151      * data should be written.  If the optimized form exists and appears
    152      * to be current, it will be used; if not, the VM will attempt to
    153      * regenerate it.
    154      *
    155      * @deprecated Applications should use one of the standard classloaders such
    156      *     as {@link dalvik.system.PathClassLoader} instead. <b>This API will be removed
    157      *     in a future Android release</b>.
    158      */
    159     @Deprecated
    160     static public DexFile loadDex(String sourcePathName, String outputPathName,
    161         int flags) throws IOException {
    162 
    163         /*
    164          * TODO: we may want to cache previously-opened DexFile objects.
    165          * The cache would be synchronized with close().  This would help
    166          * us avoid mapping the same DEX more than once when an app
    167          * decided to open it multiple times.  In practice this may not
    168          * be a real issue.
    169          */
    170         return loadDex(sourcePathName, outputPathName, flags, null, null);
    171     }
    172 
    173     /*
    174      * Private version of loadDex that also takes a class loader.
    175      *
    176      * @param sourcePathName
    177      *  Jar or APK file with "classes.dex".  (May expand this to include
    178      *  "raw DEX" in the future.)
    179      * @param outputPathName
    180      *  File that will hold the optimized form of the DEX data.
    181      * @param flags
    182      *  Enable optional features.  (Currently none defined.)
    183      * @param loader
    184      *  Class loader that is aloading the DEX file.
    185      * @param elements
    186      *  The temporary dex path list elements from DexPathList.makeElements
    187      * @return
    188      *  A new or previously-opened DexFile.
    189      * @throws IOException
    190      *  If unable to open the source or output file.
    191      */
    192     static DexFile loadDex(String sourcePathName, String outputPathName,
    193         int flags, ClassLoader loader, DexPathList.Element[] elements) throws IOException {
    194 
    195         /*
    196          * TODO: we may want to cache previously-opened DexFile objects.
    197          * The cache would be synchronized with close().  This would help
    198          * us avoid mapping the same DEX more than once when an app
    199          * decided to open it multiple times.  In practice this may not
    200          * be a real issue.
    201          */
    202         return new DexFile(sourcePathName, outputPathName, flags, loader, elements);
    203     }
    204 
    205     /**
    206      * Gets the name of the (already opened) DEX file.
    207      *
    208      * @return the file name
    209      */
    210     public String getName() {
    211         return mFileName;
    212     }
    213 
    214     @Override public String toString() {
    215         if (mFileName != null) {
    216             return getName();
    217         } else {
    218             return "InMemoryDexFile[cookie=" + Arrays.toString((long[]) mCookie) + "]";
    219         }
    220     }
    221 
    222     /**
    223      * Closes the DEX file.
    224      * <p>
    225      * This may not be able to release all of the resources. If classes from this DEX file are
    226      * still resident, the DEX file can't be unmapped. In the case where we do not release all
    227      * the resources, close is called again in the finalizer.
    228      *
    229      * @throws IOException
    230      *             if an I/O error occurs during closing the file, which
    231      *             normally should not happen
    232      */
    233     public void close() throws IOException {
    234         if (mInternalCookie != null) {
    235             if (closeDexFile(mInternalCookie)) {
    236                 mInternalCookie = null;
    237             }
    238             mCookie = null;
    239         }
    240     }
    241 
    242     /**
    243      * Loads a class. Returns the class on success, or a {@code null} reference
    244      * on failure.
    245      * <p>
    246      * If you are not calling this from a class loader, this is most likely not
    247      * going to do what you want. Use {@link Class#forName(String)} instead.
    248      * <p>
    249      * The method does not throw {@link ClassNotFoundException} if the class
    250      * isn't found because it isn't reasonable to throw exceptions wildly every
    251      * time a class is not found in the first DEX file we look at.
    252      *
    253      * @param name
    254      *            the class name, which should look like "java/lang/String"
    255      *
    256      * @param loader
    257      *            the class loader that tries to load the class (in most cases
    258      *            the caller of the method
    259      *
    260      * @return the {@link Class} object representing the class, or {@code null}
    261      *         if the class cannot be loaded
    262      */
    263     public Class loadClass(String name, ClassLoader loader) {
    264         String slashName = name.replace('.', '/');
    265         return loadClassBinaryName(slashName, loader, null);
    266     }
    267 
    268     /**
    269      * See {@link #loadClass(String, ClassLoader)}.
    270      *
    271      * This takes a "binary" class name to better match ClassLoader semantics.
    272      *
    273      * @hide
    274      */
    275     public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) {
    276         return defineClass(name, loader, mCookie, this, suppressed);
    277     }
    278 
    279     private static Class defineClass(String name, ClassLoader loader, Object cookie,
    280                                      DexFile dexFile, List<Throwable> suppressed) {
    281         Class result = null;
    282         try {
    283             result = defineClassNative(name, loader, cookie, dexFile);
    284         } catch (NoClassDefFoundError e) {
    285             if (suppressed != null) {
    286                 suppressed.add(e);
    287             }
    288         } catch (ClassNotFoundException e) {
    289             if (suppressed != null) {
    290                 suppressed.add(e);
    291             }
    292         }
    293         return result;
    294     }
    295 
    296     /**
    297      * Enumerate the names of the classes in this DEX file.
    298      *
    299      * @return an enumeration of names of classes contained in the DEX file, in
    300      *         the usual internal form (like "java/lang/String").
    301      */
    302     public Enumeration<String> entries() {
    303         return new DFEnum(this);
    304     }
    305 
    306     /*
    307      * Helper class.
    308      */
    309     private static class DFEnum implements Enumeration<String> {
    310         private int mIndex;
    311         private String[] mNameList;
    312 
    313         DFEnum(DexFile df) {
    314             mIndex = 0;
    315             mNameList = getClassNameList(df.mCookie);
    316         }
    317 
    318         public boolean hasMoreElements() {
    319             return (mIndex < mNameList.length);
    320         }
    321 
    322         public String nextElement() {
    323             return mNameList[mIndex++];
    324         }
    325     }
    326 
    327     /**
    328      * Called when the class is finalized. Makes sure the DEX file is closed.
    329      *
    330      * @throws IOException
    331      *             if an I/O error occurs during closing the file, which
    332      *             normally should not happen
    333      */
    334     @Override protected void finalize() throws Throwable {
    335         try {
    336             if (mInternalCookie != null && !closeDexFile(mInternalCookie)) {
    337                 throw new AssertionError("Failed to close dex file in finalizer.");
    338             }
    339             mInternalCookie = null;
    340             mCookie = null;
    341         } finally {
    342             super.finalize();
    343         }
    344     }
    345 
    346 
    347     /*
    348      * Open a DEX file.  The value returned is a magic VM cookie.  On
    349      * failure, an IOException is thrown.
    350      */
    351     private static Object openDexFile(String sourceName, String outputName, int flags,
    352             ClassLoader loader, DexPathList.Element[] elements) throws IOException {
    353         // Use absolute paths to enable the use of relative paths when testing on host.
    354         return openDexFileNative(new File(sourceName).getAbsolutePath(),
    355                                  (outputName == null)
    356                                      ? null
    357                                      : new File(outputName).getAbsolutePath(),
    358                                  flags,
    359                                  loader,
    360                                  elements);
    361     }
    362 
    363     private static Object openInMemoryDexFile(ByteBuffer buf) throws IOException {
    364         if (buf.isDirect()) {
    365             return createCookieWithDirectBuffer(buf, buf.position(), buf.limit());
    366         } else {
    367             return createCookieWithArray(buf.array(), buf.position(), buf.limit());
    368         }
    369     }
    370 
    371     private static native Object createCookieWithDirectBuffer(ByteBuffer buf, int start, int end);
    372     private static native Object createCookieWithArray(byte[] buf, int start, int end);
    373 
    374     /*
    375      * Returns true if the dex file is backed by a valid oat file.
    376      */
    377     /*package*/ boolean isBackedByOatFile() {
    378         return isBackedByOatFile(mCookie);
    379     }
    380 
    381     /*
    382      * Set the dex file as trusted: it can access hidden APIs of the platform.
    383      */
    384     /*package*/ void setTrusted() {
    385         setTrusted(mCookie);
    386     }
    387 
    388     /*
    389      * Returns true if we managed to close the dex file.
    390      */
    391     private static native boolean closeDexFile(Object cookie);
    392     private static native Class defineClassNative(String name, ClassLoader loader, Object cookie,
    393                                                   DexFile dexFile)
    394             throws ClassNotFoundException, NoClassDefFoundError;
    395     private static native String[] getClassNameList(Object cookie);
    396     private static native boolean isBackedByOatFile(Object cookie);
    397     private static native void setTrusted(Object cookie);
    398     /*
    399      * Open a DEX file.  The value returned is a magic VM cookie.  On
    400      * failure, an IOException is thrown.
    401      */
    402     private static native Object openDexFileNative(String sourceName, String outputName, int flags,
    403             ClassLoader loader, DexPathList.Element[] elements);
    404 
    405     /**
    406      * Returns true if the VM believes that the apk/jar file is out of date
    407      * and should be passed through "dexopt" again.
    408      *
    409      * @param fileName the absolute path to the apk/jar file to examine.
    410      * @return true if dexopt should be called on the file, false otherwise.
    411      * @throws java.io.FileNotFoundException if fileName is not readable,
    412      *         not a file, or not present.
    413      * @throws java.io.IOException if fileName is not a valid apk/jar file or
    414      *         if problems occur while parsing it.
    415      * @throws java.lang.NullPointerException if fileName is null.
    416      */
    417     public static native boolean isDexOptNeeded(String fileName)
    418             throws FileNotFoundException, IOException;
    419 
    420     /**
    421      * No dexopt should (or can) be done to update the apk/jar.
    422      *
    423      * See {@link #getDexOptNeeded(String, String, String, boolean, boolean)}.
    424      *
    425      * @hide
    426      */
    427     public static final int NO_DEXOPT_NEEDED = 0;
    428 
    429     /**
    430      * dex2oat should be run to update the apk/jar from scratch.
    431      *
    432      * See {@link #getDexOptNeeded(String, String, String, boolean, boolean)}.
    433      *
    434      * @hide
    435      */
    436     public static final int DEX2OAT_FROM_SCRATCH = 1;
    437 
    438     /**
    439      * dex2oat should be run to update the apk/jar because the existing code
    440      * is out of date with respect to the boot image.
    441      *
    442      * See {@link #getDexOptNeeded(String, String, String, boolean, boolean)}.
    443      *
    444      * @hide
    445      */
    446     public static final int DEX2OAT_FOR_BOOT_IMAGE = 2;
    447 
    448     /**
    449      * dex2oat should be run to update the apk/jar because the existing code
    450      * is out of date with respect to the target compiler filter.
    451      *
    452      * See {@link #getDexOptNeeded(String, String, String, boolean, boolean)}.
    453      *
    454      * @hide
    455      */
    456     public static final int DEX2OAT_FOR_FILTER = 3;
    457 
    458     /**
    459      * dex2oat should be run to update the apk/jar because the existing code
    460      * is not relocated to match the boot image.
    461      *
    462      * See {@link #getDexOptNeeded(String, String, String, boolean, boolean)}.
    463      *
    464      * @hide
    465      */
    466     public static final int DEX2OAT_FOR_RELOCATION = 4;
    467 
    468 
    469     /**
    470      * Calls {@link #getDexOptNeeded(String, String, String, String, String, boolean, boolean)}
    471      * with a null class loader context.
    472      *
    473      * TODO(ngeoffray, calin): deprecate / remove.
    474      * @hide
    475      */
    476     public static int getDexOptNeeded(String fileName,
    477         String instructionSet, String compilerFilter, boolean newProfile, boolean downgrade)
    478         throws FileNotFoundException, IOException {
    479             return getDexOptNeeded(
    480                 fileName, instructionSet, compilerFilter, null, newProfile, downgrade);
    481     }
    482 
    483     /**
    484      * Returns the VM's opinion of what kind of dexopt is needed to make the
    485      * apk/jar file up to date, where {@code targetMode} is used to indicate what
    486      * type of compilation the caller considers up-to-date, and {@code newProfile}
    487      * is used to indicate whether profile information has changed recently.
    488      *
    489      * @param fileName the absolute path to the apk/jar file to examine.
    490      * @param compilerFilter a compiler filter to use for what a caller considers up-to-date.
    491      * @param classLoaderContext a string encoding the class loader context the dex file
    492      *        is intended to have at runtime.
    493      * @param newProfile flag that describes whether a profile corresponding
    494      *        to the dex file has been recently updated and should be considered
    495      *        in the state of the file.
    496      * @param downgrade flag that describes if the purpose of dexopt is to downgrade the
    497      *        compiler filter. If set to false, will be evaluated as an upgrade request.
    498      * @return NO_DEXOPT_NEEDED, or DEX2OAT_*. See documentation
    499      *         of the particular status code for more information on its
    500      *         meaning. Returns a positive status code if the status refers to
    501      *         the oat file in the oat location. Returns a negative status
    502      *         code if the status refers to the oat file in the odex location.
    503      * @throws java.io.FileNotFoundException if fileName is not readable,
    504      *         not a file, or not present.
    505      * @throws java.io.IOException if fileName is not a valid apk/jar file or
    506      *         if problems occur while parsing it.
    507      * @throws java.lang.NullPointerException if fileName is null.
    508      *
    509      * @hide
    510      */
    511     public static native int getDexOptNeeded(String fileName,
    512             String instructionSet, String compilerFilter, String classLoaderContext,
    513             boolean newProfile, boolean downgrade)
    514             throws FileNotFoundException, IOException;
    515 
    516     /**
    517      * Returns the status of the dex file {@code fileName}. The returned string is
    518      * an opaque, human readable representation of the current status. The output
    519      * is only meant for debugging and is not guaranteed to be stable across
    520      * releases and/or devices.
    521      *
    522      * @hide
    523      */
    524     public static native String getDexFileStatus(String fileName, String instructionSet)
    525         throws FileNotFoundException;
    526 
    527     /**
    528      * Encapsulates information about the optimizations performed on a dex file.
    529      *
    530      * Note that the info is only meant for debugging and is not guaranteed to be
    531      * stable across releases and/or devices.
    532      *
    533      * @hide
    534      */
    535     public static final class OptimizationInfo {
    536         // The optimization status.
    537         private final String status;
    538         // The optimization reason. The reason might be "unknown" if the
    539         // the compiler artifacts were not annotated during optimizations.
    540         private final String reason;
    541 
    542         private OptimizationInfo(String status, String reason) {
    543             this.status = status;
    544             this.reason = reason;
    545         }
    546 
    547         public String getStatus() {
    548             return status;
    549         }
    550 
    551         public String getReason() {
    552             return reason;
    553         }
    554     }
    555 
    556     /**
    557      * Retrieves the optimization info for a dex file.
    558      *
    559      * @hide
    560      */
    561     public static OptimizationInfo getDexFileOptimizationInfo(
    562             String fileName, String instructionSet) throws FileNotFoundException {
    563         String[] status = getDexFileOptimizationStatus(fileName, instructionSet);
    564         return new OptimizationInfo(status[0], status[1]);
    565     }
    566 
    567     /**
    568      * Returns the optimization status of the dex file {@code fileName}. The returned
    569      * array will have 2 elements which specify:
    570      *   - index 0: the level of optimizations
    571      *   - index 1: the optimization reason. The reason might be "unknown" if the
    572      *              the compiler artifacts were not annotated during optimizations.
    573      *
    574      * The output is only meant for debugging and is not guaranteed to be stable across
    575      * releases and/or devices.
    576      *
    577      * @hide
    578      */
    579     private static native String[] getDexFileOptimizationStatus(
    580             String fileName, String instructionSet) throws FileNotFoundException;
    581 
    582     /**
    583      * Returns the paths of the optimized files generated for {@code fileName}.
    584      * If no optimized code exists the method returns null.
    585      * @hide
    586      */
    587     public static native String[] getDexFileOutputPaths(String fileName, String instructionSet)
    588         throws FileNotFoundException;
    589 
    590     /**
    591      * Returns whether the given filter is a valid filter.
    592      *
    593      * @hide
    594      */
    595     public native static boolean isValidCompilerFilter(String filter);
    596 
    597     /**
    598      * Returns whether the given filter is based on profiles.
    599      *
    600      * @hide
    601      */
    602     public native static boolean isProfileGuidedCompilerFilter(String filter);
    603 
    604     /**
    605      * Returns the version of the compiler filter that is not based on profiles.
    606      * If the input is not a valid filter, or the filter is already not based on
    607      * profiles, this returns the input.
    608      *
    609      * @hide
    610      */
    611     public native static String getNonProfileGuidedCompilerFilter(String filter);
    612 
    613     /**
    614      * Returns the version of the compiler filter that is suitable for safe mode.
    615      * If the input is not a valid filter, or the filter is already suitable for
    616      * safe mode, this returns the input.
    617      *
    618      * @hide
    619      */
    620     public native static String getSafeModeCompilerFilter(String filter);
    621 
    622     /**
    623      * Returns the static file size of the original dex file.
    624      * The original size of the uncompressed dex file is returned.
    625      * On device the dex file may be compressed or embedded in some other
    626      * file (e.g. oat) in a platform implementation dependent manner. This
    627      * method abstracts away from those details and provides an efficient
    628      * implementation given that the dex file in question has already been
    629      * uncompressed, extracted, and/or loaded by the runtime as appropriate.
    630      * <p>
    631      * In the case of multidex, returns the sum of the original uncompressed
    632      * multidex entry file sizes.
    633      *
    634      * @hide
    635      */
    636     public long getStaticSizeOfDexFile() {
    637       return getStaticSizeOfDexFile(mCookie);
    638     }
    639 
    640     private static native long getStaticSizeOfDexFile(Object cookie);
    641 }
    642