• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
33 package java.lang;
34 
35 import dalvik.system.VMDebug;
36 import dalvik.system.VMStack;
37 import java.io.File;
38 import java.io.IOException;
39 import java.io.InputStream;
40 import java.io.InputStreamReader;
41 import java.io.OutputStream;
42 import java.io.OutputStreamWriter;
43 import java.lang.ref.FinalizerReference;
44 import java.util.ArrayList;
45 import java.util.List;
46 import java.util.StringTokenizer;
47 import libcore.io.Libcore;
48 import 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  */
57 public 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      */
Runtime()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      */
exec(String[] progArray)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      */
exec(String[] progArray, String[] envp)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      */
exec(String[] progArray, String[] envp, File directory)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      */
exec(String prog)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      */
exec(String prog, String[] envp)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      */
exec(String prog, String[] envp, File directory)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      */
exit(int code)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      */
freeMemory()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      */
gc()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      */
getRuntime()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      */
load(String pathName)327     public void load(String pathName) {
328         load(pathName, VMStack.getCallingClassLoader());
329     }
330 
331     /*
332      * Loads and links a library without security checks.
333      */
load(String filename, ClassLoader loader)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      */
loadLibrary(String libName)354     public void loadLibrary(String libName) {
355         loadLibrary(libName, VMStack.getCallingClassLoader());
356     }
357 
358     /*
359      * Loads and links a library without security checks.
360      */
loadLibrary(String libraryName, ClassLoader loader)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 
nativeExit(int code)396     private static native void nativeExit(int code);
397 
nativeLoad(String filename, ClassLoader loader)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      */
runFinalization()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
runFinalizersOnExit(boolean run)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      */
totalMemory()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      */
traceInstructions(boolean enable)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      */
traceMethodCalls(boolean enable)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
getLocalizedInputStream(InputStream stream)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
getLocalizedOutputStream(OutputStream stream)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      */
addShutdownHook(Thread hook)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      */
removeShutdownHook(Thread hook)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      */
halt(int code)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      */
availableProcessors()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      */
maxMemory()615     public native long maxMemory();
616 }
617