Runtime.java revision e1ee5a2321507cbfbd4a4381b4c340b06782c58a
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(true); 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 * Requests proper finalization for all Objects on the heap. 408 * 409 * @param forced Decides whether the VM really needs to do this (true) 410 * or if this is just a suggestion that can safely be ignored 411 * (false). 412 */ 413 private native void runFinalization(boolean forced); 414 415 /** 416 * Provides a hint to the VM that it would be useful to attempt 417 * to perform any outstanding object finalization. 418 * 419 */ 420 public void runFinalization() { 421 runFinalization(false); 422 } 423 424 /** 425 * Sets the flag that indicates whether all objects are finalized when the 426 * VM is about to exit. Note that all finalization which occurs 427 * when the system is exiting is performed after all running threads have 428 * been terminated. 429 * 430 * @param run 431 * {@code true} to enable finalization on exit, {@code false} to 432 * disable it. 433 * @deprecated This method is unsafe. 434 */ 435 @Deprecated 436 public static void runFinalizersOnExit(boolean run) { 437 finalizeOnExit = run; 438 } 439 440 /** 441 * Returns the total amount of memory which is available to the running 442 * program. 443 * 444 * @return the total amount of memory, measured in bytes. 445 */ 446 public native long totalMemory(); 447 448 /** 449 * Switches the output of debug information for instructions on or off. 450 * On Android, this method does nothing. 451 * 452 * @param enable 453 * {@code true} to switch tracing on, {@code false} to switch it 454 * off. 455 */ 456 public void traceInstructions(boolean enable) { 457 return; 458 } 459 460 /** 461 * Switches the output of debug information for methods on or off. 462 * 463 * @param enable 464 * {@code true} to switch tracing on, {@code false} to switch it 465 * off. 466 */ 467 public void traceMethodCalls(boolean enable) { 468 if (enable != tracingMethods) { 469 if (enable) { 470 VMDebug.startMethodTracing(); 471 } else { 472 VMDebug.stopMethodTracing(); 473 } 474 tracingMethods = enable; 475 } 476 } 477 478 /** 479 * Returns the localized version of the specified input stream. The input 480 * stream that is returned automatically converts all characters from the 481 * local character set to Unicode after reading them from the underlying 482 * stream. 483 * 484 * @param stream 485 * the input stream to localize. 486 * @return the localized input stream. 487 * @deprecated Use {@link InputStreamReader}. 488 */ 489 @Deprecated 490 public InputStream getLocalizedInputStream(InputStream stream) { 491 String encoding = System.getProperty("file.encoding", "UTF-8"); 492 if (!encoding.equals("UTF-8")) { 493 throw new UnsupportedOperationException("Cannot localize " + encoding); 494 } 495 return stream; 496 } 497 498 /** 499 * Returns the localized version of the specified output stream. The output 500 * stream that is returned automatically converts all characters from 501 * Unicode to the local character set before writing them to the underlying 502 * stream. 503 * 504 * @param stream 505 * the output stream to localize. 506 * @return the localized output stream. 507 * @deprecated Use {@link OutputStreamWriter}. 508 */ 509 @Deprecated 510 public OutputStream getLocalizedOutputStream(OutputStream stream) { 511 String encoding = System.getProperty("file.encoding", "UTF-8"); 512 if (!encoding.equals("UTF-8")) { 513 throw new UnsupportedOperationException("Cannot localize " + encoding); 514 } 515 return stream; 516 } 517 518 /** 519 * Registers a VM shutdown hook. A shutdown hook is a 520 * {@code Thread} that is ready to run, but has not yet been started. All 521 * registered shutdown hooks will be executed when the VM 522 * terminates normally (typically when the {@link #exit(int)} method is called). 523 * 524 * <p><i>Note that on Android, the application lifecycle does not include VM termination, 525 * so calling this method will not ensure that your code is run</i>. Instead, you should 526 * use the most appropriate lifecycle notification ({@code Activity.onPause}, say). 527 * 528 * <p>Shutdown hooks are run concurrently and in an unspecified order. Hooks 529 * failing due to an unhandled exception are not a problem, but the stack 530 * trace might be printed to the console. Once initiated, the whole shutdown 531 * process can only be terminated by calling {@code halt()}. 532 * 533 * <p>If {@link #runFinalizersOnExit(boolean)} has been called with a {@code 534 * true} argument, garbage collection and finalization will take place after 535 * all hooks are either finished or have failed. Then the VM 536 * terminates. 537 * 538 * <p>It is recommended that shutdown hooks do not do any time-consuming 539 * activities, in order to not hold up the shutdown process longer than 540 * necessary. 541 * 542 * @param hook 543 * the shutdown hook to register. 544 * @throws IllegalArgumentException 545 * if the hook has already been started or if it has already 546 * been registered. 547 * @throws IllegalStateException 548 * if the VM is already shutting down. 549 */ 550 public void addShutdownHook(Thread hook) { 551 // Sanity checks 552 if (hook == null) { 553 throw new NullPointerException("Hook may not be null."); 554 } 555 556 if (shuttingDown) { 557 throw new IllegalStateException("VM already shutting down"); 558 } 559 560 if (hook.hasBeenStarted) { 561 throw new IllegalArgumentException("Hook has already been started"); 562 } 563 564 synchronized (shutdownHooks) { 565 if (shutdownHooks.contains(hook)) { 566 throw new IllegalArgumentException("Hook already registered."); 567 } 568 569 shutdownHooks.add(hook); 570 } 571 } 572 573 /** 574 * Unregisters a previously registered VM shutdown hook. 575 * 576 * @param hook 577 * the shutdown hook to remove. 578 * @return {@code true} if the hook has been removed successfully; {@code 579 * false} otherwise. 580 * @throws IllegalStateException 581 * if the VM is already shutting down. 582 */ 583 public boolean removeShutdownHook(Thread hook) { 584 // Sanity checks 585 if (hook == null) { 586 throw new NullPointerException("Hook may not be null."); 587 } 588 589 if (shuttingDown) { 590 throw new IllegalStateException("VM already shutting down"); 591 } 592 593 synchronized (shutdownHooks) { 594 return shutdownHooks.remove(hook); 595 } 596 } 597 598 /** 599 * Causes the VM to stop running, and the program to exit. 600 * Neither shutdown hooks nor finalizers are run before. 601 * 602 * @param code 603 * the return code. By convention, non-zero return codes indicate 604 * abnormal terminations. 605 * @see #addShutdownHook(Thread) 606 * @see #removeShutdownHook(Thread) 607 * @see #runFinalizersOnExit(boolean) 608 */ 609 public void halt(int code) { 610 // Get out of here... 611 nativeExit(code, false); 612 } 613 614 /** 615 * Returns the number of processors available to the VM, at least 1. 616 */ 617 public int availableProcessors() { 618 return (int) Libcore.os.sysconf(_SC_NPROCESSORS_ONLN); 619 } 620 621 /** 622 * Returns the maximum amount of memory that may be used by the virtual 623 * machine, or {@code Long.MAX_VALUE} if there is no such limit. 624 * 625 * @return the maximum amount of memory that the VM will try to 626 * allocate, measured in bytes. 627 */ 628 public native long maxMemory(); 629} 630