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