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     private long mCookie;
     38     private final String mFileName;
     39     private final CloseGuard guard = CloseGuard.get();
     40 
     41     /**
     42      * Opens a DEX file from a given File object. This will usually be a ZIP/JAR
     43      * file with a "classes.dex" inside.
     44      *
     45      * The VM will generate the name of the corresponding file in
     46      * /data/dalvik-cache and open it, possibly creating or updating
     47      * it first if system permissions allow.  Don't pass in the name of
     48      * a file in /data/dalvik-cache, as the named file is expected to be
     49      * in its original (pre-dexopt) state.
     50      *
     51      * @param file
     52      *            the File object referencing the actual DEX file
     53      *
     54      * @throws IOException
     55      *             if an I/O error occurs, such as the file not being found or
     56      *             access rights missing for opening it
     57      */
     58     public DexFile(File file) throws IOException {
     59         this(file.getPath());
     60     }
     61 
     62     /**
     63      * Opens a DEX file from a given filename. This will usually be a ZIP/JAR
     64      * file with a "classes.dex" inside.
     65      *
     66      * The VM will generate the name of the corresponding file in
     67      * /data/dalvik-cache and open it, possibly creating or updating
     68      * it first if system permissions allow.  Don't pass in the name of
     69      * a file in /data/dalvik-cache, as the named file is expected to be
     70      * in its original (pre-dexopt) state.
     71      *
     72      * @param fileName
     73      *            the filename of the DEX file
     74      *
     75      * @throws IOException
     76      *             if an I/O error occurs, such as the file not being found or
     77      *             access rights missing for opening it
     78      */
     79     public DexFile(String fileName) throws IOException {
     80         mCookie = openDexFile(fileName, null, 0);
     81         mFileName = fileName;
     82         guard.open("close");
     83         //System.out.println("DEX FILE cookie is " + mCookie + " fileName=" + fileName);
     84     }
     85 
     86     /**
     87      * Opens a DEX file from a given filename, using a specified file
     88      * to hold the optimized data.
     89      *
     90      * @param sourceName
     91      *  Jar or APK file with "classes.dex".
     92      * @param outputName
     93      *  File that will hold the optimized form of the DEX data.
     94      * @param flags
     95      *  Enable optional features.
     96      */
     97     private DexFile(String sourceName, String outputName, int flags) throws IOException {
     98         if (outputName != null) {
     99             try {
    100                 String parent = new File(outputName).getParent();
    101                 if (Libcore.os.getuid() != Libcore.os.stat(parent).st_uid) {
    102                     throw new IllegalArgumentException("Optimized data directory " + parent
    103                             + " is not owned by the current user. Shared storage cannot protect"
    104                             + " your application from code injection attacks.");
    105                 }
    106             } catch (ErrnoException ignored) {
    107                 // assume we'll fail with a more contextual error later
    108             }
    109         }
    110 
    111         mCookie = openDexFile(sourceName, outputName, flags);
    112         mFileName = sourceName;
    113         guard.open("close");
    114         //System.out.println("DEX FILE cookie is " + mCookie + " sourceName=" + sourceName + " outputName=" + outputName);
    115     }
    116 
    117     /**
    118      * Open a DEX file, specifying the file in which the optimized DEX
    119      * data should be written.  If the optimized form exists and appears
    120      * to be current, it will be used; if not, the VM will attempt to
    121      * regenerate it.
    122      *
    123      * This is intended for use by applications that wish to download
    124      * and execute DEX files outside the usual application installation
    125      * mechanism.  This function should not be called directly by an
    126      * application; instead, use a class loader such as
    127      * dalvik.system.DexClassLoader.
    128      *
    129      * @param sourcePathName
    130      *  Jar or APK file with "classes.dex".  (May expand this to include
    131      *  "raw DEX" in the future.)
    132      * @param outputPathName
    133      *  File that will hold the optimized form of the DEX data.
    134      * @param flags
    135      *  Enable optional features.  (Currently none defined.)
    136      * @return
    137      *  A new or previously-opened DexFile.
    138      * @throws IOException
    139      *  If unable to open the source or output file.
    140      */
    141     static public DexFile loadDex(String sourcePathName, String outputPathName,
    142         int flags) throws IOException {
    143 
    144         /*
    145          * TODO: we may want to cache previously-opened DexFile objects.
    146          * The cache would be synchronized with close().  This would help
    147          * us avoid mapping the same DEX more than once when an app
    148          * decided to open it multiple times.  In practice this may not
    149          * be a real issue.
    150          */
    151         return new DexFile(sourcePathName, outputPathName, flags);
    152     }
    153 
    154     /**
    155      * Gets the name of the (already opened) DEX file.
    156      *
    157      * @return the file name
    158      */
    159     public String getName() {
    160         return mFileName;
    161     }
    162 
    163     @Override public String toString() {
    164         return getName();
    165     }
    166 
    167     /**
    168      * Closes the DEX file.
    169      * <p>
    170      * This may not be able to release any resources. If classes from this
    171      * DEX file are still resident, the DEX file can't be unmapped.
    172      *
    173      * @throws IOException
    174      *             if an I/O error occurs during closing the file, which
    175      *             normally should not happen
    176      */
    177     public void close() throws IOException {
    178         if (mCookie != 0) {
    179             guard.close();
    180             closeDexFile(mCookie);
    181             mCookie = 0;
    182         }
    183     }
    184 
    185     /**
    186      * Loads a class. Returns the class on success, or a {@code null} reference
    187      * on failure.
    188      * <p>
    189      * If you are not calling this from a class loader, this is most likely not
    190      * going to do what you want. Use {@link Class#forName(String)} instead.
    191      * <p>
    192      * The method does not throw {@link ClassNotFoundException} if the class
    193      * isn't found because it isn't reasonable to throw exceptions wildly every
    194      * time a class is not found in the first DEX file we look at.
    195      *
    196      * @param name
    197      *            the class name, which should look like "java/lang/String"
    198      *
    199      * @param loader
    200      *            the class loader that tries to load the class (in most cases
    201      *            the caller of the method
    202      *
    203      * @return the {@link Class} object representing the class, or {@code null}
    204      *         if the class cannot be loaded
    205      */
    206     public Class loadClass(String name, ClassLoader loader) {
    207         String slashName = name.replace('.', '/');
    208         return loadClassBinaryName(slashName, loader, null);
    209     }
    210 
    211     /**
    212      * See {@link #loadClass(String, ClassLoader)}.
    213      *
    214      * This takes a "binary" class name to better match ClassLoader semantics.
    215      *
    216      * @hide
    217      */
    218     public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) {
    219         return defineClass(name, loader, mCookie, suppressed);
    220     }
    221 
    222     private static Class defineClass(String name, ClassLoader loader, long cookie,
    223                                      List<Throwable> suppressed) {
    224         Class result = null;
    225         try {
    226             result = defineClassNative(name, loader, cookie);
    227         } catch (NoClassDefFoundError e) {
    228             if (suppressed != null) {
    229                 suppressed.add(e);
    230             }
    231         } catch (ClassNotFoundException e) {
    232             if (suppressed != null) {
    233                 suppressed.add(e);
    234             }
    235         }
    236         return result;
    237     }
    238 
    239     /**
    240      * Enumerate the names of the classes in this DEX file.
    241      *
    242      * @return an enumeration of names of classes contained in the DEX file, in
    243      *         the usual internal form (like "java/lang/String").
    244      */
    245     public Enumeration<String> entries() {
    246         return new DFEnum(this);
    247     }
    248 
    249     /*
    250      * Helper class.
    251      */
    252     private class DFEnum implements Enumeration<String> {
    253         private int mIndex;
    254         private String[] mNameList;
    255 
    256         DFEnum(DexFile df) {
    257             mIndex = 0;
    258             mNameList = getClassNameList(mCookie);
    259         }
    260 
    261         public boolean hasMoreElements() {
    262             return (mIndex < mNameList.length);
    263         }
    264 
    265         public String nextElement() {
    266             return mNameList[mIndex++];
    267         }
    268     }
    269 
    270     /**
    271      * Called when the class is finalized. Makes sure the DEX file is closed.
    272      *
    273      * @throws IOException
    274      *             if an I/O error occurs during closing the file, which
    275      *             normally should not happen
    276      */
    277     @Override protected void finalize() throws Throwable {
    278         try {
    279             if (guard != null) {
    280                 guard.warnIfOpen();
    281             }
    282             close();
    283         } finally {
    284             super.finalize();
    285         }
    286     }
    287 
    288 
    289     /*
    290      * Open a DEX file.  The value returned is a magic VM cookie.  On
    291      * failure, an IOException is thrown.
    292      */
    293     private static long openDexFile(String sourceName, String outputName, int flags) throws IOException {
    294         // Use absolute paths to enable the use of relative paths when testing on host.
    295         return openDexFileNative(new File(sourceName).getAbsolutePath(),
    296                                  (outputName == null) ? null : new File(outputName).getAbsolutePath(),
    297                                  flags);
    298     }
    299 
    300     private static native void closeDexFile(long cookie);
    301     private static native Class defineClassNative(String name, ClassLoader loader, long cookie)
    302             throws ClassNotFoundException, NoClassDefFoundError;
    303     private static native String[] getClassNameList(long cookie);
    304     /*
    305      * Open a DEX file.  The value returned is a magic VM cookie.  On
    306      * failure, an IOException is thrown.
    307      */
    308     private static native long openDexFileNative(String sourceName, String outputName, int flags);
    309 
    310     /**
    311      * Returns true if the VM believes that the apk/jar file is out of date
    312      * and should be passed through "dexopt" again.
    313      *
    314      * @param fileName the absolute path to the apk/jar file to examine.
    315      * @return true if dexopt should be called on the file, false otherwise.
    316      * @throws java.io.FileNotFoundException if fileName is not readable,
    317      *         not a file, or not present.
    318      * @throws java.io.IOException if fileName is not a valid apk/jar file or
    319      *         if problems occur while parsing it.
    320      * @throws java.lang.NullPointerException if fileName is null.
    321      * @throws dalvik.system.StaleDexCacheError if the optimized dex file
    322      *         is stale but exists on a read-only partition.
    323      */
    324     public static native boolean isDexOptNeeded(String fileName)
    325             throws FileNotFoundException, IOException;
    326 
    327     /**
    328      * See {@link #isDexOptNeededInternal(String, String, String, boolean)}.
    329      *
    330      * @hide
    331      */
    332     public static final byte UP_TO_DATE = 0;
    333 
    334     /**
    335      * See {@link #isDexOptNeededInternal(String, String, String, boolean)}.
    336      *
    337      * @hide
    338      */
    339     public static final byte PATCHOAT_NEEDED = 1;
    340 
    341     /**
    342      * See {@link #isDexOptNeededInternal(String, String, String, boolean)}.
    343      *
    344      * @hide
    345      */
    346     public static final byte DEXOPT_NEEDED = 2;
    347 
    348     /**
    349      * Returns UP_TO_DATE if the VM believes that the apk/jar file
    350      * is up to date, PATCHOAT_NEEDED if it believes that the file is up
    351      * to date but it must be relocated to match the base address offset,
    352      * and DEXOPT_NEEDED if it believes that it is out of date and should
    353      * be passed through "dexopt" again.
    354      *
    355      * @param fileName the absolute path to the apk/jar file to examine.
    356      * @return DEXOPT_NEEDED if dexopt should be called on the file,
    357      *         PATCHOAT_NEEDED if we need to run "patchoat" on it and
    358      *         UP_TO_DATE otherwise.
    359      * @throws java.io.FileNotFoundException if fileName is not readable,
    360      *         not a file, or not present.
    361      * @throws java.io.IOException if fileName is not a valid apk/jar file or
    362      *         if problems occur while parsing it.
    363      * @throws java.lang.NullPointerException if fileName is null.
    364      * @throws dalvik.system.StaleDexCacheError if the optimized dex file
    365      *         is stale but exists on a read-only partition.
    366      *
    367      * @hide
    368      */
    369     public static native byte isDexOptNeededInternal(String fileName, String pkgname,
    370             String instructionSet, boolean defer)
    371             throws FileNotFoundException, IOException;
    372 }
    373