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