/* * Copyright (C) 2006 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 android.os; import android.net.LocalSocketAddress; import android.net.LocalSocket; import android.util.Log; import dalvik.system.Zygote; import java.io.BufferedWriter; import java.io.DataInputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.util.ArrayList; /*package*/ class ZygoteStartFailedEx extends Exception { /** * Something prevented the zygote process startup from happening normally */ ZygoteStartFailedEx() {}; ZygoteStartFailedEx(String s) {super(s);} ZygoteStartFailedEx(Throwable cause) {super(cause);} } /** * Tools for managing OS processes. */ public class Process { private static final String LOG_TAG = "Process"; private static final String ZYGOTE_SOCKET = "zygote"; /** * Name of a process for running the platform's media services. * {@hide} */ public static final String ANDROID_SHARED_MEDIA = "com.android.process.media"; /** * Name of the process that Google content providers can share. * {@hide} */ public static final String GOOGLE_SHARED_APP_CONTENT = "com.google.process.content"; /** * Defines the UID/GID under which system code runs. */ public static final int SYSTEM_UID = 1000; /** * Defines the UID/GID under which the telephony code runs. */ public static final int PHONE_UID = 1001; /** * Defines the UID/GID for the user shell. * @hide */ public static final int SHELL_UID = 2000; /** * Defines the UID/GID for the log group. * @hide */ public static final int LOG_UID = 1007; /** * Defines the UID/GID for the WIFI supplicant process. * @hide */ public static final int WIFI_UID = 1010; /** * Defines the UID/GID for the FM supplicant process. * @hide */ public static final int FM_RADIO_UID = 1019; /** * Defines the UID/GID for the NFC service process. * @hide */ public static final int NFC_UID = 1025; /** * Defines the start of a range of UIDs (and GIDs), going from this * number to {@link #LAST_APPLICATION_UID} that are reserved for assigning * to applications. */ public static final int FIRST_APPLICATION_UID = 10000; /** * Last of application-specific UIDs starting at * {@link #FIRST_APPLICATION_UID}. */ public static final int LAST_APPLICATION_UID = 99999; /** * Defines a secondary group id for access to the bluetooth hardware. */ public static final int BLUETOOTH_GID = 2000; /** * Standard priority of application threads. * Use with {@link #setThreadPriority(int)} and * {@link #setThreadPriority(int, int)}, not with the normal * {@link java.lang.Thread} class. */ public static final int THREAD_PRIORITY_DEFAULT = 0; /* * *************************************** * ** Keep in sync with utils/threads.h ** * *************************************** */ /** * Lowest available thread priority. Only for those who really, really * don't want to run if anything else is happening. * Use with {@link #setThreadPriority(int)} and * {@link #setThreadPriority(int, int)}, not with the normal * {@link java.lang.Thread} class. */ public static final int THREAD_PRIORITY_LOWEST = 19; /** * Standard priority background threads. This gives your thread a slightly * lower than normal priority, so that it will have less chance of impacting * the responsiveness of the user interface. * Use with {@link #setThreadPriority(int)} and * {@link #setThreadPriority(int, int)}, not with the normal * {@link java.lang.Thread} class. */ public static final int THREAD_PRIORITY_BACKGROUND = 10; /** * Standard priority of threads that are currently running a user interface * that the user is interacting with. Applications can not normally * change to this priority; the system will automatically adjust your * application threads as the user moves through the UI. * Use with {@link #setThreadPriority(int)} and * {@link #setThreadPriority(int, int)}, not with the normal * {@link java.lang.Thread} class. */ public static final int THREAD_PRIORITY_FOREGROUND = -2; /** * Standard priority of system display threads, involved in updating * the user interface. Applications can not * normally change to this priority. * Use with {@link #setThreadPriority(int)} and * {@link #setThreadPriority(int, int)}, not with the normal * {@link java.lang.Thread} class. */ public static final int THREAD_PRIORITY_DISPLAY = -4; /** * Standard priority of the most important display threads, for compositing * the screen and retrieving input events. Applications can not normally * change to this priority. * Use with {@link #setThreadPriority(int)} and * {@link #setThreadPriority(int, int)}, not with the normal * {@link java.lang.Thread} class. */ public static final int THREAD_PRIORITY_URGENT_DISPLAY = -8; /** * Standard priority of audio threads. Applications can not normally * change to this priority. * Use with {@link #setThreadPriority(int)} and * {@link #setThreadPriority(int, int)}, not with the normal * {@link java.lang.Thread} class. */ public static final int THREAD_PRIORITY_AUDIO = -16; /** * Standard priority of the most important audio threads. * Applications can not normally change to this priority. * Use with {@link #setThreadPriority(int)} and * {@link #setThreadPriority(int, int)}, not with the normal * {@link java.lang.Thread} class. */ public static final int THREAD_PRIORITY_URGENT_AUDIO = -19; /** * Minimum increment to make a priority more favorable. */ public static final int THREAD_PRIORITY_MORE_FAVORABLE = -1; /** * Minimum increment to make a priority less favorable. */ public static final int THREAD_PRIORITY_LESS_FAVORABLE = +1; /** * Default thread group - gets a 'normal' share of the CPU * @hide */ public static final int THREAD_GROUP_DEFAULT = 0; /** * Background non-interactive thread group - All threads in * this group are scheduled with a reduced share of the CPU. * @hide */ public static final int THREAD_GROUP_BG_NONINTERACTIVE = 1; /** * Foreground 'boost' thread group - All threads in * this group are scheduled with an increased share of the CPU * @hide **/ public static final int THREAD_GROUP_FG_BOOST = 2; public static final int SIGNAL_QUIT = 3; public static final int SIGNAL_KILL = 9; public static final int SIGNAL_USR1 = 10; // State for communicating with zygote process static LocalSocket sZygoteSocket; static DataInputStream sZygoteInputStream; static BufferedWriter sZygoteWriter; /** true if previous zygote open failed */ static boolean sPreviousZygoteOpenFailed; /** * Start a new process. * *
If processes are enabled, a new process is created and the * static main() function of a processClass is executed there. * The process will continue running after this function returns. * *
If processes are not enabled, a new thread in the caller's * process is created and main() of processClass called there. * *
The niceName parameter, if not an empty string, is a custom name to
* give to the process instead of using processClass. This allows you to
* make easily identifyable processes even if you are using the same base
* processClass to start them.
*
* @param processClass The class to use as the process's main entry
* point.
* @param niceName A more readable name to use for the process.
* @param uid The user-id under which the process will run.
* @param gid The group-id under which the process will run.
* @param gids Additional group-ids associated with the process.
* @param enableDebugger True if debugging should be enabled for this process.
* @param zygoteArgs Additional arguments to supply to the zygote process.
*
* @return int If > 0 the pid of the new process; if 0 the process is
* being emulated by a thread
* @throws RuntimeException on fatal start failure
*
* {@hide}
*/
public static final int start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int debugFlags,
String[] zygoteArgs)
{
if (supportsProcesses()) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
debugFlags, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
throw new RuntimeException(
"Starting VM process through Zygote failed", ex);
}
} else {
// Running in single-process mode
Runnable runnable = new Runnable() {
public void run() {
Process.invokeStaticMain(processClass);
}
};
// Thread constructors must not be called with null names (see spec).
if (niceName != null) {
new Thread(runnable, niceName).start();
} else {
new Thread(runnable).start();
}
return 0;
}
}
/**
* Start a new process. Don't supply a custom nice name.
* {@hide}
*/
public static final int start(String processClass, int uid, int gid,
int[] gids, int debugFlags, String[] zygoteArgs) {
return start(processClass, "", uid, gid, gids,
debugFlags, zygoteArgs);
}
private static void invokeStaticMain(String className) {
Class cl;
Object args[] = new Object[1];
args[0] = new String[0]; //this is argv
try {
cl = Class.forName(className);
cl.getMethod("main", new Class[] { String[].class })
.invoke(null, args);
} catch (Exception ex) {
// can be: ClassNotFoundException,
// NoSuchMethodException, SecurityException,
// IllegalAccessException, IllegalArgumentException
// InvocationTargetException
// or uncaught exception from main()
Log.e(LOG_TAG, "Exception invoking static main on "
+ className, ex);
throw new RuntimeException(ex);
}
}
/** retry interval for opening a zygote socket */
static final int ZYGOTE_RETRY_MILLIS = 500;
/**
* Tries to open socket to Zygote process if not already open. If
* already open, does nothing. May block and retry.
*/
private static void openZygoteSocketIfNeeded()
throws ZygoteStartFailedEx {
int retryCount;
if (sPreviousZygoteOpenFailed) {
/*
* If we've failed before, expect that we'll fail again and
* don't pause for retries.
*/
retryCount = 0;
} else {
retryCount = 10;
}
/*
* See bug #811181: Sometimes runtime can make it up before zygote.
* Really, we'd like to do something better to avoid this condition,
* but for now just wait a bit...
*/
for (int retry = 0
; (sZygoteSocket == null) && (retry < (retryCount + 1))
; retry++ ) {
if (retry > 0) {
try {
Log.i("Zygote", "Zygote not up yet, sleeping...");
Thread.sleep(ZYGOTE_RETRY_MILLIS);
} catch (InterruptedException ex) {
// should never happen
}
}
try {
sZygoteSocket = new LocalSocket();
sZygoteSocket.connect(new LocalSocketAddress(ZYGOTE_SOCKET,
LocalSocketAddress.Namespace.RESERVED));
sZygoteInputStream
= new DataInputStream(sZygoteSocket.getInputStream());
sZygoteWriter =
new BufferedWriter(
new OutputStreamWriter(
sZygoteSocket.getOutputStream()),
256);
Log.i("Zygote", "Process: zygote socket opened");
sPreviousZygoteOpenFailed = false;
break;
} catch (IOException ex) {
if (sZygoteSocket != null) {
try {
sZygoteSocket.close();
} catch (IOException ex2) {
Log.e(LOG_TAG,"I/O exception on close after exception",
ex2);
}
}
sZygoteSocket = null;
}
}
if (sZygoteSocket == null) {
sPreviousZygoteOpenFailed = true;
throw new ZygoteStartFailedEx("connect failed");
}
}
/**
* Sends an argument list to the zygote process, which starts a new child
* and returns the child's pid. Please note: the present implementation
* replaces newlines in the argument list with spaces.
* @param args argument list
* @return PID of new child process
* @throws ZygoteStartFailedEx if process start failed for any reason
*/
private static int zygoteSendArgsAndGetPid(ArrayList