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