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.BaseDexClassLoader;
36import dalvik.system.VMDebug;
37import dalvik.system.VMStack;
38import java.io.File;
39import java.io.IOException;
40import java.io.InputStream;
41import java.io.InputStreamReader;
42import java.io.OutputStream;
43import java.io.OutputStreamWriter;
44import java.lang.ref.FinalizerReference;
45import java.util.ArrayList;
46import java.util.List;
47import java.util.StringTokenizer;
48import libcore.io.IoUtils;
49import libcore.io.Libcore;
50import libcore.util.EmptyArray;
51import 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 */
60public 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
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
300    public native void gc();
301
302    /**
303     * Returns the single {@code Runtime} instance for the current application.
304     */
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     */
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     */
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     */
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     */
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
398    private static native void nativeExit(int code);
399
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.
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     */
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
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     */
467    public void traceInstructions(boolean enable) {
468    }
469
470    /**
471     * Switches the output of debug information for methods on or off.
472     */
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
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
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     */
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     */
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     */
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     */
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     */
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     */
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     */
647    public native long maxMemory();
648}
649