Runtime.java revision bbfadc8ae01454abba5335fccceaa1c80123ae49
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.io.PipedInputStream; 44import java.io.PipedOutputStream; 45import java.io.Reader; 46import java.io.UnsupportedEncodingException; 47import java.io.Writer; 48import java.nio.charset.Charsets; 49import java.util.ArrayList; 50import java.util.Arrays; 51import java.util.List; 52import java.util.StringTokenizer; 53import libcore.io.Libcore; 54import static libcore.io.OsConstants.*; 55 56/** 57 * Allows Java applications to interface with the environment in which they are 58 * running. Applications can not create an instance of this class, but they can 59 * get a singleton instance by invoking {@link #getRuntime()}. 60 * 61 * @see System 62 */ 63public class Runtime { 64 65 /** 66 * Holds the Singleton global instance of Runtime. 67 */ 68 private static final Runtime mRuntime = new Runtime(); 69 70 /** 71 * Holds the library paths, used for native library lookup. 72 */ 73 private final String[] mLibPaths; 74 75 /** 76 * Holds the list of threads to run when the VM terminates 77 */ 78 private List<Thread> shutdownHooks = new ArrayList<Thread>(); 79 80 /** 81 * Reflects whether finalization should be run for all objects 82 * when the VM terminates. 83 */ 84 private static boolean finalizeOnExit; 85 86 /** 87 * Reflects whether we are already shutting down the VM. 88 */ 89 private boolean shuttingDown; 90 91 /** 92 * Reflects whether we are tracing method calls. 93 */ 94 private boolean tracingMethods; 95 96 /** 97 * Prevent this class from being instantiated. 98 */ 99 private Runtime(){ 100 String pathList = System.getProperty("java.library.path", "."); 101 String pathSep = System.getProperty("path.separator", ":"); 102 String fileSep = System.getProperty("file.separator", "/"); 103 104 mLibPaths = pathList.split(pathSep); 105 106 // Add a '/' to the end so we don't have to do the property lookup 107 // and concatenation later. 108 for (int i = 0; i < mLibPaths.length; i++) { 109 if (!mLibPaths[i].endsWith(fileSep)) { 110 mLibPaths[i] += fileSep; 111 } 112 } 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(); 234 } else if (prog.length() == 0) { 235 throw new IllegalArgumentException(); 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. If 252 * {@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 * 256 * @param code 257 * the return code. By convention, non-zero return codes indicate 258 * abnormal terminations. 259 */ 260 public void exit(int code) { 261 // Make sure we don't try this several times 262 synchronized(this) { 263 if (!shuttingDown) { 264 shuttingDown = true; 265 266 Thread[] hooks; 267 synchronized (shutdownHooks) { 268 // create a copy of the hooks 269 hooks = new Thread[shutdownHooks.size()]; 270 shutdownHooks.toArray(hooks); 271 } 272 273 // Start all shutdown hooks concurrently 274 for (Thread hook : hooks) { 275 hook.start(); 276 } 277 278 // Wait for all shutdown hooks to finish 279 for (Thread hook : hooks) { 280 try { 281 hook.join(); 282 } catch (InterruptedException ex) { 283 // Ignore, since we are at VM shutdown. 284 } 285 } 286 287 // Ensure finalization on exit, if requested 288 if (finalizeOnExit) { 289 runFinalization(); 290 } 291 292 // Get out of here finally... 293 nativeExit(code, true); 294 } 295 } 296 } 297 298 /** 299 * Returns the amount of free memory resources which are available to the 300 * running program. 301 * 302 * @return the approximate amount of free memory, measured in bytes. 303 */ 304 public native long freeMemory(); 305 306 /** 307 * Indicates to the VM that it would be a good time to run the 308 * garbage collector. Note that this is a hint only. There is no guarantee 309 * that the garbage collector will actually be run. 310 */ 311 public native void gc(); 312 313 /** 314 * Returns the single {@code Runtime} instance. 315 * 316 * @return the {@code Runtime} object for the current application. 317 */ 318 public static Runtime getRuntime() { 319 return mRuntime; 320 } 321 322 /** 323 * Loads and links the dynamic library that is identified through the 324 * specified path. This method is similar to {@link #loadLibrary(String)}, 325 * but it accepts a full path specification whereas {@code loadLibrary} just 326 * accepts the name of the library to load. 327 * 328 * @param pathName 329 * the absolute (platform dependent) path to the library to load. 330 * @throws UnsatisfiedLinkError 331 * if the library can not be loaded. 332 */ 333 public void load(String pathName) { 334 load(pathName, VMStack.getCallingClassLoader()); 335 } 336 337 /* 338 * Loads and links a library without security checks. 339 */ 340 void load(String filename, ClassLoader loader) { 341 if (filename == null) { 342 throw new NullPointerException("library path was null."); 343 } 344 String error = nativeLoad(filename, loader); 345 if (error != null) { 346 throw new UnsatisfiedLinkError(error); 347 } 348 } 349 350 /** 351 * Loads and links the library with the specified name. The mapping of the 352 * specified library name to the full path for loading the library is 353 * implementation-dependent. 354 * 355 * @param libName 356 * the name of the library to load. 357 * @throws UnsatisfiedLinkError 358 * if the library can not be loaded. 359 */ 360 public void loadLibrary(String libName) { 361 loadLibrary(libName, VMStack.getCallingClassLoader()); 362 } 363 364 /* 365 * Loads and links a library without security checks. 366 */ 367 void loadLibrary(String libraryName, ClassLoader loader) { 368 if (loader != null) { 369 String filename = loader.findLibrary(libraryName); 370 if (filename == null) { 371 throw new UnsatisfiedLinkError("Couldn't load " + libraryName + ": " + 372 "findLibrary returned null"); 373 } 374 String error = nativeLoad(filename, loader); 375 if (error != null) { 376 throw new UnsatisfiedLinkError(error); 377 } 378 return; 379 } 380 381 String filename = System.mapLibraryName(libraryName); 382 List<String> candidates = new ArrayList<String>(); 383 String lastError = null; 384 for (String directory : mLibPaths) { 385 String candidate = directory + filename; 386 candidates.add(candidate); 387 if (new File(candidate).exists()) { 388 String error = nativeLoad(candidate, loader); 389 if (error == null) { 390 return; // We successfully loaded the library. Job done. 391 } 392 lastError = error; 393 } 394 } 395 396 if (lastError != null) { 397 throw new UnsatisfiedLinkError(lastError); 398 } 399 throw new UnsatisfiedLinkError("Library " + libraryName + " not found; tried " + candidates); 400 } 401 402 private static native void nativeExit(int code, boolean isExit); 403 404 private static native String nativeLoad(String filename, ClassLoader loader); 405 406 /** 407 * Provides a hint to the VM that it would be useful to attempt 408 * to perform any outstanding object finalization. 409 * 410 */ 411 public void runFinalization() { 412 FinalizerThread.runFinalization(); 413 } 414 415 /** 416 * Sets the flag that indicates whether all objects are finalized when the 417 * VM is about to exit. Note that all finalization which occurs 418 * when the system is exiting is performed after all running threads have 419 * been terminated. 420 * 421 * @param run 422 * {@code true} to enable finalization on exit, {@code false} to 423 * disable it. 424 * @deprecated This method is unsafe. 425 */ 426 @Deprecated 427 public static void runFinalizersOnExit(boolean run) { 428 finalizeOnExit = run; 429 } 430 431 /** 432 * Returns the total amount of memory which is available to the running 433 * program. 434 * 435 * @return the total amount of memory, measured in bytes. 436 */ 437 public native long totalMemory(); 438 439 /** 440 * Switches the output of debug information for instructions on or off. 441 * On Android, this method does nothing. 442 * 443 * @param enable 444 * {@code true} to switch tracing on, {@code false} to switch it 445 * off. 446 */ 447 public void traceInstructions(boolean enable) { 448 } 449 450 /** 451 * Switches the output of debug information for methods on or off. 452 * 453 * @param enable 454 * {@code true} to switch tracing on, {@code false} to switch it 455 * off. 456 */ 457 public void traceMethodCalls(boolean enable) { 458 if (enable != tracingMethods) { 459 if (enable) { 460 VMDebug.startMethodTracing(); 461 } else { 462 VMDebug.stopMethodTracing(); 463 } 464 tracingMethods = enable; 465 } 466 } 467 468 /** 469 * Returns the localized version of the specified input stream. The input 470 * stream that is returned automatically converts all characters from the 471 * local character set to Unicode after reading them from the underlying 472 * stream. 473 * 474 * @param stream 475 * the input stream to localize. 476 * @return the localized input stream. 477 * @deprecated Use {@link InputStreamReader}. 478 */ 479 @Deprecated 480 public InputStream getLocalizedInputStream(InputStream stream) { 481 String encoding = System.getProperty("file.encoding", "UTF-8"); 482 if (!encoding.equals("UTF-8")) { 483 throw new UnsupportedOperationException("Cannot localize " + encoding); 484 } 485 return stream; 486 } 487 488 /** 489 * Returns the localized version of the specified output stream. The output 490 * stream that is returned automatically converts all characters from 491 * Unicode to the local character set before writing them to the underlying 492 * stream. 493 * 494 * @param stream 495 * the output stream to localize. 496 * @return the localized output stream. 497 * @deprecated Use {@link OutputStreamWriter}. 498 */ 499 @Deprecated 500 public OutputStream getLocalizedOutputStream(OutputStream stream) { 501 String encoding = System.getProperty("file.encoding", "UTF-8"); 502 if (!encoding.equals("UTF-8")) { 503 throw new UnsupportedOperationException("Cannot localize " + encoding); 504 } 505 return stream; 506 } 507 508 /** 509 * Registers a VM shutdown hook. A shutdown hook is a 510 * {@code Thread} that is ready to run, but has not yet been started. All 511 * registered shutdown hooks will be executed when the VM 512 * terminates normally (typically when the {@link #exit(int)} method is called). 513 * 514 * <p><i>Note that on Android, the application lifecycle does not include VM termination, 515 * so calling this method will not ensure that your code is run</i>. Instead, you should 516 * use the most appropriate lifecycle notification ({@code Activity.onPause}, say). 517 * 518 * <p>Shutdown hooks are run concurrently and in an unspecified order. Hooks 519 * failing due to an unhandled exception are not a problem, but the stack 520 * trace might be printed to the console. Once initiated, the whole shutdown 521 * process can only be terminated by calling {@code halt()}. 522 * 523 * <p>If {@link #runFinalizersOnExit(boolean)} has been called with a {@code 524 * true} argument, garbage collection and finalization will take place after 525 * all hooks are either finished or have failed. Then the VM 526 * terminates. 527 * 528 * <p>It is recommended that shutdown hooks do not do any time-consuming 529 * activities, in order to not hold up the shutdown process longer than 530 * necessary. 531 * 532 * @param hook 533 * the shutdown hook to register. 534 * @throws IllegalArgumentException 535 * if the hook has already been started or if it has already 536 * been registered. 537 * @throws IllegalStateException 538 * if the VM is already shutting down. 539 */ 540 public void addShutdownHook(Thread hook) { 541 // Sanity checks 542 if (hook == null) { 543 throw new NullPointerException("Hook may not be null."); 544 } 545 546 if (shuttingDown) { 547 throw new IllegalStateException("VM already shutting down"); 548 } 549 550 if (hook.hasBeenStarted) { 551 throw new IllegalArgumentException("Hook has already been started"); 552 } 553 554 synchronized (shutdownHooks) { 555 if (shutdownHooks.contains(hook)) { 556 throw new IllegalArgumentException("Hook already registered."); 557 } 558 559 shutdownHooks.add(hook); 560 } 561 } 562 563 /** 564 * Unregisters a previously registered VM shutdown hook. 565 * 566 * @param hook 567 * the shutdown hook to remove. 568 * @return {@code true} if the hook has been removed successfully; {@code 569 * false} otherwise. 570 * @throws IllegalStateException 571 * if the VM is already shutting down. 572 */ 573 public boolean removeShutdownHook(Thread hook) { 574 // Sanity checks 575 if (hook == null) { 576 throw new NullPointerException("Hook may not be null."); 577 } 578 579 if (shuttingDown) { 580 throw new IllegalStateException("VM already shutting down"); 581 } 582 583 synchronized (shutdownHooks) { 584 return shutdownHooks.remove(hook); 585 } 586 } 587 588 /** 589 * Causes the VM to stop running, and the program to exit. 590 * Neither shutdown hooks nor finalizers are run before. 591 * 592 * @param code 593 * the return code. By convention, non-zero return codes indicate 594 * abnormal terminations. 595 * @see #addShutdownHook(Thread) 596 * @see #removeShutdownHook(Thread) 597 * @see #runFinalizersOnExit(boolean) 598 */ 599 public void halt(int code) { 600 // Get out of here... 601 nativeExit(code, false); 602 } 603 604 /** 605 * Returns the number of processors available to the VM, at least 1. 606 */ 607 public int availableProcessors() { 608 return (int) Libcore.os.sysconf(_SC_NPROCESSORS_ONLN); 609 } 610 611 /** 612 * Returns the maximum amount of memory that may be used by the virtual 613 * machine, or {@code Long.MAX_VALUE} if there is no such limit. 614 * 615 * @return the maximum amount of memory that the VM will try to 616 * allocate, measured in bytes. 617 */ 618 public native long maxMemory(); 619} 620