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