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