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