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