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