1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 /* 18 * Copyright (C) 2008 The Android Open Source Project 19 * 20 * Licensed under the Apache License, Version 2.0 (the "License"); 21 * you may not use this file except in compliance with the License. 22 * You may obtain a copy of the License at 23 * 24 * http://www.apache.org/licenses/LICENSE-2.0 25 * 26 * Unless required by applicable law or agreed to in writing, software 27 * distributed under the License is distributed on an "AS IS" BASIS, 28 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 29 * See the License for the specific language governing permissions and 30 * limitations under the License. 31 */ 32 33 package java.lang; 34 35 import dalvik.system.BaseDexClassLoader; 36 import dalvik.system.VMDebug; 37 import dalvik.system.VMStack; 38 import java.io.File; 39 import java.io.IOException; 40 import java.io.InputStream; 41 import java.io.InputStreamReader; 42 import java.io.OutputStream; 43 import java.io.OutputStreamWriter; 44 import java.lang.ref.FinalizerReference; 45 import java.util.ArrayList; 46 import java.util.List; 47 import java.util.StringTokenizer; 48 import libcore.io.IoUtils; 49 import libcore.io.Libcore; 50 import libcore.util.EmptyArray; 51 import static android.system.OsConstants._SC_NPROCESSORS_CONF; 52 53 /** 54 * Allows Java applications to interface with the environment in which they are 55 * running. Applications can not create an instance of this class, but they can 56 * get a singleton instance by invoking {@link #getRuntime()}. 57 * 58 * @see System 59 */ 60 public class Runtime { 61 62 /** 63 * Holds the Singleton global instance of Runtime. 64 */ 65 private static final Runtime mRuntime = new Runtime(); 66 67 /** 68 * Holds the library paths, used for native library lookup. 69 */ 70 private final String[] mLibPaths = initLibPaths(); 71 initLibPaths()72 private static String[] initLibPaths() { 73 String javaLibraryPath = System.getProperty("java.library.path"); 74 if (javaLibraryPath == null) { 75 return EmptyArray.STRING; 76 } 77 String[] paths = javaLibraryPath.split(":"); 78 // Add a '/' to the end of each directory so we don't have to do it every time. 79 for (int i = 0; i < paths.length; ++i) { 80 if (!paths[i].endsWith("/")) { 81 paths[i] += "/"; 82 } 83 } 84 return paths; 85 } 86 87 /** 88 * Holds the list of threads to run when the VM terminates 89 */ 90 private List<Thread> shutdownHooks = new ArrayList<Thread>(); 91 92 /** 93 * Reflects whether finalization should be run for all objects 94 * when the VM terminates. 95 */ 96 private static boolean finalizeOnExit; 97 98 /** 99 * Reflects whether we are already shutting down the VM. 100 */ 101 private boolean shuttingDown; 102 103 /** 104 * Reflects whether we are tracing method calls. 105 */ 106 private boolean tracingMethods; 107 108 /** 109 * Prevent this class from being instantiated. 110 */ Runtime()111 private Runtime() { 112 } 113 114 /** 115 * Executes the specified command and its arguments in a separate native 116 * process. The new process inherits the environment of the caller. Calling 117 * this method is equivalent to calling {@code exec(progArray, null, null)}. 118 * 119 * @param progArray 120 * the array containing the program to execute as well as any 121 * arguments to the program. 122 * @return the new {@code Process} object that represents the native 123 * process. 124 * @throws IOException 125 * if the requested program can not be executed. 126 */ exec(String[] progArray)127 public Process exec(String[] progArray) throws java.io.IOException { 128 return exec(progArray, null, null); 129 } 130 131 /** 132 * Executes the specified command and its arguments in a separate native 133 * process. The new process uses the environment provided in {@code envp}. 134 * Calling this method is equivalent to calling 135 * {@code exec(progArray, envp, null)}. 136 * 137 * @param progArray 138 * the array containing the program to execute as well as any 139 * arguments to the program. 140 * @param envp 141 * the array containing the environment to start the new process 142 * in. 143 * @return the new {@code Process} object that represents the native 144 * process. 145 * @throws IOException 146 * if the requested program can not be executed. 147 */ exec(String[] progArray, String[] envp)148 public Process exec(String[] progArray, String[] envp) throws java.io.IOException { 149 return exec(progArray, envp, null); 150 } 151 152 /** 153 * Executes the specified command and its arguments in a separate native 154 * process. The new process uses the environment provided in {@code envp} 155 * and the working directory specified by {@code directory}. 156 * 157 * @param progArray 158 * the array containing the program to execute as well as any 159 * arguments to the program. 160 * @param envp 161 * the array containing the environment to start the new process 162 * in. 163 * @param directory 164 * the directory in which to execute the program. If {@code null}, 165 * execute if in the same directory as the parent process. 166 * @return the new {@code Process} object that represents the native 167 * process. 168 * @throws IOException 169 * if the requested program can not be executed. 170 */ exec(String[] progArray, String[] envp, File directory)171 public Process exec(String[] progArray, String[] envp, File directory) throws IOException { 172 // ProcessManager is responsible for all argument checking. 173 return ProcessManager.getInstance().exec(progArray, envp, directory, false); 174 } 175 176 /** 177 * Executes the specified program in a separate native process. The new 178 * process inherits the environment of the caller. Calling this method is 179 * equivalent to calling {@code exec(prog, null, null)}. 180 * 181 * @param prog 182 * the name of the program to execute. 183 * @return the new {@code Process} object that represents the native 184 * process. 185 * @throws IOException 186 * if the requested program can not be executed. 187 */ exec(String prog)188 public Process exec(String prog) throws java.io.IOException { 189 return exec(prog, null, null); 190 } 191 192 /** 193 * Executes the specified program in a separate native process. The new 194 * process uses the environment provided in {@code envp}. Calling this 195 * method is equivalent to calling {@code exec(prog, envp, null)}. 196 * 197 * @param prog 198 * the name of the program to execute. 199 * @param envp 200 * the array containing the environment to start the new process 201 * in. 202 * @return the new {@code Process} object that represents the native 203 * process. 204 * @throws IOException 205 * if the requested program can not be executed. 206 */ exec(String prog, String[] envp)207 public Process exec(String prog, String[] envp) throws java.io.IOException { 208 return exec(prog, envp, null); 209 } 210 211 /** 212 * Executes the specified program in a separate native process. The new 213 * process uses the environment provided in {@code envp} and the working 214 * directory specified by {@code directory}. 215 * 216 * @param prog 217 * the name of the program to execute. 218 * @param envp 219 * the array containing the environment to start the new process 220 * in. 221 * @param directory 222 * the directory in which to execute the program. If {@code null}, 223 * execute if in the same directory as the parent process. 224 * @return the new {@code Process} object that represents the native 225 * process. 226 * @throws IOException 227 * if the requested program can not be executed. 228 */ exec(String prog, String[] envp, File directory)229 public Process exec(String prog, String[] envp, File directory) throws java.io.IOException { 230 // Sanity checks 231 if (prog == null) { 232 throw new NullPointerException("prog == null"); 233 } else if (prog.isEmpty()) { 234 throw new IllegalArgumentException("prog is empty"); 235 } 236 237 // Break down into tokens, as described in Java docs 238 StringTokenizer tokenizer = new StringTokenizer(prog); 239 int length = tokenizer.countTokens(); 240 String[] progArray = new String[length]; 241 for (int i = 0; i < length; i++) { 242 progArray[i] = tokenizer.nextToken(); 243 } 244 245 // Delegate 246 return exec(progArray, envp, directory); 247 } 248 249 /** 250 * Causes the VM to stop running and the program to exit. 251 * If {@link #runFinalizersOnExit(boolean)} has been previously invoked with a 252 * {@code true} argument, then all objects will be properly 253 * garbage-collected and finalized first. 254 * Use 0 to signal success to the calling process and 1 to signal failure. 255 * This method is unlikely to be useful to an Android application. 256 */ exit(int code)257 public void exit(int code) { 258 // Make sure we don't try this several times 259 synchronized(this) { 260 if (!shuttingDown) { 261 shuttingDown = true; 262 263 Thread[] hooks; 264 synchronized (shutdownHooks) { 265 // create a copy of the hooks 266 hooks = new Thread[shutdownHooks.size()]; 267 shutdownHooks.toArray(hooks); 268 } 269 270 // Start all shutdown hooks concurrently 271 for (Thread hook : hooks) { 272 hook.start(); 273 } 274 275 // Wait for all shutdown hooks to finish 276 for (Thread hook : hooks) { 277 try { 278 hook.join(); 279 } catch (InterruptedException ex) { 280 // Ignore, since we are at VM shutdown. 281 } 282 } 283 284 // Ensure finalization on exit, if requested 285 if (finalizeOnExit) { 286 runFinalization(); 287 } 288 289 // Get out of here finally... 290 nativeExit(code); 291 } 292 } 293 } 294 295 /** 296 * Indicates to the VM that it would be a good time to run the 297 * garbage collector. Note that this is a hint only. There is no guarantee 298 * that the garbage collector will actually be run. 299 */ gc()300 public native void gc(); 301 302 /** 303 * Returns the single {@code Runtime} instance for the current application. 304 */ getRuntime()305 public static Runtime getRuntime() { 306 return mRuntime; 307 } 308 309 /** 310 * Loads the shared library found at the given absolute path. 311 * This should be of the form {@code /path/to/library/libMyLibrary.so}. 312 * Most callers should use {@link #loadLibrary(String)} instead, and 313 * let the system find the correct file to load. 314 * 315 * @throws UnsatisfiedLinkError if the library can not be loaded, 316 * either because it's not found or because there is something wrong with it. 317 */ load(String absolutePath)318 public void load(String absolutePath) { 319 load(absolutePath, VMStack.getCallingClassLoader()); 320 } 321 322 /* 323 * Loads the given shared library using the given ClassLoader. 324 */ load(String absolutePath, ClassLoader loader)325 void load(String absolutePath, ClassLoader loader) { 326 if (absolutePath == null) { 327 throw new NullPointerException("absolutePath == null"); 328 } 329 String error = doLoad(absolutePath, loader); 330 if (error != null) { 331 throw new UnsatisfiedLinkError(error); 332 } 333 } 334 335 /** 336 * Loads a shared library. Class loaders have some influence over this 337 * process, but for a typical Android app, it works as follows: 338 * 339 * <p>Given the name {@code "MyLibrary"}, that string will be passed to 340 * {@link System#mapLibraryName}. That means it would be a mistake 341 * for the caller to include the usual {@code "lib"} prefix and {@code ".so"} 342 * suffix. 343 * 344 * <p>That file will then be searched for on the application's native library 345 * search path. This consists of the application's own native library directory 346 * followed by the system's native library directories. 347 * 348 * @throws UnsatisfiedLinkError if the library can not be loaded, 349 * either because it's not found or because there is something wrong with it. 350 */ loadLibrary(String nickname)351 public void loadLibrary(String nickname) { 352 loadLibrary(nickname, VMStack.getCallingClassLoader()); 353 } 354 355 /* 356 * Searches for and loads the given shared library using the given ClassLoader. 357 */ loadLibrary(String libraryName, ClassLoader loader)358 void loadLibrary(String libraryName, ClassLoader loader) { 359 if (loader != null) { 360 String filename = loader.findLibrary(libraryName); 361 if (filename == null) { 362 // It's not necessarily true that the ClassLoader used 363 // System.mapLibraryName, but the default setup does, and it's 364 // misleading to say we didn't find "libMyLibrary.so" when we 365 // actually searched for "liblibMyLibrary.so.so". 366 throw new UnsatisfiedLinkError(loader + " couldn't find \"" + 367 System.mapLibraryName(libraryName) + "\""); 368 } 369 String error = doLoad(filename, loader); 370 if (error != null) { 371 throw new UnsatisfiedLinkError(error); 372 } 373 return; 374 } 375 376 String filename = System.mapLibraryName(libraryName); 377 List<String> candidates = new ArrayList<String>(); 378 String lastError = null; 379 for (String directory : mLibPaths) { 380 String candidate = directory + filename; 381 candidates.add(candidate); 382 383 if (IoUtils.canOpenReadOnly(candidate)) { 384 String error = doLoad(candidate, loader); 385 if (error == null) { 386 return; // We successfully loaded the library. Job done. 387 } 388 lastError = error; 389 } 390 } 391 392 if (lastError != null) { 393 throw new UnsatisfiedLinkError(lastError); 394 } 395 throw new UnsatisfiedLinkError("Library " + libraryName + " not found; tried " + candidates); 396 } 397 nativeExit(int code)398 private static native void nativeExit(int code); 399 doLoad(String name, ClassLoader loader)400 private String doLoad(String name, ClassLoader loader) { 401 // Android apps are forked from the zygote, so they can't have a custom LD_LIBRARY_PATH, 402 // which means that by default an app's shared library directory isn't on LD_LIBRARY_PATH. 403 404 // The PathClassLoader set up by frameworks/base knows the appropriate path, so we can load 405 // libraries with no dependencies just fine, but an app that has multiple libraries that 406 // depend on each other needed to load them in most-dependent-first order. 407 408 // We added API to Android's dynamic linker so we can update the library path used for 409 // the currently-running process. We pull the desired path out of the ClassLoader here 410 // and pass it to nativeLoad so that it can call the private dynamic linker API. 411 412 // We didn't just change frameworks/base to update the LD_LIBRARY_PATH once at the 413 // beginning because multiple apks can run in the same process and third party code can 414 // use its own BaseDexClassLoader. 415 416 // We didn't just add a dlopen_with_custom_LD_LIBRARY_PATH call because we wanted any 417 // dlopen(3) calls made from a .so's JNI_OnLoad to work too. 418 419 // So, find out what the native library search path is for the ClassLoader in question... 420 String ldLibraryPath = null; 421 if (loader != null && loader instanceof BaseDexClassLoader) { 422 ldLibraryPath = ((BaseDexClassLoader) loader).getLdLibraryPath(); 423 } 424 // nativeLoad should be synchronized so there's only one LD_LIBRARY_PATH in use regardless 425 // of how many ClassLoaders are in the system, but dalvik doesn't support synchronized 426 // internal natives. 427 synchronized (this) { 428 return nativeLoad(name, loader, ldLibraryPath); 429 } 430 } 431 432 // TODO: should be synchronized, but dalvik doesn't support synchronized internal natives. nativeLoad(String filename, ClassLoader loader, String ldLibraryPath)433 private static native String nativeLoad(String filename, ClassLoader loader, String ldLibraryPath); 434 435 /** 436 * Provides a hint to the VM that it would be useful to attempt 437 * to perform any outstanding object finalization. 438 */ runFinalization()439 public void runFinalization() { 440 try { 441 FinalizerReference.finalizeAllEnqueued(); 442 } catch (InterruptedException e) { 443 Thread.currentThread().interrupt(); 444 } 445 } 446 447 /** 448 * Sets the flag that indicates whether all objects are finalized when the 449 * VM is about to exit. Note that all finalization which occurs 450 * when the system is exiting is performed after all running threads have 451 * been terminated. 452 * 453 * @param run 454 * {@code true} to enable finalization on exit, {@code false} to 455 * disable it. 456 * @deprecated This method is unsafe. 457 */ 458 @Deprecated runFinalizersOnExit(boolean run)459 public static void runFinalizersOnExit(boolean run) { 460 finalizeOnExit = run; 461 } 462 463 /** 464 * Switches the output of debug information for instructions on or off. 465 * On Android, this method does nothing. 466 */ traceInstructions(boolean enable)467 public void traceInstructions(boolean enable) { 468 } 469 470 /** 471 * Switches the output of debug information for methods on or off. 472 */ traceMethodCalls(boolean enable)473 public void traceMethodCalls(boolean enable) { 474 if (enable != tracingMethods) { 475 if (enable) { 476 VMDebug.startMethodTracing(); 477 } else { 478 VMDebug.stopMethodTracing(); 479 } 480 tracingMethods = enable; 481 } 482 } 483 484 /** 485 * Returns the localized version of the specified input stream. The input 486 * stream that is returned automatically converts all characters from the 487 * local character set to Unicode after reading them from the underlying 488 * stream. 489 * 490 * @param stream 491 * the input stream to localize. 492 * @return the localized input stream. 493 * @deprecated Use {@link InputStreamReader} instead. 494 */ 495 @Deprecated getLocalizedInputStream(InputStream stream)496 public InputStream getLocalizedInputStream(InputStream stream) { 497 String encoding = System.getProperty("file.encoding", "UTF-8"); 498 if (!encoding.equals("UTF-8")) { 499 throw new UnsupportedOperationException("Cannot localize " + encoding); 500 } 501 return stream; 502 } 503 504 /** 505 * Returns the localized version of the specified output stream. The output 506 * stream that is returned automatically converts all characters from 507 * Unicode to the local character set before writing them to the underlying 508 * stream. 509 * 510 * @param stream 511 * the output stream to localize. 512 * @return the localized output stream. 513 * @deprecated Use {@link OutputStreamWriter} instead. 514 */ 515 @Deprecated getLocalizedOutputStream(OutputStream stream)516 public OutputStream getLocalizedOutputStream(OutputStream stream) { 517 String encoding = System.getProperty("file.encoding", "UTF-8"); 518 if (!encoding.equals("UTF-8")) { 519 throw new UnsupportedOperationException("Cannot localize " + encoding); 520 } 521 return stream; 522 } 523 524 /** 525 * Registers a VM shutdown hook. A shutdown hook is a 526 * {@code Thread} that is ready to run, but has not yet been started. All 527 * registered shutdown hooks will be executed when the VM 528 * terminates normally (typically when the {@link #exit(int)} method is called). 529 * 530 * <p><i>Note that on Android, the application lifecycle does not include VM termination, 531 * so calling this method will not ensure that your code is run</i>. Instead, you should 532 * use the most appropriate lifecycle notification ({@code Activity.onPause}, say). 533 * 534 * <p>Shutdown hooks are run concurrently and in an unspecified order. Hooks 535 * failing due to an unhandled exception are not a problem, but the stack 536 * trace might be printed to the console. Once initiated, the whole shutdown 537 * process can only be terminated by calling {@code halt()}. 538 * 539 * <p>If {@link #runFinalizersOnExit(boolean)} has been called with a {@code 540 * true} argument, garbage collection and finalization will take place after 541 * all hooks are either finished or have failed. Then the VM 542 * terminates. 543 * 544 * <p>It is recommended that shutdown hooks do not do any time-consuming 545 * activities, in order to not hold up the shutdown process longer than 546 * necessary. 547 * 548 * @param hook 549 * the shutdown hook to register. 550 * @throws IllegalArgumentException 551 * if the hook has already been started or if it has already 552 * been registered. 553 * @throws IllegalStateException 554 * if the VM is already shutting down. 555 */ addShutdownHook(Thread hook)556 public void addShutdownHook(Thread hook) { 557 // Sanity checks 558 if (hook == null) { 559 throw new NullPointerException("hook == null"); 560 } 561 562 if (shuttingDown) { 563 throw new IllegalStateException("VM already shutting down"); 564 } 565 566 if (hook.hasBeenStarted) { 567 throw new IllegalArgumentException("Hook has already been started"); 568 } 569 570 synchronized (shutdownHooks) { 571 if (shutdownHooks.contains(hook)) { 572 throw new IllegalArgumentException("Hook already registered."); 573 } 574 575 shutdownHooks.add(hook); 576 } 577 } 578 579 /** 580 * Unregisters a previously registered VM shutdown hook. 581 * 582 * @param hook 583 * the shutdown hook to remove. 584 * @return {@code true} if the hook has been removed successfully; {@code 585 * false} otherwise. 586 * @throws IllegalStateException 587 * if the VM is already shutting down. 588 */ removeShutdownHook(Thread hook)589 public boolean removeShutdownHook(Thread hook) { 590 // Sanity checks 591 if (hook == null) { 592 throw new NullPointerException("hook == null"); 593 } 594 595 if (shuttingDown) { 596 throw new IllegalStateException("VM already shutting down"); 597 } 598 599 synchronized (shutdownHooks) { 600 return shutdownHooks.remove(hook); 601 } 602 } 603 604 /** 605 * Causes the VM to stop running, and the program to exit with the given return code. 606 * Use 0 to signal success to the calling process and 1 to signal failure. 607 * Neither shutdown hooks nor finalizers are run before exiting. 608 * This method is unlikely to be useful to an Android application. 609 */ halt(int code)610 public void halt(int code) { 611 // Get out of here... 612 nativeExit(code); 613 } 614 615 /** 616 * Returns the number of processor cores available to the VM, at least 1. 617 * Traditionally this returned the number currently online, 618 * but many mobile devices are able to take unused cores offline to 619 * save power, so releases newer than Android 4.2 (Jelly Bean) return the maximum number of 620 * cores that could be made available if there were no power or heat 621 * constraints. 622 */ availableProcessors()623 public int availableProcessors() { 624 return (int) Libcore.os.sysconf(_SC_NPROCESSORS_CONF); 625 } 626 627 /** 628 * Returns the number of bytes currently available on the heap without expanding the heap. See 629 * {@link #totalMemory} for the heap's current size. When these bytes are exhausted, the heap 630 * may expand. See {@link #maxMemory} for that limit. 631 */ freeMemory()632 public native long freeMemory(); 633 634 /** 635 * Returns the number of bytes taken by the heap at its current size. The heap may expand or 636 * contract over time, as the number of live objects increases or decreases. See 637 * {@link #maxMemory} for the maximum heap size, and {@link #freeMemory} for an idea of how much 638 * the heap could currently contract. 639 */ totalMemory()640 public native long totalMemory(); 641 642 /** 643 * Returns the maximum number of bytes the heap can expand to. See {@link #totalMemory} for the 644 * current number of bytes taken by the heap, and {@link #freeMemory} for the current number of 645 * those bytes actually used by live objects. 646 */ maxMemory()647 public native long maxMemory(); 648 } 649