| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| /* |
| * Copyright (C) 2008 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package java.lang; |
| |
| import dalvik.system.BaseDexClassLoader; |
| import dalvik.system.VMDebug; |
| import dalvik.system.VMStack; |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.OutputStream; |
| import java.io.OutputStreamWriter; |
| import java.lang.ref.FinalizerReference; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.StringTokenizer; |
| import libcore.io.IoUtils; |
| import libcore.io.Libcore; |
| import libcore.util.EmptyArray; |
| import static android.system.OsConstants._SC_NPROCESSORS_CONF; |
| |
| /** |
| * Allows Java applications to interface with the environment in which they are |
| * running. Applications can not create an instance of this class, but they can |
| * get a singleton instance by invoking {@link #getRuntime()}. |
| * |
| * @see System |
| */ |
| public class Runtime { |
| |
| /** |
| * Holds the Singleton global instance of Runtime. |
| */ |
| private static final Runtime mRuntime = new Runtime(); |
| |
| /** |
| * Holds the library paths, used for native library lookup. |
| */ |
| private final String[] mLibPaths = initLibPaths(); |
| |
| private static String[] initLibPaths() { |
| String javaLibraryPath = System.getProperty("java.library.path"); |
| if (javaLibraryPath == null) { |
| return EmptyArray.STRING; |
| } |
| String[] paths = javaLibraryPath.split(":"); |
| // Add a '/' to the end of each directory so we don't have to do it every time. |
| for (int i = 0; i < paths.length; ++i) { |
| if (!paths[i].endsWith("/")) { |
| paths[i] += "/"; |
| } |
| } |
| return paths; |
| } |
| |
| /** |
| * Holds the list of threads to run when the VM terminates |
| */ |
| private List<Thread> shutdownHooks = new ArrayList<Thread>(); |
| |
| /** |
| * Reflects whether finalization should be run for all objects |
| * when the VM terminates. |
| */ |
| private static boolean finalizeOnExit; |
| |
| /** |
| * Reflects whether we are already shutting down the VM. |
| */ |
| private boolean shuttingDown; |
| |
| /** |
| * Reflects whether we are tracing method calls. |
| */ |
| private boolean tracingMethods; |
| |
| /** |
| * Prevent this class from being instantiated. |
| */ |
| private Runtime() { |
| } |
| |
| /** |
| * Executes the specified command and its arguments in a separate native |
| * process. The new process inherits the environment of the caller. Calling |
| * this method is equivalent to calling {@code exec(progArray, null, null)}. |
| * |
| * @param progArray |
| * the array containing the program to execute as well as any |
| * arguments to the program. |
| * @return the new {@code Process} object that represents the native |
| * process. |
| * @throws IOException |
| * if the requested program can not be executed. |
| */ |
| public Process exec(String[] progArray) throws java.io.IOException { |
| return exec(progArray, null, null); |
| } |
| |
| /** |
| * Executes the specified command and its arguments in a separate native |
| * process. The new process uses the environment provided in {@code envp}. |
| * Calling this method is equivalent to calling |
| * {@code exec(progArray, envp, null)}. |
| * |
| * @param progArray |
| * the array containing the program to execute as well as any |
| * arguments to the program. |
| * @param envp |
| * the array containing the environment to start the new process |
| * in. |
| * @return the new {@code Process} object that represents the native |
| * process. |
| * @throws IOException |
| * if the requested program can not be executed. |
| */ |
| public Process exec(String[] progArray, String[] envp) throws java.io.IOException { |
| return exec(progArray, envp, null); |
| } |
| |
| /** |
| * Executes the specified command and its arguments in a separate native |
| * process. The new process uses the environment provided in {@code envp} |
| * and the working directory specified by {@code directory}. |
| * |
| * @param progArray |
| * the array containing the program to execute as well as any |
| * arguments to the program. |
| * @param envp |
| * the array containing the environment to start the new process |
| * in. |
| * @param directory |
| * the directory in which to execute the program. If {@code null}, |
| * execute if in the same directory as the parent process. |
| * @return the new {@code Process} object that represents the native |
| * process. |
| * @throws IOException |
| * if the requested program can not be executed. |
| */ |
| public Process exec(String[] progArray, String[] envp, File directory) throws IOException { |
| // ProcessManager is responsible for all argument checking. |
| return ProcessManager.getInstance().exec(progArray, envp, directory, false); |
| } |
| |
| /** |
| * Executes the specified program in a separate native process. The new |
| * process inherits the environment of the caller. Calling this method is |
| * equivalent to calling {@code exec(prog, null, null)}. |
| * |
| * @param prog |
| * the name of the program to execute. |
| * @return the new {@code Process} object that represents the native |
| * process. |
| * @throws IOException |
| * if the requested program can not be executed. |
| */ |
| public Process exec(String prog) throws java.io.IOException { |
| return exec(prog, null, null); |
| } |
| |
| /** |
| * Executes the specified program in a separate native process. The new |
| * process uses the environment provided in {@code envp}. Calling this |
| * method is equivalent to calling {@code exec(prog, envp, null)}. |
| * |
| * @param prog |
| * the name of the program to execute. |
| * @param envp |
| * the array containing the environment to start the new process |
| * in. |
| * @return the new {@code Process} object that represents the native |
| * process. |
| * @throws IOException |
| * if the requested program can not be executed. |
| */ |
| public Process exec(String prog, String[] envp) throws java.io.IOException { |
| return exec(prog, envp, null); |
| } |
| |
| /** |
| * Executes the specified program in a separate native process. The new |
| * process uses the environment provided in {@code envp} and the working |
| * directory specified by {@code directory}. |
| * |
| * @param prog |
| * the name of the program to execute. |
| * @param envp |
| * the array containing the environment to start the new process |
| * in. |
| * @param directory |
| * the directory in which to execute the program. If {@code null}, |
| * execute if in the same directory as the parent process. |
| * @return the new {@code Process} object that represents the native |
| * process. |
| * @throws IOException |
| * if the requested program can not be executed. |
| */ |
| public Process exec(String prog, String[] envp, File directory) throws java.io.IOException { |
| // Sanity checks |
| if (prog == null) { |
| throw new NullPointerException("prog == null"); |
| } else if (prog.isEmpty()) { |
| throw new IllegalArgumentException("prog is empty"); |
| } |
| |
| // Break down into tokens, as described in Java docs |
| StringTokenizer tokenizer = new StringTokenizer(prog); |
| int length = tokenizer.countTokens(); |
| String[] progArray = new String[length]; |
| for (int i = 0; i < length; i++) { |
| progArray[i] = tokenizer.nextToken(); |
| } |
| |
| // Delegate |
| return exec(progArray, envp, directory); |
| } |
| |
| /** |
| * Causes the VM to stop running and the program to exit. |
| * If {@link #runFinalizersOnExit(boolean)} has been previously invoked with a |
| * {@code true} argument, then all objects will be properly |
| * garbage-collected and finalized first. |
| * Use 0 to signal success to the calling process and 1 to signal failure. |
| * This method is unlikely to be useful to an Android application. |
| */ |
| public void exit(int code) { |
| // Make sure we don't try this several times |
| synchronized(this) { |
| if (!shuttingDown) { |
| shuttingDown = true; |
| |
| Thread[] hooks; |
| synchronized (shutdownHooks) { |
| // create a copy of the hooks |
| hooks = new Thread[shutdownHooks.size()]; |
| shutdownHooks.toArray(hooks); |
| } |
| |
| // Start all shutdown hooks concurrently |
| for (Thread hook : hooks) { |
| hook.start(); |
| } |
| |
| // Wait for all shutdown hooks to finish |
| for (Thread hook : hooks) { |
| try { |
| hook.join(); |
| } catch (InterruptedException ex) { |
| // Ignore, since we are at VM shutdown. |
| } |
| } |
| |
| // Ensure finalization on exit, if requested |
| if (finalizeOnExit) { |
| runFinalization(); |
| } |
| |
| // Get out of here finally... |
| nativeExit(code); |
| } |
| } |
| } |
| |
| /** |
| * Indicates to the VM that it would be a good time to run the |
| * garbage collector. Note that this is a hint only. There is no guarantee |
| * that the garbage collector will actually be run. |
| */ |
| public native void gc(); |
| |
| /** |
| * Returns the single {@code Runtime} instance for the current application. |
| */ |
| public static Runtime getRuntime() { |
| return mRuntime; |
| } |
| |
| /** |
| * Loads the shared library found at the given absolute path. |
| * This should be of the form {@code /path/to/library/libMyLibrary.so}. |
| * Most callers should use {@link #loadLibrary(String)} instead, and |
| * let the system find the correct file to load. |
| * |
| * @throws UnsatisfiedLinkError if the library can not be loaded, |
| * either because it's not found or because there is something wrong with it. |
| */ |
| public void load(String absolutePath) { |
| load(absolutePath, VMStack.getCallingClassLoader()); |
| } |
| |
| /* |
| * Loads the given shared library using the given ClassLoader. |
| */ |
| void load(String absolutePath, ClassLoader loader) { |
| if (absolutePath == null) { |
| throw new NullPointerException("absolutePath == null"); |
| } |
| String error = doLoad(absolutePath, loader); |
| if (error != null) { |
| throw new UnsatisfiedLinkError(error); |
| } |
| } |
| |
| /** |
| * Loads a shared library. Class loaders have some influence over this |
| * process, but for a typical Android app, it works as follows: |
| * |
| * <p>Given the name {@code "MyLibrary"}, that string will be passed to |
| * {@link System#mapLibraryName}. That means it would be a mistake |
| * for the caller to include the usual {@code "lib"} prefix and {@code ".so"} |
| * suffix. |
| * |
| * <p>That file will then be searched for on the application's native library |
| * search path. This consists of the application's own native library directory |
| * followed by the system's native library directories. |
| * |
| * @throws UnsatisfiedLinkError if the library can not be loaded, |
| * either because it's not found or because there is something wrong with it. |
| */ |
| public void loadLibrary(String nickname) { |
| loadLibrary(nickname, VMStack.getCallingClassLoader()); |
| } |
| |
| /* |
| * Searches for and loads the given shared library using the given ClassLoader. |
| */ |
| void loadLibrary(String libraryName, ClassLoader loader) { |
| if (loader != null) { |
| String filename = loader.findLibrary(libraryName); |
| if (filename == null) { |
| // It's not necessarily true that the ClassLoader used |
| // System.mapLibraryName, but the default setup does, and it's |
| // misleading to say we didn't find "libMyLibrary.so" when we |
| // actually searched for "liblibMyLibrary.so.so". |
| throw new UnsatisfiedLinkError(loader + " couldn't find \"" + |
| System.mapLibraryName(libraryName) + "\""); |
| } |
| String error = doLoad(filename, loader); |
| if (error != null) { |
| throw new UnsatisfiedLinkError(error); |
| } |
| return; |
| } |
| |
| String filename = System.mapLibraryName(libraryName); |
| List<String> candidates = new ArrayList<String>(); |
| String lastError = null; |
| for (String directory : mLibPaths) { |
| String candidate = directory + filename; |
| candidates.add(candidate); |
| |
| if (IoUtils.canOpenReadOnly(candidate)) { |
| String error = doLoad(candidate, loader); |
| if (error == null) { |
| return; // We successfully loaded the library. Job done. |
| } |
| lastError = error; |
| } |
| } |
| |
| if (lastError != null) { |
| throw new UnsatisfiedLinkError(lastError); |
| } |
| throw new UnsatisfiedLinkError("Library " + libraryName + " not found; tried " + candidates); |
| } |
| |
| private static native void nativeExit(int code); |
| |
| private String doLoad(String name, ClassLoader loader) { |
| // Android apps are forked from the zygote, so they can't have a custom LD_LIBRARY_PATH, |
| // which means that by default an app's shared library directory isn't on LD_LIBRARY_PATH. |
| |
| // The PathClassLoader set up by frameworks/base knows the appropriate path, so we can load |
| // libraries with no dependencies just fine, but an app that has multiple libraries that |
| // depend on each other needed to load them in most-dependent-first order. |
| |
| // We added API to Android's dynamic linker so we can update the library path used for |
| // the currently-running process. We pull the desired path out of the ClassLoader here |
| // and pass it to nativeLoad so that it can call the private dynamic linker API. |
| |
| // We didn't just change frameworks/base to update the LD_LIBRARY_PATH once at the |
| // beginning because multiple apks can run in the same process and third party code can |
| // use its own BaseDexClassLoader. |
| |
| // We didn't just add a dlopen_with_custom_LD_LIBRARY_PATH call because we wanted any |
| // dlopen(3) calls made from a .so's JNI_OnLoad to work too. |
| |
| // So, find out what the native library search path is for the ClassLoader in question... |
| String ldLibraryPath = null; |
| if (loader != null && loader instanceof BaseDexClassLoader) { |
| ldLibraryPath = ((BaseDexClassLoader) loader).getLdLibraryPath(); |
| } |
| // nativeLoad should be synchronized so there's only one LD_LIBRARY_PATH in use regardless |
| // of how many ClassLoaders are in the system, but dalvik doesn't support synchronized |
| // internal natives. |
| synchronized (this) { |
| return nativeLoad(name, loader, ldLibraryPath); |
| } |
| } |
| |
| // TODO: should be synchronized, but dalvik doesn't support synchronized internal natives. |
| private static native String nativeLoad(String filename, ClassLoader loader, String ldLibraryPath); |
| |
| /** |
| * Provides a hint to the VM that it would be useful to attempt |
| * to perform any outstanding object finalization. |
| */ |
| public void runFinalization() { |
| try { |
| FinalizerReference.finalizeAllEnqueued(); |
| } catch (InterruptedException e) { |
| Thread.currentThread().interrupt(); |
| } |
| } |
| |
| /** |
| * Sets the flag that indicates whether all objects are finalized when the |
| * VM is about to exit. Note that all finalization which occurs |
| * when the system is exiting is performed after all running threads have |
| * been terminated. |
| * |
| * @param run |
| * {@code true} to enable finalization on exit, {@code false} to |
| * disable it. |
| * @deprecated This method is unsafe. |
| */ |
| @Deprecated |
| public static void runFinalizersOnExit(boolean run) { |
| finalizeOnExit = run; |
| } |
| |
| /** |
| * Switches the output of debug information for instructions on or off. |
| * On Android, this method does nothing. |
| */ |
| public void traceInstructions(boolean enable) { |
| } |
| |
| /** |
| * Switches the output of debug information for methods on or off. |
| */ |
| public void traceMethodCalls(boolean enable) { |
| if (enable != tracingMethods) { |
| if (enable) { |
| VMDebug.startMethodTracing(); |
| } else { |
| VMDebug.stopMethodTracing(); |
| } |
| tracingMethods = enable; |
| } |
| } |
| |
| /** |
| * Returns the localized version of the specified input stream. The input |
| * stream that is returned automatically converts all characters from the |
| * local character set to Unicode after reading them from the underlying |
| * stream. |
| * |
| * @param stream |
| * the input stream to localize. |
| * @return the localized input stream. |
| * @deprecated Use {@link InputStreamReader} instead. |
| */ |
| @Deprecated |
| public InputStream getLocalizedInputStream(InputStream stream) { |
| String encoding = System.getProperty("file.encoding", "UTF-8"); |
| if (!encoding.equals("UTF-8")) { |
| throw new UnsupportedOperationException("Cannot localize " + encoding); |
| } |
| return stream; |
| } |
| |
| /** |
| * Returns the localized version of the specified output stream. The output |
| * stream that is returned automatically converts all characters from |
| * Unicode to the local character set before writing them to the underlying |
| * stream. |
| * |
| * @param stream |
| * the output stream to localize. |
| * @return the localized output stream. |
| * @deprecated Use {@link OutputStreamWriter} instead. |
| */ |
| @Deprecated |
| public OutputStream getLocalizedOutputStream(OutputStream stream) { |
| String encoding = System.getProperty("file.encoding", "UTF-8"); |
| if (!encoding.equals("UTF-8")) { |
| throw new UnsupportedOperationException("Cannot localize " + encoding); |
| } |
| return stream; |
| } |
| |
| /** |
| * Registers a VM shutdown hook. A shutdown hook is a |
| * {@code Thread} that is ready to run, but has not yet been started. All |
| * registered shutdown hooks will be executed when the VM |
| * terminates normally (typically when the {@link #exit(int)} method is called). |
| * |
| * <p><i>Note that on Android, the application lifecycle does not include VM termination, |
| * so calling this method will not ensure that your code is run</i>. Instead, you should |
| * use the most appropriate lifecycle notification ({@code Activity.onPause}, say). |
| * |
| * <p>Shutdown hooks are run concurrently and in an unspecified order. Hooks |
| * failing due to an unhandled exception are not a problem, but the stack |
| * trace might be printed to the console. Once initiated, the whole shutdown |
| * process can only be terminated by calling {@code halt()}. |
| * |
| * <p>If {@link #runFinalizersOnExit(boolean)} has been called with a {@code |
| * true} argument, garbage collection and finalization will take place after |
| * all hooks are either finished or have failed. Then the VM |
| * terminates. |
| * |
| * <p>It is recommended that shutdown hooks do not do any time-consuming |
| * activities, in order to not hold up the shutdown process longer than |
| * necessary. |
| * |
| * @param hook |
| * the shutdown hook to register. |
| * @throws IllegalArgumentException |
| * if the hook has already been started or if it has already |
| * been registered. |
| * @throws IllegalStateException |
| * if the VM is already shutting down. |
| */ |
| public void addShutdownHook(Thread hook) { |
| // Sanity checks |
| if (hook == null) { |
| throw new NullPointerException("hook == null"); |
| } |
| |
| if (shuttingDown) { |
| throw new IllegalStateException("VM already shutting down"); |
| } |
| |
| if (hook.hasBeenStarted) { |
| throw new IllegalArgumentException("Hook has already been started"); |
| } |
| |
| synchronized (shutdownHooks) { |
| if (shutdownHooks.contains(hook)) { |
| throw new IllegalArgumentException("Hook already registered."); |
| } |
| |
| shutdownHooks.add(hook); |
| } |
| } |
| |
| /** |
| * Unregisters a previously registered VM shutdown hook. |
| * |
| * @param hook |
| * the shutdown hook to remove. |
| * @return {@code true} if the hook has been removed successfully; {@code |
| * false} otherwise. |
| * @throws IllegalStateException |
| * if the VM is already shutting down. |
| */ |
| public boolean removeShutdownHook(Thread hook) { |
| // Sanity checks |
| if (hook == null) { |
| throw new NullPointerException("hook == null"); |
| } |
| |
| if (shuttingDown) { |
| throw new IllegalStateException("VM already shutting down"); |
| } |
| |
| synchronized (shutdownHooks) { |
| return shutdownHooks.remove(hook); |
| } |
| } |
| |
| /** |
| * Causes the VM to stop running, and the program to exit with the given return code. |
| * Use 0 to signal success to the calling process and 1 to signal failure. |
| * Neither shutdown hooks nor finalizers are run before exiting. |
| * This method is unlikely to be useful to an Android application. |
| */ |
| public void halt(int code) { |
| // Get out of here... |
| nativeExit(code); |
| } |
| |
| /** |
| * Returns the number of processor cores available to the VM, at least 1. |
| * Traditionally this returned the number currently online, |
| * but many mobile devices are able to take unused cores offline to |
| * save power, so releases newer than Android 4.2 (Jelly Bean) return the maximum number of |
| * cores that could be made available if there were no power or heat |
| * constraints. |
| */ |
| public int availableProcessors() { |
| return (int) Libcore.os.sysconf(_SC_NPROCESSORS_CONF); |
| } |
| |
| /** |
| * Returns the number of bytes currently available on the heap without expanding the heap. See |
| * {@link #totalMemory} for the heap's current size. When these bytes are exhausted, the heap |
| * may expand. See {@link #maxMemory} for that limit. |
| */ |
| public native long freeMemory(); |
| |
| /** |
| * Returns the number of bytes taken by the heap at its current size. The heap may expand or |
| * contract over time, as the number of live objects increases or decreases. See |
| * {@link #maxMemory} for the maximum heap size, and {@link #freeMemory} for an idea of how much |
| * the heap could currently contract. |
| */ |
| public native long totalMemory(); |
| |
| /** |
| * Returns the maximum number of bytes the heap can expand to. See {@link #totalMemory} for the |
| * current number of bytes taken by the heap, and {@link #freeMemory} for the current number of |
| * those bytes actually used by live objects. |
| */ |
| public native long maxMemory(); |
| } |