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