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