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