Home | History | Annotate | Download | only in lang
      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 
     33 package java.lang;
     34 
     35 import dalvik.system.BaseDexClassLoader;
     36 import dalvik.system.VMDebug;
     37 import dalvik.system.VMRuntime;
     38 import dalvik.system.VMStack;
     39 import java.io.File;
     40 import java.io.IOException;
     41 import java.io.InputStream;
     42 import java.io.InputStreamReader;
     43 import java.io.OutputStream;
     44 import java.io.OutputStreamWriter;
     45 import java.lang.ref.FinalizerReference;
     46 import java.util.ArrayList;
     47 import java.util.List;
     48 import java.util.StringTokenizer;
     49 import libcore.io.IoUtils;
     50 import libcore.io.Libcore;
     51 import libcore.util.EmptyArray;
     52 import static android.system.OsConstants._SC_NPROCESSORS_CONF;
     53 
     54 /**
     55  * Allows Java applications to interface with the environment in which they are
     56  * running. Applications can not create an instance of this class, but they can
     57  * get a singleton instance by invoking {@link #getRuntime()}.
     58  *
     59  * @see System
     60  */
     61 public class Runtime {
     62 
     63     /**
     64      * Holds the Singleton global instance of Runtime.
     65      */
     66     private static final Runtime mRuntime = new Runtime();
     67 
     68     /**
     69      * Holds the library paths, used for native library lookup.
     70      */
     71     private final String[] mLibPaths = initLibPaths();
     72 
     73     private static String[] initLibPaths() {
     74         String javaLibraryPath = System.getProperty("java.library.path");
     75         if (javaLibraryPath == null) {
     76             return EmptyArray.STRING;
     77         }
     78         String[] paths = javaLibraryPath.split(":");
     79         // Add a '/' to the end of each directory so we don't have to do it every time.
     80         for (int i = 0; i < paths.length; ++i) {
     81             if (!paths[i].endsWith("/")) {
     82                 paths[i] += "/";
     83             }
     84         }
     85         return paths;
     86     }
     87 
     88     /**
     89      * Holds the list of threads to run when the VM terminates
     90      */
     91     private List<Thread> shutdownHooks = new ArrayList<Thread>();
     92 
     93     /**
     94      * Reflects whether finalization should be run for all objects
     95      * when the VM terminates.
     96      */
     97     private static boolean finalizeOnExit;
     98 
     99     /**
    100      * Reflects whether we are already shutting down the VM.
    101      */
    102     private boolean shuttingDown;
    103 
    104     /**
    105      * Reflects whether we are tracing method calls.
    106      */
    107     private boolean tracingMethods;
    108 
    109     /**
    110      * Prevent this class from being instantiated.
    111      */
    112     private Runtime() {
    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("prog == null");
    234         } else if (prog.isEmpty()) {
    235             throw new IllegalArgumentException("prog is empty");
    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.
    252      * If {@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      * Use 0 to signal success to the calling process and 1 to signal failure.
    256      * This method is unlikely to be useful to an Android application.
    257      */
    258     public void exit(int code) {
    259         // Make sure we don't try this several times
    260         synchronized(this) {
    261             if (!shuttingDown) {
    262                 shuttingDown = true;
    263 
    264                 Thread[] hooks;
    265                 synchronized (shutdownHooks) {
    266                     // create a copy of the hooks
    267                     hooks = new Thread[shutdownHooks.size()];
    268                     shutdownHooks.toArray(hooks);
    269                 }
    270 
    271                 // Start all shutdown hooks concurrently
    272                 for (Thread hook : hooks) {
    273                     hook.start();
    274                 }
    275 
    276                 // Wait for all shutdown hooks to finish
    277                 for (Thread hook : hooks) {
    278                     try {
    279                         hook.join();
    280                     } catch (InterruptedException ex) {
    281                         // Ignore, since we are at VM shutdown.
    282                     }
    283                 }
    284 
    285                 // Ensure finalization on exit, if requested
    286                 if (finalizeOnExit) {
    287                     runFinalization();
    288                 }
    289 
    290                 // Get out of here finally...
    291                 nativeExit(code);
    292             }
    293         }
    294     }
    295 
    296     /**
    297      * Indicates to the VM that it would be a good time to run the
    298      * garbage collector. Note that this is a hint only. There is no guarantee
    299      * that the garbage collector will actually be run.
    300      */
    301     public native void gc();
    302 
    303     /**
    304      * Returns the single {@code Runtime} instance for the current application.
    305      */
    306     public static Runtime getRuntime() {
    307         return mRuntime;
    308     }
    309 
    310     /**
    311      * Loads the shared library found at the given absolute path.
    312      * This should be of the form {@code /path/to/library/libMyLibrary.so}.
    313      * Most callers should use {@link #loadLibrary(String)} instead, and
    314      * let the system find the correct file to load.
    315      *
    316      * @throws UnsatisfiedLinkError if the library can not be loaded,
    317      * either because it's not found or because there is something wrong with it.
    318      */
    319     public void load(String absolutePath) {
    320         load(absolutePath, VMStack.getCallingClassLoader());
    321     }
    322 
    323     /*
    324      * Loads the given shared library using the given ClassLoader.
    325      */
    326     void load(String absolutePath, ClassLoader loader) {
    327         if (absolutePath == null) {
    328             throw new NullPointerException("absolutePath == null");
    329         }
    330         String error = doLoad(absolutePath, loader);
    331         if (error != null) {
    332             throw new UnsatisfiedLinkError(error);
    333         }
    334     }
    335 
    336     /**
    337      * Loads a shared library. Class loaders have some influence over this
    338      * process, but for a typical Android app, it works as follows:
    339      *
    340      * <p>Given the name {@code "MyLibrary"}, that string will be passed to
    341      * {@link System#mapLibraryName}. That means it would be a mistake
    342      * for the caller to include the usual {@code "lib"} prefix and {@code ".so"}
    343      * suffix.
    344      *
    345      * <p>That file will then be searched for on the application's native library
    346      * search path. This consists of the application's own native library directory
    347      * followed by the system's native library directories.
    348      *
    349      * @throws UnsatisfiedLinkError if the library can not be loaded,
    350      * either because it's not found or because there is something wrong with it.
    351      */
    352     public void loadLibrary(String nickname) {
    353         loadLibrary(nickname, VMStack.getCallingClassLoader());
    354     }
    355 
    356     /*
    357      * Searches for and loads the given shared library using the given ClassLoader.
    358      */
    359     void loadLibrary(String libraryName, ClassLoader loader) {
    360         if (loader != null) {
    361             String filename = loader.findLibrary(libraryName);
    362             if (filename == null) {
    363                 // It's not necessarily true that the ClassLoader used
    364                 // System.mapLibraryName, but the default setup does, and it's
    365                 // misleading to say we didn't find "libMyLibrary.so" when we
    366                 // actually searched for "liblibMyLibrary.so.so".
    367                 throw new UnsatisfiedLinkError(loader + " couldn't find \"" +
    368                                                System.mapLibraryName(libraryName) + "\"");
    369             }
    370             String error = doLoad(filename, loader);
    371             if (error != null) {
    372                 throw new UnsatisfiedLinkError(error);
    373             }
    374             return;
    375         }
    376 
    377         String filename = System.mapLibraryName(libraryName);
    378         List<String> candidates = new ArrayList<String>();
    379         String lastError = null;
    380         for (String directory : mLibPaths) {
    381             String candidate = directory + filename;
    382             candidates.add(candidate);
    383 
    384             if (IoUtils.canOpenReadOnly(candidate)) {
    385                 String error = doLoad(candidate, loader);
    386                 if (error == null) {
    387                     return; // We successfully loaded the library. Job done.
    388                 }
    389                 lastError = error;
    390             }
    391         }
    392 
    393         if (lastError != null) {
    394             throw new UnsatisfiedLinkError(lastError);
    395         }
    396         throw new UnsatisfiedLinkError("Library " + libraryName + " not found; tried " + candidates);
    397     }
    398 
    399     private static native void nativeExit(int code);
    400 
    401     private String doLoad(String name, ClassLoader loader) {
    402         // Android apps are forked from the zygote, so they can't have a custom LD_LIBRARY_PATH,
    403         // which means that by default an app's shared library directory isn't on LD_LIBRARY_PATH.
    404 
    405         // The PathClassLoader set up by frameworks/base knows the appropriate path, so we can load
    406         // libraries with no dependencies just fine, but an app that has multiple libraries that
    407         // depend on each other needed to load them in most-dependent-first order.
    408 
    409         // We added API to Android's dynamic linker so we can update the library path used for
    410         // the currently-running process. We pull the desired path out of the ClassLoader here
    411         // and pass it to nativeLoad so that it can call the private dynamic linker API.
    412 
    413         // We didn't just change frameworks/base to update the LD_LIBRARY_PATH once at the
    414         // beginning because multiple apks can run in the same process and third party code can
    415         // use its own BaseDexClassLoader.
    416 
    417         // We didn't just add a dlopen_with_custom_LD_LIBRARY_PATH call because we wanted any
    418         // dlopen(3) calls made from a .so's JNI_OnLoad to work too.
    419 
    420         // So, find out what the native library search path is for the ClassLoader in question...
    421         String ldLibraryPath = null;
    422         String dexPath = null;
    423         if (loader == null) {
    424             // We use the given library path for the boot class loader. This is the path
    425             // also used in loadLibraryName if loader is null.
    426             ldLibraryPath = System.getProperty("java.library.path");
    427         } else if (loader instanceof BaseDexClassLoader) {
    428             BaseDexClassLoader dexClassLoader = (BaseDexClassLoader) loader;
    429             ldLibraryPath = dexClassLoader.getLdLibraryPath();
    430         }
    431         // nativeLoad should be synchronized so there's only one LD_LIBRARY_PATH in use regardless
    432         // of how many ClassLoaders are in the system, but dalvik doesn't support synchronized
    433         // internal natives.
    434         synchronized (this) {
    435             return nativeLoad(name, loader, ldLibraryPath);
    436         }
    437     }
    438 
    439     // TODO: should be synchronized, but dalvik doesn't support synchronized internal natives.
    440     private static native String nativeLoad(String filename, ClassLoader loader,
    441             String ldLibraryPath);
    442 
    443     /**
    444      * Provides a hint to the runtime that it would be useful to attempt
    445      * to perform any outstanding object finalization.
    446      */
    447     public void runFinalization() {
    448         // 0 for no timeout.
    449         VMRuntime.runFinalization(0);
    450     }
    451 
    452     /**
    453      * Sets the flag that indicates whether all objects are finalized when the
    454      * runtime is about to exit. Note that all finalization which occurs
    455      * when the system is exiting is performed after all running threads have
    456      * been terminated.
    457      *
    458      * @param run
    459      *            {@code true} to enable finalization on exit, {@code false} to
    460      *            disable it.
    461      * @deprecated This method is unsafe.
    462      */
    463     @Deprecated
    464     public static void runFinalizersOnExit(boolean run) {
    465         finalizeOnExit = run;
    466     }
    467 
    468     /**
    469      * Switches the output of debug information for instructions on or off.
    470      * On Android, this method does nothing.
    471      */
    472     public void traceInstructions(boolean enable) {
    473     }
    474 
    475     /**
    476      * Switches the output of debug information for methods on or off.
    477      */
    478     public void traceMethodCalls(boolean enable) {
    479         if (enable != tracingMethods) {
    480             if (enable) {
    481                 VMDebug.startMethodTracing();
    482             } else {
    483                 VMDebug.stopMethodTracing();
    484             }
    485             tracingMethods = enable;
    486         }
    487     }
    488 
    489     /**
    490      * Returns the localized version of the specified input stream. The input
    491      * stream that is returned automatically converts all characters from the
    492      * local character set to Unicode after reading them from the underlying
    493      * stream.
    494      *
    495      * @param stream
    496      *            the input stream to localize.
    497      * @return the localized input stream.
    498      * @deprecated Use {@link InputStreamReader} instead.
    499      */
    500     @Deprecated
    501     public InputStream getLocalizedInputStream(InputStream stream) {
    502         String encoding = System.getProperty("file.encoding", "UTF-8");
    503         if (!encoding.equals("UTF-8")) {
    504             throw new UnsupportedOperationException("Cannot localize " + encoding);
    505         }
    506         return stream;
    507     }
    508 
    509     /**
    510      * Returns the localized version of the specified output stream. The output
    511      * stream that is returned automatically converts all characters from
    512      * Unicode to the local character set before writing them to the underlying
    513      * stream.
    514      *
    515      * @param stream
    516      *            the output stream to localize.
    517      * @return the localized output stream.
    518      * @deprecated Use {@link OutputStreamWriter} instead.
    519      */
    520     @Deprecated
    521     public OutputStream getLocalizedOutputStream(OutputStream stream) {
    522         String encoding = System.getProperty("file.encoding", "UTF-8");
    523         if (!encoding.equals("UTF-8")) {
    524             throw new UnsupportedOperationException("Cannot localize " + encoding);
    525         }
    526         return stream;
    527     }
    528 
    529     /**
    530      * Registers a VM shutdown hook. A shutdown hook is a
    531      * {@code Thread} that is ready to run, but has not yet been started. All
    532      * registered shutdown hooks will be executed when the VM
    533      * terminates normally (typically when the {@link #exit(int)} method is called).
    534      *
    535      * <p><i>Note that on Android, the application lifecycle does not include VM termination,
    536      * so calling this method will not ensure that your code is run</i>. Instead, you should
    537      * use the most appropriate lifecycle notification ({@code Activity.onPause}, say).
    538      *
    539      * <p>Shutdown hooks are run concurrently and in an unspecified order. Hooks
    540      * failing due to an unhandled exception are not a problem, but the stack
    541      * trace might be printed to the console. Once initiated, the whole shutdown
    542      * process can only be terminated by calling {@code halt()}.
    543      *
    544      * <p>If {@link #runFinalizersOnExit(boolean)} has been called with a {@code
    545      * true} argument, garbage collection and finalization will take place after
    546      * all hooks are either finished or have failed. Then the VM
    547      * terminates.
    548      *
    549      * <p>It is recommended that shutdown hooks do not do any time-consuming
    550      * activities, in order to not hold up the shutdown process longer than
    551      * necessary.
    552      *
    553      * @param hook
    554      *            the shutdown hook to register.
    555      * @throws IllegalArgumentException
    556      *             if the hook has already been started or if it has already
    557      *             been registered.
    558      * @throws IllegalStateException
    559      *             if the VM is already shutting down.
    560      */
    561     public void addShutdownHook(Thread hook) {
    562         // Sanity checks
    563         if (hook == null) {
    564             throw new NullPointerException("hook == null");
    565         }
    566 
    567         if (shuttingDown) {
    568             throw new IllegalStateException("VM already shutting down");
    569         }
    570 
    571         if (hook.hasBeenStarted) {
    572             throw new IllegalArgumentException("Hook has already been started");
    573         }
    574 
    575         synchronized (shutdownHooks) {
    576             if (shutdownHooks.contains(hook)) {
    577                 throw new IllegalArgumentException("Hook already registered.");
    578             }
    579 
    580             shutdownHooks.add(hook);
    581         }
    582     }
    583 
    584     /**
    585      * Unregisters a previously registered VM shutdown hook.
    586      *
    587      * @param hook
    588      *            the shutdown hook to remove.
    589      * @return {@code true} if the hook has been removed successfully; {@code
    590      *         false} otherwise.
    591      * @throws IllegalStateException
    592      *             if the VM is already shutting down.
    593      */
    594     public boolean removeShutdownHook(Thread hook) {
    595         // Sanity checks
    596         if (hook == null) {
    597             throw new NullPointerException("hook == null");
    598         }
    599 
    600         if (shuttingDown) {
    601             throw new IllegalStateException("VM already shutting down");
    602         }
    603 
    604         synchronized (shutdownHooks) {
    605             return shutdownHooks.remove(hook);
    606         }
    607     }
    608 
    609     /**
    610      * Causes the VM to stop running, and the program to exit with the given return code.
    611      * Use 0 to signal success to the calling process and 1 to signal failure.
    612      * Neither shutdown hooks nor finalizers are run before exiting.
    613      * This method is unlikely to be useful to an Android application.
    614      */
    615     public void halt(int code) {
    616         // Get out of here...
    617         nativeExit(code);
    618     }
    619 
    620     /**
    621      * Returns the number of processor cores available to the VM, at least 1.
    622      * Traditionally this returned the number currently online,
    623      * but many mobile devices are able to take unused cores offline to
    624      * save power, so releases newer than Android 4.2 (Jelly Bean) return the maximum number of
    625      * cores that could be made available if there were no power or heat
    626      * constraints.
    627      */
    628     public int availableProcessors() {
    629         return (int) Libcore.os.sysconf(_SC_NPROCESSORS_CONF);
    630     }
    631 
    632     /**
    633      * Returns the number of bytes currently available on the heap without expanding the heap. See
    634      * {@link #totalMemory} for the heap's current size. When these bytes are exhausted, the heap
    635      * may expand. See {@link #maxMemory} for that limit.
    636      */
    637     public native long freeMemory();
    638 
    639     /**
    640      * Returns the number of bytes taken by the heap at its current size. The heap may expand or
    641      * contract over time, as the number of live objects increases or decreases. See
    642      * {@link #maxMemory} for the maximum heap size, and {@link #freeMemory} for an idea of how much
    643      * the heap could currently contract.
    644      */
    645     public native long totalMemory();
    646 
    647     /**
    648      * Returns the maximum number of bytes the heap can expand to. See {@link #totalMemory} for the
    649      * current number of bytes taken by the heap, and {@link #freeMemory} for the current number of
    650      * those bytes actually used by live objects.
    651      */
    652     public native long maxMemory();
    653 }
    654