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