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