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