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