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