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 java.io.File;
     20 import java.io.FileNotFoundException;
     21 import java.io.IOException;
     22 import java.security.ProtectionDomain;
     23 import java.util.Enumeration;
     24 
     25 
     26 /**
     27  * Manipulates DEX files. The class is similar in principle to
     28  * {@link java.util.zip.ZipFile}. It is used primarily by class loaders.
     29  * <p>
     30  * Note we don't directly open and read the DEX file here. They're memory-mapped
     31  * read-only by the VM.
     32  */
     33 public final class DexFile {
     34     private final int mCookie;
     35     private String mFileName;
     36 
     37     /**
     38      * Opens a DEX file from a given File object. This will usually be a ZIP/JAR
     39      * file with a "classes.dex" inside.
     40      *
     41      * The VM will generate the name of the corresponding file in
     42      * /data/dalvik-cache and open it, possibly creating or updating
     43      * it first if system permissions allow.  Don't pass in the name of
     44      * a file in /data/dalvik-cache, as the named file is expected to be
     45      * in its original (pre-dexopt) state.
     46      *
     47      * @param file
     48      *            the File object referencing the actual DEX file
     49      *
     50      * @throws IOException
     51      *             if an I/O error occurs, such as the file not being found or
     52      *             access rights missing for opening it
     53      */
     54     public DexFile(File file) throws IOException {
     55         this(file.getPath());
     56     }
     57 
     58     /**
     59      * Opens a DEX file from a given filename. This will usually be a ZIP/JAR
     60      * file with a "classes.dex" inside.
     61      *
     62      * The VM will generate the name of the corresponding file in
     63      * /data/dalvik-cache and open it, possibly creating or updating
     64      * it first if system permissions allow.  Don't pass in the name of
     65      * a file in /data/dalvik-cache, as the named file is expected to be
     66      * in its original (pre-dexopt) state.
     67      *
     68      * @param fileName
     69      *            the filename of the DEX file
     70      *
     71      * @throws IOException
     72      *             if an I/O error occurs, such as the file not being found or
     73      *             access rights missing for opening it
     74      */
     75     public DexFile(String fileName) throws IOException {
     76         String wantDex = System.getProperty("android.vm.dexfile", "false");
     77         if (!wantDex.equals("true"))
     78             throw new UnsupportedOperationException("No dex in this VM");
     79 
     80         mCookie = openDexFile(fileName, null, 0);
     81         mFileName = fileName;
     82         //System.out.println("DEX FILE cookie is " + mCookie);
     83     }
     84 
     85     /**
     86      * Opens a DEX file from a given filename, using a specified file
     87      * to hold the optimized data.
     88      *
     89      * @param sourceName
     90      *  Jar or APK file with "classes.dex".
     91      * @param outputName
     92      *  File that will hold the optimized form of the DEX data.
     93      * @param flags
     94      *  Enable optional features.
     95      */
     96     private DexFile(String sourceName, String outputName, int flags)
     97         throws IOException {
     98 
     99         String wantDex = System.getProperty("android.vm.dexfile", "false");
    100         if (!wantDex.equals("true"))
    101             throw new UnsupportedOperationException("No dex in this VM");
    102 
    103         mCookie = openDexFile(sourceName, outputName, flags);
    104         mFileName = sourceName;
    105         //System.out.println("DEX FILE cookie is " + mCookie);
    106     }
    107 
    108     /**
    109      * Open a DEX file, specifying the file in which the optimized DEX
    110      * data should be written.  If the optimized form exists and appears
    111      * to be current, it will be used; if not, the VM will attempt to
    112      * regenerate it.
    113      *
    114      * This is intended for use by applications that wish to download
    115      * and execute DEX files outside the usual application installation
    116      * mechanism.  This function should not be called directly by an
    117      * application; instead, use a class loader such as
    118      * dalvik.system.DexClassLoader.
    119      *
    120      * @param sourcePathName
    121      *  Jar or APK file with "classes.dex".  (May expand this to include
    122      *  "raw DEX" in the future.)
    123      * @param outputPathName
    124      *  File that will hold the optimized form of the DEX data.
    125      * @param flags
    126      *  Enable optional features.  (Currently none defined.)
    127      * @return
    128      *  A new or previously-opened DexFile.
    129      * @throws IOException
    130      *  If unable to open the source or output file.
    131      */
    132     static public DexFile loadDex(String sourcePathName, String outputPathName,
    133         int flags) throws IOException {
    134 
    135         /*
    136          * TODO: we may want to cache previously-opened DexFile objects.
    137          * The cache would be synchronized with close().  This would help
    138          * us avoid mapping the same DEX more than once when an app
    139          * decided to open it multiple times.  In practice this may not
    140          * be a real issue.
    141          */
    142         return new DexFile(sourcePathName, outputPathName, flags);
    143     }
    144 
    145     /**
    146      * Gets the name of the (already opened) DEX file.
    147      *
    148      * @return the file name
    149      */
    150     public String getName() {
    151         return mFileName;
    152     }
    153 
    154     /**
    155      * Closes the DEX file.
    156      * <p>
    157      * This may not be able to release any resources. If classes have been
    158      * loaded, the underlying storage can't be discarded.
    159      *
    160      * @throws IOException
    161      *             if an I/O error occurs during closing the file, which
    162      *             normally should not happen
    163      *
    164      * @cts Second sentence is a bit cryptic.
    165      */
    166     public void close() throws IOException {
    167         closeDexFile(mCookie);
    168     }
    169 
    170     /**
    171      * Loads a class. Returns the class on success, or a {@code null} reference
    172      * on failure.
    173      * <p>
    174      * If you are not calling this from a class loader, this is most likely not
    175      * going to do what you want. Use {@link Class#forName(String)} instead.
    176      * <p>
    177      * The method does not throw {@link ClassNotFoundException} if the class
    178      * isn't found because it isn't feasible to throw exceptions wildly every
    179      * time a class is not found in the first DEX file we look at. It will
    180      * throw exceptions for other failures, though.
    181      *
    182      * @param name
    183      *            the class name, which should look like "java/lang/String"
    184      *
    185      * @param loader
    186      *            the class loader that tries to load the class (in most cases
    187      *            the caller of the method
    188      *
    189      * @return the {@link Class} object representing the class, or {@code null}
    190      *         if the class cannot be loaded
    191      *
    192      * @cts Exception comment is a bit cryptic. What exception will be thrown?
    193      */
    194     public Class loadClass(String name, ClassLoader loader) {
    195         String slashName = name.replace('.', '/');
    196         return loadClassBinaryName(slashName, loader);
    197     }
    198 
    199     /**
    200      * See {@link #loadClass(String, ClassLoader)}.
    201      *
    202      * This takes a "binary" class name to better match ClassLoader semantics.
    203      *
    204      * {@hide}
    205      */
    206     public Class loadClassBinaryName(String name, ClassLoader loader) {
    207         return defineClass(name, loader, mCookie,
    208             null);
    209             //new ProtectionDomain(name) /*DEBUG ONLY*/);
    210     }
    211 
    212     native private static Class defineClass(String name, ClassLoader loader,
    213         int cookie, ProtectionDomain pd);
    214 
    215     /**
    216      * Enumerate the names of the classes in this DEX file.
    217      *
    218      * @return an enumeration of names of classes contained in the DEX file, in
    219      *         the usual internal form (like "java/lang/String").
    220      */
    221     public Enumeration<String> entries() {
    222         return new DFEnum(this);
    223     }
    224 
    225     /*
    226      * Helper class.
    227      */
    228     private class DFEnum implements Enumeration<String> {
    229         private int mIndex;
    230         private String[] mNameList;
    231 
    232         DFEnum(DexFile df) {
    233             mIndex = 0;
    234             mNameList = getClassNameList(mCookie);
    235         }
    236 
    237         public boolean hasMoreElements() {
    238             return (mIndex < mNameList.length);
    239         }
    240 
    241         public String nextElement() {
    242             return mNameList[mIndex++];
    243         }
    244     }
    245 
    246     /* return a String array with class names */
    247     native private static String[] getClassNameList(int cookie);
    248 
    249     /**
    250      * Called when the class is finalized. Makes sure the DEX file is closed.
    251      *
    252      * @throws IOException
    253      *             if an I/O error occurs during closing the file, which
    254      *             normally should not happen
    255      */
    256     @Override protected void finalize() throws Throwable {
    257         try {
    258             close();
    259         } finally {
    260             super.finalize();
    261         }
    262     }
    263 
    264     /*
    265      * Open a DEX file.  The value returned is a magic VM cookie.  On
    266      * failure, an IOException is thrown.
    267      */
    268     native private static int openDexFile(String sourceName, String outputName,
    269         int flags) throws IOException;
    270 
    271     /*
    272      * Close DEX file.
    273      */
    274     native private static void closeDexFile(int cookie);
    275 
    276     /**
    277      * Returns true if the VM believes that the apk/jar file is out of date
    278      * and should be passed through "dexopt" again.
    279      *
    280      * @param fileName the absolute path to the apk/jar file to examine.
    281      * @return true if dexopt should be called on the file, false otherwise.
    282      * @throws java.io.FileNotFoundException if fileName is not readable,
    283      *         not a file, or not present.
    284      * @throws java.io.IOException if fileName is not a valid apk/jar file or
    285      *         if problems occur while parsing it.
    286      * @throws java.lang.NullPointerException if fileName is null.
    287      * @throws dalvik.system.StaleDexCacheError if the optimized dex file
    288      *         is stale but exists on a read-only partition.
    289      */
    290     native public static boolean isDexOptNeeded(String fileName)
    291             throws FileNotFoundException, IOException;
    292 }
    293