Runtime.java revision 6d17baa25d349e2659eb16fe5eef3088d48e5e22
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.VMDebug; 36import dalvik.system.VMStack; 37import java.io.File; 38import java.io.IOException; 39import java.io.InputStream; 40import java.io.InputStreamReader; 41import java.io.OutputStream; 42import java.io.OutputStreamWriter; 43import java.lang.ref.FinalizerReference; 44import java.util.ArrayList; 45import java.util.List; 46import java.util.StringTokenizer; 47import libcore.io.IoUtils; 48import libcore.io.Libcore; 49import static libcore.io.OsConstants._SC_NPROCESSORS_CONF; 50 51/** 52 * Allows Java applications to interface with the environment in which they are 53 * running. Applications can not create an instance of this class, but they can 54 * get a singleton instance by invoking {@link #getRuntime()}. 55 * 56 * @see System 57 */ 58public class Runtime { 59 60 /** 61 * Holds the Singleton global instance of Runtime. 62 */ 63 private static final Runtime mRuntime = new Runtime(); 64 65 /** 66 * Holds the library paths, used for native library lookup. 67 */ 68 private final String[] mLibPaths; 69 70 /** 71 * Holds the list of threads to run when the VM terminates 72 */ 73 private List<Thread> shutdownHooks = new ArrayList<Thread>(); 74 75 /** 76 * Reflects whether finalization should be run for all objects 77 * when the VM terminates. 78 */ 79 private static boolean finalizeOnExit; 80 81 /** 82 * Reflects whether we are already shutting down the VM. 83 */ 84 private boolean shuttingDown; 85 86 /** 87 * Reflects whether we are tracing method calls. 88 */ 89 private boolean tracingMethods; 90 91 /** 92 * Prevent this class from being instantiated. 93 */ 94 private Runtime() { 95 String pathList = System.getProperty("java.library.path", "."); 96 String pathSep = System.getProperty("path.separator", ":"); 97 String fileSep = System.getProperty("file.separator", "/"); 98 99 mLibPaths = pathList.split(pathSep); 100 101 // Add a '/' to the end so we don't have to do the property lookup 102 // and concatenation later. 103 for (int i = 0; i < mLibPaths.length; i++) { 104 if (!mLibPaths[i].endsWith(fileSep)) { 105 mLibPaths[i] += fileSep; 106 } 107 } 108 } 109 110 /** 111 * Executes the specified command and its arguments in a separate native 112 * process. The new process inherits the environment of the caller. Calling 113 * this method is equivalent to calling {@code exec(progArray, null, null)}. 114 * 115 * @param progArray 116 * the array containing the program to execute as well as any 117 * arguments to the program. 118 * @return the new {@code Process} object that represents the native 119 * process. 120 * @throws IOException 121 * if the requested program can not be executed. 122 */ 123 public Process exec(String[] progArray) throws java.io.IOException { 124 return exec(progArray, null, null); 125 } 126 127 /** 128 * Executes the specified command and its arguments in a separate native 129 * process. The new process uses the environment provided in {@code envp}. 130 * Calling this method is equivalent to calling 131 * {@code exec(progArray, envp, null)}. 132 * 133 * @param progArray 134 * the array containing the program to execute as well as any 135 * arguments to the program. 136 * @param envp 137 * the array containing the environment to start the new process 138 * in. 139 * @return the new {@code Process} object that represents the native 140 * process. 141 * @throws IOException 142 * if the requested program can not be executed. 143 */ 144 public Process exec(String[] progArray, String[] envp) throws java.io.IOException { 145 return exec(progArray, envp, null); 146 } 147 148 /** 149 * Executes the specified command and its arguments in a separate native 150 * process. The new process uses the environment provided in {@code envp} 151 * and the working directory specified by {@code directory}. 152 * 153 * @param progArray 154 * the array containing the program to execute as well as any 155 * arguments to the program. 156 * @param envp 157 * the array containing the environment to start the new process 158 * in. 159 * @param directory 160 * the directory in which to execute the program. If {@code null}, 161 * execute if in the same directory as the parent process. 162 * @return the new {@code Process} object that represents the native 163 * process. 164 * @throws IOException 165 * if the requested program can not be executed. 166 */ 167 public Process exec(String[] progArray, String[] envp, File directory) throws IOException { 168 // ProcessManager is responsible for all argument checking. 169 return ProcessManager.getInstance().exec(progArray, envp, directory, false); 170 } 171 172 /** 173 * Executes the specified program in a separate native process. The new 174 * process inherits the environment of the caller. Calling this method is 175 * equivalent to calling {@code exec(prog, null, null)}. 176 * 177 * @param prog 178 * the name of the program to execute. 179 * @return the new {@code Process} object that represents the native 180 * process. 181 * @throws IOException 182 * if the requested program can not be executed. 183 */ 184 public Process exec(String prog) throws java.io.IOException { 185 return exec(prog, null, null); 186 } 187 188 /** 189 * Executes the specified program in a separate native process. The new 190 * process uses the environment provided in {@code envp}. Calling this 191 * method is equivalent to calling {@code exec(prog, envp, null)}. 192 * 193 * @param prog 194 * the name of the program to execute. 195 * @param envp 196 * the array containing the environment to start the new process 197 * in. 198 * @return the new {@code Process} object that represents the native 199 * process. 200 * @throws IOException 201 * if the requested program can not be executed. 202 */ 203 public Process exec(String prog, String[] envp) throws java.io.IOException { 204 return exec(prog, envp, null); 205 } 206 207 /** 208 * Executes the specified program in a separate native process. The new 209 * process uses the environment provided in {@code envp} and the working 210 * directory specified by {@code directory}. 211 * 212 * @param prog 213 * the name of the program to execute. 214 * @param envp 215 * the array containing the environment to start the new process 216 * in. 217 * @param directory 218 * the directory in which to execute the program. If {@code null}, 219 * execute if in the same directory as the parent process. 220 * @return the new {@code Process} object that represents the native 221 * process. 222 * @throws IOException 223 * if the requested program can not be executed. 224 */ 225 public Process exec(String prog, String[] envp, File directory) throws java.io.IOException { 226 // Sanity checks 227 if (prog == null) { 228 throw new NullPointerException("prog == null"); 229 } else if (prog.isEmpty()) { 230 throw new IllegalArgumentException("prog is empty"); 231 } 232 233 // Break down into tokens, as described in Java docs 234 StringTokenizer tokenizer = new StringTokenizer(prog); 235 int length = tokenizer.countTokens(); 236 String[] progArray = new String[length]; 237 for (int i = 0; i < length; i++) { 238 progArray[i] = tokenizer.nextToken(); 239 } 240 241 // Delegate 242 return exec(progArray, envp, directory); 243 } 244 245 /** 246 * Causes the VM to stop running and the program to exit. 247 * If {@link #runFinalizersOnExit(boolean)} has been previously invoked with a 248 * {@code true} argument, then all objects will be properly 249 * garbage-collected and finalized first. 250 * Use 0 to signal success to the calling process and 1 to signal failure. 251 * This method is unlikely to be useful to an Android application. 252 */ 253 public void exit(int code) { 254 // Make sure we don't try this several times 255 synchronized(this) { 256 if (!shuttingDown) { 257 shuttingDown = true; 258 259 Thread[] hooks; 260 synchronized (shutdownHooks) { 261 // create a copy of the hooks 262 hooks = new Thread[shutdownHooks.size()]; 263 shutdownHooks.toArray(hooks); 264 } 265 266 // Start all shutdown hooks concurrently 267 for (Thread hook : hooks) { 268 hook.start(); 269 } 270 271 // Wait for all shutdown hooks to finish 272 for (Thread hook : hooks) { 273 try { 274 hook.join(); 275 } catch (InterruptedException ex) { 276 // Ignore, since we are at VM shutdown. 277 } 278 } 279 280 // Ensure finalization on exit, if requested 281 if (finalizeOnExit) { 282 runFinalization(); 283 } 284 285 // Get out of here finally... 286 nativeExit(code); 287 } 288 } 289 } 290 291 /** 292 * Returns the amount of free memory available to the running program, in bytes. 293 */ 294 public native long freeMemory(); 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 and links the dynamic library that is identified through the 312 * specified path. This method is similar to {@link #loadLibrary(String)}, 313 * but it accepts a full path specification whereas {@code loadLibrary} just 314 * accepts the name of the library to load. 315 * 316 * @param pathName 317 * the absolute (platform dependent) path to the library to load. 318 * @throws UnsatisfiedLinkError 319 * if the library can not be loaded. 320 */ 321 public void load(String pathName) { 322 load(pathName, VMStack.getCallingClassLoader()); 323 } 324 325 /* 326 * Loads and links the given library without security checks. 327 */ 328 void load(String pathName, ClassLoader loader) { 329 if (pathName == null) { 330 throw new NullPointerException("pathName == null"); 331 } 332 String error = nativeLoad(pathName, loader); 333 if (error != null) { 334 throw new UnsatisfiedLinkError(error); 335 } 336 } 337 338 /** 339 * Loads and links the library with the specified name. The mapping of the 340 * specified library name to the full path for loading the library is 341 * implementation-dependent. 342 * 343 * @param libName 344 * the name of the library to load. 345 * @throws UnsatisfiedLinkError 346 * if the library can not be loaded. 347 */ 348 public void loadLibrary(String libName) { 349 loadLibrary(libName, VMStack.getCallingClassLoader()); 350 } 351 352 /* 353 * Searches for a library, then loads and links it without security checks. 354 */ 355 void loadLibrary(String libraryName, ClassLoader loader) { 356 if (loader != null) { 357 String filename = loader.findLibrary(libraryName); 358 if (filename == null) { 359 throw new UnsatisfiedLinkError("Couldn't load " + libraryName 360 + " from loader " + loader 361 + ": findLibrary returned null"); 362 } 363 String error = nativeLoad(filename, loader); 364 if (error != null) { 365 throw new UnsatisfiedLinkError(error); 366 } 367 return; 368 } 369 370 String filename = System.mapLibraryName(libraryName); 371 List<String> candidates = new ArrayList<String>(); 372 String lastError = null; 373 for (String directory : mLibPaths) { 374 String candidate = directory + filename; 375 candidates.add(candidate); 376 377 if (IoUtils.canOpenReadOnly(candidate)) { 378 String error = nativeLoad(candidate, loader); 379 if (error == null) { 380 return; // We successfully loaded the library. Job done. 381 } 382 lastError = error; 383 } 384 } 385 386 if (lastError != null) { 387 throw new UnsatisfiedLinkError(lastError); 388 } 389 throw new UnsatisfiedLinkError("Library " + libraryName + " not found; tried " + candidates); 390 } 391 392 private static native void nativeExit(int code); 393 394 private static native String nativeLoad(String filename, ClassLoader loader); 395 396 /** 397 * Provides a hint to the VM that it would be useful to attempt 398 * to perform any outstanding object finalization. 399 */ 400 public void runFinalization() { 401 try { 402 FinalizerReference.finalizeAllEnqueued(); 403 } catch (InterruptedException e) { 404 Thread.currentThread().interrupt(); 405 } 406 } 407 408 /** 409 * Sets the flag that indicates whether all objects are finalized when the 410 * VM is about to exit. Note that all finalization which occurs 411 * when the system is exiting is performed after all running threads have 412 * been terminated. 413 * 414 * @param run 415 * {@code true} to enable finalization on exit, {@code false} to 416 * disable it. 417 * @deprecated This method is unsafe. 418 */ 419 @Deprecated 420 public static void runFinalizersOnExit(boolean run) { 421 finalizeOnExit = run; 422 } 423 424 /** 425 * Returns the total amount of memory which is available to the running 426 * program, in bytes. 427 */ 428 public native long totalMemory(); 429 430 /** 431 * Switches the output of debug information for instructions on or off. 432 * On Android, this method does nothing. 433 */ 434 public void traceInstructions(boolean enable) { 435 } 436 437 /** 438 * Switches the output of debug information for methods on or off. 439 */ 440 public void traceMethodCalls(boolean enable) { 441 if (enable != tracingMethods) { 442 if (enable) { 443 VMDebug.startMethodTracing(); 444 } else { 445 VMDebug.stopMethodTracing(); 446 } 447 tracingMethods = enable; 448 } 449 } 450 451 /** 452 * Returns the localized version of the specified input stream. The input 453 * stream that is returned automatically converts all characters from the 454 * local character set to Unicode after reading them from the underlying 455 * stream. 456 * 457 * @param stream 458 * the input stream to localize. 459 * @return the localized input stream. 460 * @deprecated Use {@link InputStreamReader}. 461 */ 462 @Deprecated 463 public InputStream getLocalizedInputStream(InputStream stream) { 464 String encoding = System.getProperty("file.encoding", "UTF-8"); 465 if (!encoding.equals("UTF-8")) { 466 throw new UnsupportedOperationException("Cannot localize " + encoding); 467 } 468 return stream; 469 } 470 471 /** 472 * Returns the localized version of the specified output stream. The output 473 * stream that is returned automatically converts all characters from 474 * Unicode to the local character set before writing them to the underlying 475 * stream. 476 * 477 * @param stream 478 * the output stream to localize. 479 * @return the localized output stream. 480 * @deprecated Use {@link OutputStreamWriter}. 481 */ 482 @Deprecated 483 public OutputStream getLocalizedOutputStream(OutputStream stream) { 484 String encoding = System.getProperty("file.encoding", "UTF-8"); 485 if (!encoding.equals("UTF-8")) { 486 throw new UnsupportedOperationException("Cannot localize " + encoding); 487 } 488 return stream; 489 } 490 491 /** 492 * Registers a VM shutdown hook. A shutdown hook is a 493 * {@code Thread} that is ready to run, but has not yet been started. All 494 * registered shutdown hooks will be executed when the VM 495 * terminates normally (typically when the {@link #exit(int)} method is called). 496 * 497 * <p><i>Note that on Android, the application lifecycle does not include VM termination, 498 * so calling this method will not ensure that your code is run</i>. Instead, you should 499 * use the most appropriate lifecycle notification ({@code Activity.onPause}, say). 500 * 501 * <p>Shutdown hooks are run concurrently and in an unspecified order. Hooks 502 * failing due to an unhandled exception are not a problem, but the stack 503 * trace might be printed to the console. Once initiated, the whole shutdown 504 * process can only be terminated by calling {@code halt()}. 505 * 506 * <p>If {@link #runFinalizersOnExit(boolean)} has been called with a {@code 507 * true} argument, garbage collection and finalization will take place after 508 * all hooks are either finished or have failed. Then the VM 509 * terminates. 510 * 511 * <p>It is recommended that shutdown hooks do not do any time-consuming 512 * activities, in order to not hold up the shutdown process longer than 513 * necessary. 514 * 515 * @param hook 516 * the shutdown hook to register. 517 * @throws IllegalArgumentException 518 * if the hook has already been started or if it has already 519 * been registered. 520 * @throws IllegalStateException 521 * if the VM is already shutting down. 522 */ 523 public void addShutdownHook(Thread hook) { 524 // Sanity checks 525 if (hook == null) { 526 throw new NullPointerException("hook == null"); 527 } 528 529 if (shuttingDown) { 530 throw new IllegalStateException("VM already shutting down"); 531 } 532 533 if (hook.hasBeenStarted) { 534 throw new IllegalArgumentException("Hook has already been started"); 535 } 536 537 synchronized (shutdownHooks) { 538 if (shutdownHooks.contains(hook)) { 539 throw new IllegalArgumentException("Hook already registered."); 540 } 541 542 shutdownHooks.add(hook); 543 } 544 } 545 546 /** 547 * Unregisters a previously registered VM shutdown hook. 548 * 549 * @param hook 550 * the shutdown hook to remove. 551 * @return {@code true} if the hook has been removed successfully; {@code 552 * false} otherwise. 553 * @throws IllegalStateException 554 * if the VM is already shutting down. 555 */ 556 public boolean removeShutdownHook(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 synchronized (shutdownHooks) { 567 return shutdownHooks.remove(hook); 568 } 569 } 570 571 /** 572 * Causes the VM to stop running, and the program to exit with the given return code. 573 * Use 0 to signal success to the calling process and 1 to signal failure. 574 * Neither shutdown hooks nor finalizers are run before exiting. 575 * This method is unlikely to be useful to an Android application. 576 */ 577 public void halt(int code) { 578 // Get out of here... 579 nativeExit(code); 580 } 581 582 /** 583 * Returns the number of processor cores available to the VM, at least 1. 584 * Traditionally this returned the number currently online, 585 * but many mobile devices are able to take unused cores offline to 586 * save power, so releases newer than Android 4.2 (Jelly Bean) return the maximum number of 587 * cores that could be made available if there were no power or heat 588 * constraints. 589 */ 590 public int availableProcessors() { 591 return (int) Libcore.os.sysconf(_SC_NPROCESSORS_CONF); 592 } 593 594 /** 595 * Returns the maximum amount of memory that may be used by the virtual 596 * machine, or {@code Long.MAX_VALUE} if there is no such limit. 597 * 598 * @return the maximum amount of memory that the VM will try to 599 * allocate, measured in bytes. 600 */ 601 public native long maxMemory(); 602} 603