am 7e79b9aa: am dbff9107: Merge "Support wrapping app processes to inject debug instrumentation. Bug: 4437846"
* commit '7e79b9aad85db332d334f8bc1625bf46f030830f': Support wrapping app processes to inject debug instrumentation. Bug: 4437846
This commit is contained in:
@@ -128,10 +128,7 @@ int main(int argc, const char* const argv[])
|
||||
mArgLen--;
|
||||
|
||||
AppRuntime runtime;
|
||||
const char *arg;
|
||||
const char *argv0;
|
||||
|
||||
argv0 = argv[0];
|
||||
const char* argv0 = argv[0];
|
||||
|
||||
// Process command line arguments
|
||||
// ignore argv[0]
|
||||
@@ -142,39 +139,53 @@ int main(int argc, const char* const argv[])
|
||||
|
||||
int i = runtime.addVmArguments(argc, argv);
|
||||
|
||||
// Next arg is parent directory
|
||||
if (i < argc) {
|
||||
runtime.mParentDir = argv[i++];
|
||||
// Parse runtime arguments. Stop at first unrecognized option.
|
||||
bool zygote = false;
|
||||
bool startSystemServer = false;
|
||||
bool application = false;
|
||||
const char* parentDir = NULL;
|
||||
const char* niceName = NULL;
|
||||
const char* className = NULL;
|
||||
while (i < argc) {
|
||||
const char* arg = argv[i++];
|
||||
if (!parentDir) {
|
||||
parentDir = arg;
|
||||
} else if (strcmp(arg, "--zygote") == 0) {
|
||||
zygote = true;
|
||||
niceName = "zygote";
|
||||
} else if (strcmp(arg, "--start-system-server") == 0) {
|
||||
startSystemServer = true;
|
||||
} else if (strcmp(arg, "--application") == 0) {
|
||||
application = true;
|
||||
} else if (strncmp(arg, "--nice-name=", 12) == 0) {
|
||||
niceName = arg + 12;
|
||||
} else {
|
||||
className = arg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Next arg is startup classname or "--zygote"
|
||||
if (i < argc) {
|
||||
arg = argv[i++];
|
||||
if (0 == strcmp("--zygote", arg)) {
|
||||
bool startSystemServer = (i < argc) ?
|
||||
strcmp(argv[i], "--start-system-server") == 0 : false;
|
||||
setArgv0(argv0, "zygote");
|
||||
set_process_name("zygote");
|
||||
runtime.start("com.android.internal.os.ZygoteInit",
|
||||
startSystemServer);
|
||||
} else {
|
||||
set_process_name(argv0);
|
||||
if (niceName && *niceName) {
|
||||
setArgv0(argv0, niceName);
|
||||
set_process_name(niceName);
|
||||
}
|
||||
|
||||
runtime.mClassName = arg;
|
||||
runtime.mParentDir = parentDir;
|
||||
|
||||
// Remainder of args get passed to startup class main()
|
||||
runtime.mArgC = argc-i;
|
||||
runtime.mArgV = argv+i;
|
||||
|
||||
LOGV("App process is starting with pid=%d, class=%s.\n",
|
||||
getpid(), runtime.getClassName());
|
||||
runtime.start();
|
||||
}
|
||||
if (zygote) {
|
||||
runtime.start("com.android.internal.os.ZygoteInit",
|
||||
startSystemServer ? "start-system-server" : "");
|
||||
} else if (className) {
|
||||
// Remainder of args get passed to startup class main()
|
||||
runtime.mClassName = className;
|
||||
runtime.mArgC = argc - i;
|
||||
runtime.mArgV = argv + i;
|
||||
runtime.start("com.android.internal.os.RuntimeInit",
|
||||
application ? "application" : "tool");
|
||||
} else {
|
||||
LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
|
||||
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
|
||||
app_usage();
|
||||
return 10;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -497,8 +497,8 @@ int main(int argc, char* const argv[])
|
||||
} else {
|
||||
#ifndef HAVE_ANDROID_OS
|
||||
QuickRuntime* runt = new QuickRuntime();
|
||||
runt->start("com/android/server/SystemServer",
|
||||
false /* spontaneously fork system server from zygote */);
|
||||
runt->start("com/android/server/SystemServer",
|
||||
"" /* spontaneously fork system server from zygote */);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -257,13 +257,12 @@ public class 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
|
||||
* @return An object that describes the result of the attempt to start the process.
|
||||
* @throws RuntimeException on fatal start failure
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
public static final int start(final String processClass,
|
||||
public static final ProcessStartResult start(final String processClass,
|
||||
final String niceName,
|
||||
int uid, int gid, int[] gids,
|
||||
int debugFlags,
|
||||
@@ -294,8 +293,7 @@ public class Process {
|
||||
} else {
|
||||
new Thread(runnable).start();
|
||||
}
|
||||
|
||||
return 0;
|
||||
return new ProcessStartResult();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,7 +301,7 @@ public class Process {
|
||||
* Start a new process. Don't supply a custom nice name.
|
||||
* {@hide}
|
||||
*/
|
||||
public static final int start(String processClass, int uid, int gid,
|
||||
public static final ProcessStartResult start(String processClass, int uid, int gid,
|
||||
int[] gids, int debugFlags, String[] zygoteArgs) {
|
||||
return start(processClass, "", uid, gid, gids,
|
||||
debugFlags, zygoteArgs);
|
||||
@@ -418,14 +416,11 @@ public class Process {
|
||||
* 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
|
||||
* @return An object that describes the result of the attempt to start the process.
|
||||
* @throws ZygoteStartFailedEx if process start failed for any reason
|
||||
*/
|
||||
private static int zygoteSendArgsAndGetPid(ArrayList<String> args)
|
||||
private static ProcessStartResult zygoteSendArgsAndGetResult(ArrayList<String> args)
|
||||
throws ZygoteStartFailedEx {
|
||||
|
||||
int pid;
|
||||
|
||||
openZygoteSocketIfNeeded();
|
||||
|
||||
try {
|
||||
@@ -436,7 +431,8 @@ public class Process {
|
||||
* b) a number of newline-separated argument strings equal to count
|
||||
*
|
||||
* After the zygote process reads these it will write the pid of
|
||||
* the child or -1 on failure.
|
||||
* the child or -1 on failure, followed by boolean to
|
||||
* indicate whether a wrapper process was used.
|
||||
*/
|
||||
|
||||
sZygoteWriter.write(Integer.toString(args.size()));
|
||||
@@ -456,11 +452,13 @@ public class Process {
|
||||
sZygoteWriter.flush();
|
||||
|
||||
// Should there be a timeout on this?
|
||||
pid = sZygoteInputStream.readInt();
|
||||
|
||||
if (pid < 0) {
|
||||
ProcessStartResult result = new ProcessStartResult();
|
||||
result.pid = sZygoteInputStream.readInt();
|
||||
if (result.pid < 0) {
|
||||
throw new ZygoteStartFailedEx("fork() failed");
|
||||
}
|
||||
result.usingWrapper = sZygoteInputStream.readBoolean();
|
||||
return result;
|
||||
} catch (IOException ex) {
|
||||
try {
|
||||
if (sZygoteSocket != null) {
|
||||
@@ -475,8 +473,6 @@ public class Process {
|
||||
|
||||
throw new ZygoteStartFailedEx(ex);
|
||||
}
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -490,18 +486,16 @@ public class Process {
|
||||
* new process should setgroup() to.
|
||||
* @param enableDebugger True if debugging should be enabled for this process.
|
||||
* @param extraArgs Additional arguments to supply to the zygote process.
|
||||
* @return PID
|
||||
* @return An object that describes the result of the attempt to start the process.
|
||||
* @throws ZygoteStartFailedEx if process start failed for any reason
|
||||
*/
|
||||
private static int startViaZygote(final String processClass,
|
||||
private static ProcessStartResult startViaZygote(final String processClass,
|
||||
final String niceName,
|
||||
final int uid, final int gid,
|
||||
final int[] gids,
|
||||
int debugFlags,
|
||||
String[] extraArgs)
|
||||
throws ZygoteStartFailedEx {
|
||||
int pid;
|
||||
|
||||
synchronized(Process.class) {
|
||||
ArrayList<String> argsForZygote = new ArrayList<String>();
|
||||
|
||||
@@ -553,15 +547,9 @@ public class Process {
|
||||
argsForZygote.add(arg);
|
||||
}
|
||||
}
|
||||
|
||||
pid = zygoteSendArgsAndGetPid(argsForZygote);
|
||||
}
|
||||
|
||||
if (pid <= 0) {
|
||||
throw new ZygoteStartFailedEx("zygote start failed:" + pid);
|
||||
return zygoteSendArgsAndGetResult(argsForZygote);
|
||||
}
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -615,6 +603,20 @@ public class Process {
|
||||
return (int) procStatusValues[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent process id for a currently running process.
|
||||
* @param pid the process id
|
||||
* @return the parent process id of the process, or -1 if the process is not running.
|
||||
* @hide
|
||||
*/
|
||||
public static final int getParentPid(int pid) {
|
||||
String[] procStatusLabels = { "PPid:" };
|
||||
long[] procStatusValues = new long[1];
|
||||
procStatusValues[0] = -1;
|
||||
Process.readProcLines("/proc/" + pid + "/status", procStatusLabels, procStatusValues);
|
||||
return (int) procStatusValues[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the priority of a thread, based on Linux priorities.
|
||||
*
|
||||
@@ -826,4 +828,21 @@ public class Process {
|
||||
* @hide
|
||||
*/
|
||||
public static final native long getPss(int pid);
|
||||
|
||||
/**
|
||||
* Specifies the outcome of having started a process.
|
||||
* @hide
|
||||
*/
|
||||
public static final class ProcessStartResult {
|
||||
/**
|
||||
* The PID of the newly started process.
|
||||
* Always >= 0. (If the start failed, an exception will have been thrown instead.)
|
||||
*/
|
||||
public int pid;
|
||||
|
||||
/**
|
||||
* True if the process was started with a wrapper attached.
|
||||
*/
|
||||
public boolean usingWrapper;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import android.os.Debug;
|
||||
import android.os.IBinder;
|
||||
import android.os.Process;
|
||||
import android.os.SystemProperties;
|
||||
import android.util.Config;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
|
||||
@@ -46,6 +45,7 @@ import org.apache.harmony.luni.internal.util.TimezoneGetter;
|
||||
*/
|
||||
public class RuntimeInit {
|
||||
private final static String TAG = "AndroidRuntime";
|
||||
private final static boolean DEBUG = false;
|
||||
|
||||
/** true if commonInit() has been called */
|
||||
private static boolean initialized;
|
||||
@@ -90,14 +90,14 @@ public class RuntimeInit {
|
||||
}
|
||||
|
||||
private static final void commonInit() {
|
||||
if (Config.LOGV) Slog.d(TAG, "Entered RuntimeInit!");
|
||||
if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!");
|
||||
|
||||
/* set default handler; this applies to all threads in the VM */
|
||||
Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler());
|
||||
|
||||
int hasQwerty = getQwertyKeyboard();
|
||||
|
||||
if (Config.LOGV) Slog.d(TAG, ">>>>> qwerty keyboard = " + hasQwerty);
|
||||
if (DEBUG) Slog.d(TAG, ">>>>> qwerty keyboard = " + hasQwerty);
|
||||
if (hasQwerty == 1) {
|
||||
System.setProperty("qwerty", "1");
|
||||
}
|
||||
@@ -184,11 +184,6 @@ public class RuntimeInit {
|
||||
*/
|
||||
private static void invokeStaticMain(String className, String[] argv)
|
||||
throws ZygoteInit.MethodAndArgsCaller {
|
||||
|
||||
// We want to be fairly aggressive about heap utilization, to avoid
|
||||
// holding on to a lot of memory that isn't needed.
|
||||
VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
|
||||
|
||||
Class<?> cl;
|
||||
|
||||
try {
|
||||
@@ -226,6 +221,13 @@ public class RuntimeInit {
|
||||
}
|
||||
|
||||
public static final void main(String[] argv) {
|
||||
if (argv.length == 2 && argv[1].equals("application")) {
|
||||
if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application");
|
||||
redirectLogStreams();
|
||||
} else {
|
||||
if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting tool");
|
||||
}
|
||||
|
||||
commonInit();
|
||||
|
||||
/*
|
||||
@@ -234,7 +236,7 @@ public class RuntimeInit {
|
||||
*/
|
||||
finishInit();
|
||||
|
||||
if (Config.LOGV) Slog.d(TAG, "Leaving RuntimeInit!");
|
||||
if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");
|
||||
}
|
||||
|
||||
public static final native void finishInit();
|
||||
@@ -246,7 +248,6 @@ public class RuntimeInit {
|
||||
*
|
||||
* Current recognized args:
|
||||
* <ul>
|
||||
* <li> --nice-name=<i>nice name to appear in ps</i>
|
||||
* <li> <code> [--] <start class name> <args>
|
||||
* </ul>
|
||||
*
|
||||
@@ -254,43 +255,60 @@ public class RuntimeInit {
|
||||
*/
|
||||
public static final void zygoteInit(String[] argv)
|
||||
throws ZygoteInit.MethodAndArgsCaller {
|
||||
// TODO: Doing this here works, but it seems kind of arbitrary. Find
|
||||
// a better place. The goal is to set it up for applications, but not
|
||||
// tools like am.
|
||||
System.setOut(new AndroidPrintStream(Log.INFO, "System.out"));
|
||||
System.setErr(new AndroidPrintStream(Log.WARN, "System.err"));
|
||||
if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");
|
||||
|
||||
redirectLogStreams();
|
||||
|
||||
commonInit();
|
||||
zygoteInitNative();
|
||||
|
||||
int curArg = 0;
|
||||
for ( /* curArg */ ; curArg < argv.length; curArg++) {
|
||||
String arg = argv[curArg];
|
||||
applicationInit(argv);
|
||||
}
|
||||
|
||||
if (arg.equals("--")) {
|
||||
curArg++;
|
||||
break;
|
||||
} else if (!arg.startsWith("--")) {
|
||||
break;
|
||||
} else if (arg.startsWith("--nice-name=")) {
|
||||
String niceName = arg.substring(arg.indexOf('=') + 1);
|
||||
Process.setArgV0(niceName);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* The main function called when an application is started through a
|
||||
* wrapper process.
|
||||
*
|
||||
* When the wrapper starts, the runtime starts {@link RuntimeInit#main}
|
||||
* which calls {@link WrapperInit#main} which then calls this method.
|
||||
* So we don't need to call commonInit() here.
|
||||
*
|
||||
* @param argv arg strings
|
||||
*/
|
||||
public static void wrapperInit(String[] argv)
|
||||
throws ZygoteInit.MethodAndArgsCaller {
|
||||
if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from wrapper");
|
||||
|
||||
if (curArg == argv.length) {
|
||||
Slog.e(TAG, "Missing classname argument to RuntimeInit!");
|
||||
applicationInit(argv);
|
||||
}
|
||||
|
||||
private static void applicationInit(String[] argv)
|
||||
throws ZygoteInit.MethodAndArgsCaller {
|
||||
// We want to be fairly aggressive about heap utilization, to avoid
|
||||
// holding on to a lot of memory that isn't needed.
|
||||
VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
|
||||
|
||||
final Arguments args;
|
||||
try {
|
||||
args = new Arguments(argv);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
Slog.e(TAG, ex.getMessage());
|
||||
// let the process exit
|
||||
return;
|
||||
}
|
||||
|
||||
// Remaining arguments are passed to the start class's static main
|
||||
invokeStaticMain(args.startClass, args.startArgs);
|
||||
}
|
||||
|
||||
String startClass = argv[curArg++];
|
||||
String[] startArgs = new String[argv.length - curArg];
|
||||
|
||||
System.arraycopy(argv, curArg, startArgs, 0, startArgs.length);
|
||||
invokeStaticMain(startClass, startArgs);
|
||||
/**
|
||||
* Redirect System.out and System.err to the Android log.
|
||||
*/
|
||||
public static void redirectLogStreams() {
|
||||
System.out.close();
|
||||
System.setOut(new AndroidPrintStream(Log.INFO, "System.out"));
|
||||
System.err.close();
|
||||
System.setErr(new AndroidPrintStream(Log.WARN, "System.err"));
|
||||
}
|
||||
|
||||
public static final native void zygoteInitNative();
|
||||
@@ -353,4 +371,55 @@ public class RuntimeInit {
|
||||
// Register handlers for DDM messages.
|
||||
android.ddm.DdmRegister.registerHandlers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles argument parsing for args related to the runtime.
|
||||
*
|
||||
* Current recognized args:
|
||||
* <ul>
|
||||
* <li> <code> [--] <start class name> <args>
|
||||
* </ul>
|
||||
*/
|
||||
static class Arguments {
|
||||
/** first non-option argument */
|
||||
String startClass;
|
||||
|
||||
/** all following arguments */
|
||||
String[] startArgs;
|
||||
|
||||
/**
|
||||
* Constructs instance and parses args
|
||||
* @param args runtime command-line args
|
||||
* @throws IllegalArgumentException
|
||||
*/
|
||||
Arguments(String args[]) throws IllegalArgumentException {
|
||||
parseArgs(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the commandline arguments intended for the Runtime.
|
||||
*/
|
||||
private void parseArgs(String args[])
|
||||
throws IllegalArgumentException {
|
||||
int curArg = 0;
|
||||
for (; curArg < args.length; curArg++) {
|
||||
String arg = args[curArg];
|
||||
|
||||
if (arg.equals("--")) {
|
||||
curArg++;
|
||||
break;
|
||||
} else if (!arg.startsWith("--")) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (curArg == args.length) {
|
||||
throw new IllegalArgumentException("Missing classname argument to RuntimeInit!");
|
||||
}
|
||||
|
||||
startClass = args[curArg++];
|
||||
startArgs = new String[args.length - curArg];
|
||||
System.arraycopy(args, curArg, startArgs, 0, startArgs.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
120
core/java/com/android/internal/os/WrapperInit.java
Normal file
120
core/java/com/android/internal/os/WrapperInit.java
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.android.internal.os;
|
||||
|
||||
import android.os.Process;
|
||||
import android.util.Slog;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import dalvik.system.Zygote;
|
||||
|
||||
/**
|
||||
* Startup class for the wrapper process.
|
||||
* @hide
|
||||
*/
|
||||
public class WrapperInit {
|
||||
private final static String TAG = "AndroidRuntime";
|
||||
|
||||
/**
|
||||
* Class not instantiable.
|
||||
*/
|
||||
private WrapperInit() {
|
||||
}
|
||||
|
||||
/**
|
||||
* The main function called when starting a runtime application through a
|
||||
* wrapper process instead of by forking Zygote.
|
||||
*
|
||||
* The first argument specifies the file descriptor for a pipe that should receive
|
||||
* the pid of this process, or 0 if none. The remaining arguments are passed to
|
||||
* the runtime.
|
||||
*
|
||||
* @param args The command-line arguments.
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
// Tell the Zygote what our actual PID is (since it only knows about the
|
||||
// wrapper that it directly forked).
|
||||
int fdNum = Integer.parseInt(args[0], 10);
|
||||
if (fdNum != 0) {
|
||||
try {
|
||||
FileDescriptor fd = ZygoteInit.createFileDescriptor(fdNum);
|
||||
DataOutputStream os = new DataOutputStream(new FileOutputStream(fd));
|
||||
os.writeInt(Process.myPid());
|
||||
os.close();
|
||||
ZygoteInit.closeDescriptorQuietly(fd);
|
||||
} catch (IOException ex) {
|
||||
Slog.d(TAG, "Could not write pid of wrapped process to Zygote pipe.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
// Mimic Zygote preloading.
|
||||
ZygoteInit.preload();
|
||||
|
||||
// Launch the application.
|
||||
String[] runtimeArgs = new String[args.length - 1];
|
||||
System.arraycopy(args, 1, runtimeArgs, 0, runtimeArgs.length);
|
||||
RuntimeInit.wrapperInit(runtimeArgs);
|
||||
} catch (ZygoteInit.MethodAndArgsCaller caller) {
|
||||
caller.run();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a runtime application with a wrapper command.
|
||||
* This method never returns.
|
||||
*
|
||||
* @param invokeWith The wrapper command.
|
||||
* @param niceName The nice name for the application, or null if none.
|
||||
* @param pipeFd The pipe to which the application's pid should be written, or null if none.
|
||||
* @param args Arguments for {@link RuntimeInit.main}.
|
||||
*/
|
||||
public static void execApplication(String invokeWith, String niceName,
|
||||
FileDescriptor pipeFd, String[] args) {
|
||||
StringBuilder command = new StringBuilder(invokeWith);
|
||||
command.append(" /system/bin/app_process /system/bin --application");
|
||||
if (niceName != null) {
|
||||
command.append(" '--nice-name=").append(niceName).append("'");
|
||||
}
|
||||
command.append(" com.android.internal.os.WrapperInit ");
|
||||
command.append(pipeFd != null ? ZygoteInit.getFDFromFileDescriptor(pipeFd) : 0);
|
||||
Zygote.appendQuotedShellArgs(command, args);
|
||||
Zygote.execShell(command.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a standalone application with a wrapper command.
|
||||
* This method never returns.
|
||||
*
|
||||
* @param invokeWith The wrapper command.
|
||||
* @param classPath The class path.
|
||||
* @param className The class name to invoke.
|
||||
* @param args Arguments for the main() method of the specified class.
|
||||
*/
|
||||
public static void execStandalone(String invokeWith, String classPath, String className,
|
||||
String[] args) {
|
||||
StringBuilder command = new StringBuilder(invokeWith);
|
||||
command.append(" /system/bin/dalvikvm -classpath '").append(classPath);
|
||||
command.append("' ").append(className);
|
||||
Zygote.appendQuotedShellArgs(command, args);
|
||||
Zygote.execShell(command.toString());
|
||||
}
|
||||
}
|
||||
@@ -26,8 +26,10 @@ import dalvik.system.PathClassLoader;
|
||||
import dalvik.system.Zygote;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
@@ -193,15 +195,20 @@ class ZygoteConnection {
|
||||
new FileOutputStream(descriptors[2]));
|
||||
}
|
||||
|
||||
int pid;
|
||||
int pid = -1;
|
||||
FileDescriptor childPipeFd = null;
|
||||
FileDescriptor serverPipeFd = null;
|
||||
|
||||
try {
|
||||
parsedArgs = new Arguments(args);
|
||||
|
||||
applyUidSecurityPolicy(parsedArgs, peer);
|
||||
applyDebuggerSecurityPolicy(parsedArgs);
|
||||
applyRlimitSecurityPolicy(parsedArgs, peer);
|
||||
applyCapabilitiesSecurityPolicy(parsedArgs, peer);
|
||||
applyInvokeWithSecurityPolicy(parsedArgs, peer);
|
||||
|
||||
applyDebuggerSystemProperty(parsedArgs);
|
||||
applyInvokeWithSystemProperty(parsedArgs);
|
||||
|
||||
int[][] rlimits = null;
|
||||
|
||||
@@ -209,25 +216,44 @@ class ZygoteConnection {
|
||||
rlimits = parsedArgs.rlimits.toArray(intArray2d);
|
||||
}
|
||||
|
||||
if (parsedArgs.runtimeInit && parsedArgs.invokeWith != null) {
|
||||
FileDescriptor[] pipeFds = new FileDescriptor[2];
|
||||
ZygoteInit.pipe(pipeFds);
|
||||
childPipeFd = pipeFds[1];
|
||||
serverPipeFd = pipeFds[0];
|
||||
ZygoteInit.setCloseOnExec(serverPipeFd, true);
|
||||
}
|
||||
|
||||
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid,
|
||||
parsedArgs.gids, parsedArgs.debugFlags, rlimits);
|
||||
} catch (IOException ex) {
|
||||
logAndPrintError(newStderr, "Exception creating pipe", ex);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
logAndPrintError (newStderr, "Invalid zygote arguments", ex);
|
||||
pid = -1;
|
||||
logAndPrintError(newStderr, "Invalid zygote arguments", ex);
|
||||
} catch (ZygoteSecurityException ex) {
|
||||
logAndPrintError(newStderr,
|
||||
"Zygote security policy prevents request: ", ex);
|
||||
pid = -1;
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
// in child
|
||||
handleChildProc(parsedArgs, descriptors, newStderr);
|
||||
// should never happen
|
||||
return true;
|
||||
} else { /* pid != 0 */
|
||||
// in parent...pid of < 0 means failure
|
||||
return handleParentProc(pid, descriptors, parsedArgs);
|
||||
try {
|
||||
if (pid == 0) {
|
||||
// in child
|
||||
ZygoteInit.closeDescriptorQuietly(serverPipeFd);
|
||||
serverPipeFd = null;
|
||||
handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
|
||||
|
||||
// should never get here, the child is expected to either
|
||||
// throw ZygoteInit.MethodAndArgsCaller or exec().
|
||||
return true;
|
||||
} else {
|
||||
// in parent...pid of < 0 means failure
|
||||
ZygoteInit.closeDescriptorQuietly(childPipeFd);
|
||||
childPipeFd = null;
|
||||
return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
|
||||
}
|
||||
} finally {
|
||||
ZygoteInit.closeDescriptorQuietly(childPipeFd);
|
||||
ZygoteInit.closeDescriptorQuietly(serverPipeFd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,8 +270,8 @@ class ZygoteConnection {
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles argument parsing for args related to the zygote spawner.<p>
|
||||
|
||||
* Handles argument parsing for args related to the zygote spawner.
|
||||
*
|
||||
* Current recognized args:
|
||||
* <ul>
|
||||
* <li> --setuid=<i>uid of child process, defaults to 0</i>
|
||||
@@ -274,6 +300,7 @@ class ZygoteConnection {
|
||||
* be handed off to com.android.internal.os.RuntimeInit, rather than
|
||||
* processed directly
|
||||
* Android runtime startup (eg, Binder initialization) is also eschewed.
|
||||
* <li> --nice-name=<i>nice name to appear in ps</i>
|
||||
* <li> If <code>--runtime-init</code> is present:
|
||||
* [--] <args for RuntimeInit >
|
||||
* <li> If <code>--runtime-init</code> is absent:
|
||||
@@ -307,6 +334,9 @@ class ZygoteConnection {
|
||||
/** from --runtime-init */
|
||||
boolean runtimeInit;
|
||||
|
||||
/** from --nice-name */
|
||||
String niceName;
|
||||
|
||||
/** from --capabilities */
|
||||
boolean capabilitiesSpecified;
|
||||
long permittedCapabilities;
|
||||
@@ -315,6 +345,9 @@ class ZygoteConnection {
|
||||
/** from all --rlimit=r,c,m */
|
||||
ArrayList<int[]> rlimits;
|
||||
|
||||
/** from --invoke-with */
|
||||
String invokeWith;
|
||||
|
||||
/**
|
||||
* Any args after and including the first non-option arg
|
||||
* (or after a '--')
|
||||
@@ -438,6 +471,23 @@ class ZygoteConnection {
|
||||
for (int i = params.length - 1; i >= 0 ; i--) {
|
||||
gids[i] = Integer.parseInt(params[i]);
|
||||
}
|
||||
} else if (arg.equals("--invoke-with")) {
|
||||
if (invokeWith != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Duplicate arg specified");
|
||||
}
|
||||
try {
|
||||
invokeWith = args[++curArg];
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
throw new IllegalArgumentException(
|
||||
"--invoke-with requires argument");
|
||||
}
|
||||
} else if (arg.startsWith("--nice-name=")) {
|
||||
if (niceName != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Duplicate arg specified");
|
||||
}
|
||||
niceName = arg.substring(arg.indexOf('=') + 1);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@@ -567,14 +617,15 @@ class ZygoteConnection {
|
||||
|
||||
|
||||
/**
|
||||
* Applies debugger security policy.
|
||||
* Applies debugger system properties to the zygote arguments.
|
||||
*
|
||||
* If "ro.debuggable" is "1", all apps are debuggable. Otherwise,
|
||||
* the debugger state is specified via the "--enable-debugger" flag
|
||||
* in the spawn request.
|
||||
*
|
||||
* @param args non-null; zygote spawner args
|
||||
*/
|
||||
private static void applyDebuggerSecurityPolicy(Arguments args) {
|
||||
public static void applyDebuggerSystemProperty(Arguments args) {
|
||||
if ("1".equals(SystemProperties.get("ro.debuggable"))) {
|
||||
args.debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;
|
||||
}
|
||||
@@ -663,6 +714,49 @@ class ZygoteConnection {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies zygote security policy.
|
||||
* Based on the credentials of the process issuing a zygote command:
|
||||
* <ol>
|
||||
* <li> uid 0 (root) may specify --invoke-with to launch Zygote with a
|
||||
* wrapper command.
|
||||
* <li> Any other uid may not specify any invoke-with argument.
|
||||
* </ul>
|
||||
*
|
||||
* @param args non-null; zygote spawner arguments
|
||||
* @param peer non-null; peer credentials
|
||||
* @throws ZygoteSecurityException
|
||||
*/
|
||||
private static void applyInvokeWithSecurityPolicy(Arguments args, Credentials peer)
|
||||
throws ZygoteSecurityException {
|
||||
int peerUid = peer.getUid();
|
||||
|
||||
if (args.invokeWith != null && peerUid != 0) {
|
||||
throw new ZygoteSecurityException("Peer is not permitted to specify "
|
||||
+ "an explicit invoke-with wrapper command");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies invoke-with system properties to the zygote arguments.
|
||||
*
|
||||
* @param parsedArgs non-null; zygote args
|
||||
*/
|
||||
public static void applyInvokeWithSystemProperty(Arguments args) {
|
||||
if (args.invokeWith == null && args.niceName != null) {
|
||||
if (args.niceName != null) {
|
||||
String property = "wrap." + args.niceName;
|
||||
if (property.length() > 31) {
|
||||
property = property.substring(0, 31);
|
||||
}
|
||||
args.invokeWith = SystemProperties.get(property);
|
||||
if (args.invokeWith != null && args.invokeWith.length() == 0) {
|
||||
args.invokeWith = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles post-fork setup of child proc, closing sockets as appropriate,
|
||||
* reopen stdio as appropriate, and ultimately throwing MethodAndArgsCaller
|
||||
@@ -670,6 +764,7 @@ class ZygoteConnection {
|
||||
*
|
||||
* @param parsedArgs non-null; zygote args
|
||||
* @param descriptors null-ok; new file descriptors for stdio if available.
|
||||
* @param pipeFd null-ok; pipe for communication back to Zygote.
|
||||
* @param newStderr null-ok; stream to use for stderr until stdio
|
||||
* is reopened.
|
||||
*
|
||||
@@ -677,7 +772,7 @@ class ZygoteConnection {
|
||||
* trampoline to code that invokes static main.
|
||||
*/
|
||||
private void handleChildProc(Arguments parsedArgs,
|
||||
FileDescriptor[] descriptors, PrintStream newStderr)
|
||||
FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
|
||||
throws ZygoteInit.MethodAndArgsCaller {
|
||||
|
||||
/*
|
||||
@@ -704,7 +799,7 @@ class ZygoteConnection {
|
||||
descriptors[1], descriptors[2]);
|
||||
|
||||
for (FileDescriptor fd: descriptors) {
|
||||
ZygoteInit.closeDescriptor(fd);
|
||||
ZygoteInit.closeDescriptorQuietly(fd);
|
||||
}
|
||||
newStderr = System.err;
|
||||
} catch (IOException ex) {
|
||||
@@ -712,37 +807,48 @@ class ZygoteConnection {
|
||||
}
|
||||
}
|
||||
|
||||
if (parsedArgs.niceName != null) {
|
||||
Process.setArgV0(parsedArgs.niceName);
|
||||
}
|
||||
|
||||
if (parsedArgs.runtimeInit) {
|
||||
RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
|
||||
} else {
|
||||
ClassLoader cloader;
|
||||
|
||||
if (parsedArgs.classpath != null) {
|
||||
cloader
|
||||
= new PathClassLoader(parsedArgs.classpath,
|
||||
ClassLoader.getSystemClassLoader());
|
||||
if (parsedArgs.invokeWith != null) {
|
||||
WrapperInit.execApplication(parsedArgs.invokeWith,
|
||||
parsedArgs.niceName, pipeFd, parsedArgs.remainingArgs);
|
||||
} else {
|
||||
cloader = ClassLoader.getSystemClassLoader();
|
||||
RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
|
||||
}
|
||||
|
||||
} else {
|
||||
String className;
|
||||
try {
|
||||
className = parsedArgs.remainingArgs[0];
|
||||
} catch (ArrayIndexOutOfBoundsException ex) {
|
||||
logAndPrintError (newStderr,
|
||||
logAndPrintError(newStderr,
|
||||
"Missing required class name argument", null);
|
||||
return;
|
||||
}
|
||||
String[] mainArgs
|
||||
= new String[parsedArgs.remainingArgs.length - 1];
|
||||
|
||||
String[] mainArgs = new String[parsedArgs.remainingArgs.length - 1];
|
||||
System.arraycopy(parsedArgs.remainingArgs, 1,
|
||||
mainArgs, 0, mainArgs.length);
|
||||
|
||||
try {
|
||||
ZygoteInit.invokeStaticMain(cloader, className, mainArgs);
|
||||
} catch (RuntimeException ex) {
|
||||
logAndPrintError (newStderr, "Error starting. ", ex);
|
||||
if (parsedArgs.invokeWith != null) {
|
||||
WrapperInit.execStandalone(parsedArgs.invokeWith,
|
||||
parsedArgs.classpath, className, mainArgs);
|
||||
} else {
|
||||
ClassLoader cloader;
|
||||
if (parsedArgs.classpath != null) {
|
||||
cloader = new PathClassLoader(parsedArgs.classpath,
|
||||
ClassLoader.getSystemClassLoader());
|
||||
} else {
|
||||
cloader = ClassLoader.getSystemClassLoader();
|
||||
}
|
||||
|
||||
try {
|
||||
ZygoteInit.invokeStaticMain(cloader, className, mainArgs);
|
||||
} catch (RuntimeException ex) {
|
||||
logAndPrintError(newStderr, "Error starting.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -754,40 +860,61 @@ class ZygoteConnection {
|
||||
* if < 0;
|
||||
* @param descriptors null-ok; file descriptors for child's new stdio if
|
||||
* specified.
|
||||
* @param pipeFd null-ok; pipe for communication with child.
|
||||
* @param parsedArgs non-null; zygote args
|
||||
* @return true for "exit command loop" and false for "continue command
|
||||
* loop"
|
||||
*/
|
||||
private boolean handleParentProc(int pid,
|
||||
FileDescriptor[] descriptors, Arguments parsedArgs) {
|
||||
FileDescriptor[] descriptors, FileDescriptor pipeFd, Arguments parsedArgs) {
|
||||
|
||||
if(pid > 0) {
|
||||
// Try to move the new child into the peer's process group.
|
||||
try {
|
||||
ZygoteInit.setpgid(pid, ZygoteInit.getpgid(peer.getPid()));
|
||||
} catch (IOException ex) {
|
||||
// This exception is expected in the case where
|
||||
// the peer is not in our session
|
||||
// TODO get rid of this log message in the case where
|
||||
// getsid(0) != getsid(peer.getPid())
|
||||
Log.i(TAG, "Zygote: setpgid failed. This is "
|
||||
+ "normal if peer is not in our session");
|
||||
if (pid > 0) {
|
||||
setChildPgid(pid);
|
||||
}
|
||||
|
||||
if (descriptors != null) {
|
||||
for (FileDescriptor fd: descriptors) {
|
||||
ZygoteInit.closeDescriptorQuietly(fd);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (descriptors != null) {
|
||||
for (FileDescriptor fd: descriptors) {
|
||||
ZygoteInit.closeDescriptor(fd);
|
||||
boolean usingWrapper = false;
|
||||
if (pipeFd != null && pid > 0) {
|
||||
DataInputStream is = new DataInputStream(new FileInputStream(pipeFd));
|
||||
int innerPid = -1;
|
||||
try {
|
||||
innerPid = is.readInt();
|
||||
} catch (IOException ex) {
|
||||
Log.w(TAG, "Error reading pid from wrapped process, child may have died", ex);
|
||||
} finally {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException ex) {
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that the pid reported by the wrapped process is either the
|
||||
// child process that we forked, or a descendant of it.
|
||||
if (innerPid > 0) {
|
||||
int parentPid = innerPid;
|
||||
while (parentPid > 0 && parentPid != pid) {
|
||||
parentPid = Process.getParentPid(parentPid);
|
||||
}
|
||||
if (parentPid > 0) {
|
||||
Log.i(TAG, "Wrapped process has pid " + innerPid);
|
||||
pid = innerPid;
|
||||
usingWrapper = true;
|
||||
} else {
|
||||
Log.w(TAG, "Wrapped process reported a pid that is not a child of "
|
||||
+ "the process that we forked: childPid=" + pid
|
||||
+ " innerPid=" + innerPid);
|
||||
}
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
Log.e(TAG, "Error closing passed descriptors in "
|
||||
+ "parent process", ex);
|
||||
}
|
||||
|
||||
try {
|
||||
mSocketOutStream.writeInt(pid);
|
||||
mSocketOutStream.writeBoolean(usingWrapper);
|
||||
} catch (IOException ex) {
|
||||
Log.e(TAG, "Error reading from command socket", ex);
|
||||
return true;
|
||||
@@ -808,6 +935,20 @@ class ZygoteConnection {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void setChildPgid(int pid) {
|
||||
// Try to move the new child into the peer's process group.
|
||||
try {
|
||||
ZygoteInit.setpgid(pid, ZygoteInit.getpgid(peer.getPid()));
|
||||
} catch (IOException ex) {
|
||||
// This exception is expected in the case where
|
||||
// the peer is not in our session
|
||||
// TODO get rid of this log message in the case where
|
||||
// getsid(0) != getsid(peer.getPid())
|
||||
Log.i(TAG, "Zygote: setpgid failed. This is "
|
||||
+ "normal if peer is not in our session");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an error message and prints it to the specified stream, if
|
||||
* provided
|
||||
|
||||
@@ -22,6 +22,8 @@ import android.content.res.TypedArray;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.LocalServerSocket;
|
||||
import android.os.Debug;
|
||||
import android.os.FileUtils;
|
||||
import android.os.Process;
|
||||
import android.os.SystemClock;
|
||||
import android.os.SystemProperties;
|
||||
import android.util.Config;
|
||||
@@ -67,7 +69,7 @@ public class ZygoteInit {
|
||||
private static final int PRELOAD_GC_THRESHOLD = 50000;
|
||||
|
||||
public static final String USAGE_STRING =
|
||||
" <\"true\"|\"false\" for startSystemServer>";
|
||||
" <\"start-system-server\"|\"\" for startSystemServer>";
|
||||
|
||||
private static LocalServerSocket sServerSocket;
|
||||
|
||||
@@ -245,6 +247,11 @@ public class ZygoteInit {
|
||||
}
|
||||
}
|
||||
|
||||
static void preload() {
|
||||
preloadClasses();
|
||||
preloadResources();
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs Zygote process initialization. Loads and initializes
|
||||
* commonly used classes.
|
||||
@@ -494,11 +501,20 @@ public class ZygoteInit {
|
||||
|
||||
closeServerSocket();
|
||||
|
||||
/*
|
||||
* Pass the remaining arguments to SystemServer.
|
||||
* "--nice-name=system_server com.android.server.SystemServer"
|
||||
*/
|
||||
RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
|
||||
if (parsedArgs.niceName != null) {
|
||||
Process.setArgV0(parsedArgs.niceName);
|
||||
}
|
||||
|
||||
if (parsedArgs.invokeWith != null) {
|
||||
WrapperInit.execApplication(parsedArgs.invokeWith,
|
||||
parsedArgs.niceName, null, parsedArgs.remainingArgs);
|
||||
} else {
|
||||
/*
|
||||
* Pass the remaining arguments to SystemServer.
|
||||
*/
|
||||
RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
|
||||
}
|
||||
|
||||
/* should never reach here */
|
||||
}
|
||||
|
||||
@@ -523,20 +539,13 @@ public class ZygoteInit {
|
||||
|
||||
try {
|
||||
parsedArgs = new ZygoteConnection.Arguments(args);
|
||||
|
||||
/*
|
||||
* Enable debugging of the system process if *either* the command line flags
|
||||
* indicate it should be debuggable or the ro.debuggable system property
|
||||
* is set to "1"
|
||||
*/
|
||||
int debugFlags = parsedArgs.debugFlags;
|
||||
if ("1".equals(SystemProperties.get("ro.debuggable")))
|
||||
debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;
|
||||
ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
|
||||
ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
|
||||
|
||||
/* Request to fork the system server process */
|
||||
pid = Zygote.forkSystemServer(
|
||||
parsedArgs.uid, parsedArgs.gid,
|
||||
parsedArgs.gids, debugFlags, null,
|
||||
parsedArgs.gids, parsedArgs.debugFlags, null,
|
||||
parsedArgs.permittedCapabilities,
|
||||
parsedArgs.effectiveCapabilities);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
@@ -561,9 +570,7 @@ public class ZygoteInit {
|
||||
registerZygoteSocket();
|
||||
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
|
||||
SystemClock.uptimeMillis());
|
||||
preloadClasses();
|
||||
//cacheRegisterMaps();
|
||||
preloadResources();
|
||||
preload();
|
||||
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
|
||||
SystemClock.uptimeMillis());
|
||||
|
||||
@@ -578,9 +585,9 @@ public class ZygoteInit {
|
||||
throw new RuntimeException(argv[0] + USAGE_STRING);
|
||||
}
|
||||
|
||||
if (argv[1].equals("true")) {
|
||||
if (argv[1].equals("start-system-server")) {
|
||||
startSystemServer();
|
||||
} else if (!argv[1].equals("false")) {
|
||||
} else if (!argv[1].equals("")) {
|
||||
throw new RuntimeException(argv[0] + USAGE_STRING);
|
||||
}
|
||||
|
||||
@@ -760,6 +767,13 @@ public class ZygoteInit {
|
||||
static native void closeDescriptor(FileDescriptor fd)
|
||||
throws IOException;
|
||||
|
||||
static void closeDescriptorQuietly(FileDescriptor fd) {
|
||||
try {
|
||||
closeDescriptor(fd);
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the close-on-exec flag for the specified file descriptor.
|
||||
*
|
||||
@@ -810,6 +824,21 @@ public class ZygoteInit {
|
||||
static native FileDescriptor createFileDescriptor(int fd)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Gets the integer value of a file descriptor.
|
||||
*
|
||||
* @param fd non-null; file descriptor
|
||||
* @return integer OS file descriptor
|
||||
*/
|
||||
static native int getFDFromFileDescriptor(FileDescriptor fd);
|
||||
|
||||
/**
|
||||
* Creates a pipe.
|
||||
*
|
||||
* @param outFds non-null; an array to hold two file descriptors, initially null
|
||||
*/
|
||||
static native void pipe(FileDescriptor[] outFds) throws IOException;
|
||||
|
||||
/**
|
||||
* Class not instantiable.
|
||||
*/
|
||||
|
||||
@@ -879,8 +879,11 @@ bail:
|
||||
* Start the Android runtime. This involves starting the virtual machine
|
||||
* and calling the "static void main(String[] args)" method in the class
|
||||
* named by "className".
|
||||
*
|
||||
* Passes the main function two arguments, the class name and the specified
|
||||
* options string.
|
||||
*/
|
||||
void AndroidRuntime::start(const char* className, const bool startSystemServer)
|
||||
void AndroidRuntime::start(const char* className, const char* options)
|
||||
{
|
||||
LOGD("\n>>>>>> AndroidRuntime START %s <<<<<<\n",
|
||||
className != NULL ? className : "(unknown)");
|
||||
@@ -895,7 +898,7 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer)
|
||||
* 'startSystemServer == true' means runtime is obslete and not run from
|
||||
* init.rc anymore, so we print out the boot start event here.
|
||||
*/
|
||||
if (startSystemServer) {
|
||||
if (strcmp(options, "start-system-server") == 0) {
|
||||
/* track our progress through the boot sequence */
|
||||
const int LOG_BOOT_PROGRESS_START = 3000;
|
||||
LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,
|
||||
@@ -929,13 +932,13 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer)
|
||||
|
||||
/*
|
||||
* We want to call main() with a String array with arguments in it.
|
||||
* At present we only have one argument, the class name. Create an
|
||||
* array to hold it.
|
||||
* At present we have two arguments, the class name and an option string.
|
||||
* Create an array to hold them.
|
||||
*/
|
||||
jclass stringClass;
|
||||
jobjectArray strArray;
|
||||
jstring classNameStr;
|
||||
jstring startSystemServerStr;
|
||||
jstring optionsStr;
|
||||
|
||||
stringClass = env->FindClass("java/lang/String");
|
||||
assert(stringClass != NULL);
|
||||
@@ -944,9 +947,8 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer)
|
||||
classNameStr = env->NewStringUTF(className);
|
||||
assert(classNameStr != NULL);
|
||||
env->SetObjectArrayElement(strArray, 0, classNameStr);
|
||||
startSystemServerStr = env->NewStringUTF(startSystemServer ?
|
||||
"true" : "false");
|
||||
env->SetObjectArrayElement(strArray, 1, startSystemServerStr);
|
||||
optionsStr = env->NewStringUTF(options);
|
||||
env->SetObjectArrayElement(strArray, 1, optionsStr);
|
||||
|
||||
/*
|
||||
* Start VM. This thread becomes the main thread of the VM, and will
|
||||
@@ -990,12 +992,6 @@ bail:
|
||||
free(slashClassName);
|
||||
}
|
||||
|
||||
void AndroidRuntime::start()
|
||||
{
|
||||
start("com.android.internal.os.RuntimeInit",
|
||||
false /* Don't start the system server */);
|
||||
}
|
||||
|
||||
void AndroidRuntime::onExit(int code)
|
||||
{
|
||||
LOGV("AndroidRuntime onExit calling exit(%d)", code);
|
||||
|
||||
@@ -145,6 +145,10 @@ static void com_android_internal_os_ZygoteInit_reopenStdio(JNIEnv* env,
|
||||
static void com_android_internal_os_ZygoteInit_closeDescriptor(JNIEnv* env,
|
||||
jobject clazz, jobject descriptor)
|
||||
{
|
||||
if (!descriptor) {
|
||||
return;
|
||||
}
|
||||
|
||||
int fd;
|
||||
int err;
|
||||
|
||||
@@ -327,6 +331,30 @@ static jobject com_android_internal_os_ZygoteInit_createFileDescriptor (
|
||||
return jniCreateFileDescriptor(env, fd);
|
||||
}
|
||||
|
||||
static jint com_android_internal_os_ZygoteInit_getFDFromFileDescriptor (
|
||||
JNIEnv *env, jobject clazz, jobject fd)
|
||||
{
|
||||
return jniGetFDFromFileDescriptor(env, fd);
|
||||
}
|
||||
|
||||
static void com_android_internal_os_ZygoteInit_pipe (
|
||||
JNIEnv *env, jobject clazz, jobjectArray outFds)
|
||||
{
|
||||
int fds[2];
|
||||
if (pipe(fds) < 0) {
|
||||
jniThrowIOException(env, errno);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
jobject fdObj = jniCreateFileDescriptor(env, fds[i]);
|
||||
if (env->ExceptionCheck()) {
|
||||
return;
|
||||
}
|
||||
env->SetObjectArrayElement(outFds, i, fdObj);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* JNI registration.
|
||||
*/
|
||||
@@ -355,7 +383,11 @@ static JNINativeMethod gMethods[] = {
|
||||
{ "selectReadable", "([Ljava/io/FileDescriptor;)I",
|
||||
(void *) com_android_internal_os_ZygoteInit_selectReadable },
|
||||
{ "createFileDescriptor", "(I)Ljava/io/FileDescriptor;",
|
||||
(void *) com_android_internal_os_ZygoteInit_createFileDescriptor }
|
||||
(void *) com_android_internal_os_ZygoteInit_createFileDescriptor },
|
||||
{ "getFDFromFileDescriptor", "(Ljava/io/FileDescriptor;)I",
|
||||
(void *) com_android_internal_os_ZygoteInit_getFDFromFileDescriptor },
|
||||
{ "pipe", "([Ljava/io/FileDescriptor;)V",
|
||||
(void *) com_android_internal_os_ZygoteInit_pipe },
|
||||
};
|
||||
int register_com_android_internal_os_ZygoteInit(JNIEnv* env)
|
||||
{
|
||||
|
||||
@@ -37,6 +37,13 @@ public:
|
||||
AndroidRuntime();
|
||||
virtual ~AndroidRuntime();
|
||||
|
||||
enum StartMode {
|
||||
Zygote,
|
||||
SystemServer,
|
||||
Application,
|
||||
Tool,
|
||||
};
|
||||
|
||||
/**
|
||||
* Register a set of methods in the specified class.
|
||||
*/
|
||||
@@ -61,8 +68,7 @@ public:
|
||||
|
||||
int addVmArguments(int argc, const char* const argv[]);
|
||||
|
||||
void start(const char *classname, const bool startSystemServer);
|
||||
void start(); // start in android.util.RuntimeInit
|
||||
void start(const char *classname, const char* options);
|
||||
|
||||
static AndroidRuntime* getRuntime();
|
||||
|
||||
|
||||
@@ -203,6 +203,12 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
// before we decide it's never going to come up for real.
|
||||
static final int PROC_START_TIMEOUT = 10*1000;
|
||||
|
||||
// How long we wait for a launched process to attach to the activity manager
|
||||
// before we decide it's never going to come up for real, when the process was
|
||||
// started with a wrapper for instrumentation (such as Valgrind) because it
|
||||
// could take much longer than usual.
|
||||
static final int PROC_START_TIMEOUT_WITH_WRAPPER = 300*1000;
|
||||
|
||||
// How long to wait after going idle before forcing apps to GC.
|
||||
static final int GC_TIMEOUT = 5*1000;
|
||||
|
||||
@@ -841,14 +847,6 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
* Current sequence id for process LRU updating.
|
||||
*/
|
||||
int mLruSeq = 0;
|
||||
|
||||
/**
|
||||
* Set to true if the ANDROID_SIMPLE_PROCESS_MANAGEMENT envvar
|
||||
* is set, indicating the user wants processes started in such a way
|
||||
* that they can use ANDROID_PROCESS_WRAPPER and know what will be
|
||||
* running in each process (thus no pre-initialized process, etc).
|
||||
*/
|
||||
boolean mSimpleProcessManagement = false;
|
||||
|
||||
/**
|
||||
* System monitoring: number of processes that died since the last
|
||||
@@ -1395,15 +1393,6 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
}
|
||||
|
||||
private ActivityManagerService() {
|
||||
String v = System.getenv("ANDROID_SIMPLE_PROCESS_MANAGEMENT");
|
||||
if (v != null && Integer.getInteger(v) != 0) {
|
||||
mSimpleProcessManagement = true;
|
||||
}
|
||||
v = System.getenv("ANDROID_DEBUG_APP");
|
||||
if (v != null) {
|
||||
mSimpleProcessManagement = true;
|
||||
}
|
||||
|
||||
Slog.i(TAG, "Memory class: " + ActivityManager.staticGetMemoryClass());
|
||||
|
||||
File dataDir = Environment.getDataDirectory();
|
||||
@@ -1872,9 +1861,11 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
if ("1".equals(SystemProperties.get("debug.assert"))) {
|
||||
debugFlags |= Zygote.DEBUG_ENABLE_ASSERT;
|
||||
}
|
||||
int pid = Process.start("android.app.ActivityThread",
|
||||
mSimpleProcessManagement ? app.processName : null, uid, uid,
|
||||
gids, debugFlags, null);
|
||||
|
||||
// Start the process. It will either succeed and return a result containing
|
||||
// the PID of the new process, or else throw a RuntimeException.
|
||||
Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread",
|
||||
app.processName, uid, uid, gids, debugFlags, null);
|
||||
BatteryStatsImpl bs = app.batteryStats.getBatteryStats();
|
||||
synchronized (bs) {
|
||||
if (bs.isOnBattery()) {
|
||||
@@ -1882,12 +1873,12 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
}
|
||||
}
|
||||
|
||||
EventLog.writeEvent(EventLogTags.AM_PROC_START, pid, uid,
|
||||
EventLog.writeEvent(EventLogTags.AM_PROC_START, startResult.pid, uid,
|
||||
app.processName, hostingType,
|
||||
hostingNameStr != null ? hostingNameStr : "");
|
||||
|
||||
if (app.persistent) {
|
||||
Watchdog.getInstance().processStarted(app.processName, pid);
|
||||
Watchdog.getInstance().processStarted(app.processName, startResult.pid);
|
||||
}
|
||||
|
||||
StringBuilder buf = mStringBuilder;
|
||||
@@ -1901,7 +1892,7 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
buf.append(hostingNameStr);
|
||||
}
|
||||
buf.append(": pid=");
|
||||
buf.append(pid);
|
||||
buf.append(startResult.pid);
|
||||
buf.append(" uid=");
|
||||
buf.append(uid);
|
||||
buf.append(" gids={");
|
||||
@@ -1914,26 +1905,22 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
}
|
||||
buf.append("}");
|
||||
Slog.i(TAG, buf.toString());
|
||||
if (pid == 0 || pid == MY_PID) {
|
||||
if (startResult.pid == 0 || startResult.pid == MY_PID) {
|
||||
// Processes are being emulated with threads.
|
||||
app.pid = MY_PID;
|
||||
app.removed = false;
|
||||
mStartingProcesses.add(app);
|
||||
} else if (pid > 0) {
|
||||
app.pid = pid;
|
||||
} else {
|
||||
app.pid = startResult.pid;
|
||||
app.usingWrapper = startResult.usingWrapper;
|
||||
app.removed = false;
|
||||
synchronized (mPidsSelfLocked) {
|
||||
this.mPidsSelfLocked.put(pid, app);
|
||||
this.mPidsSelfLocked.put(startResult.pid, app);
|
||||
Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
|
||||
msg.obj = app;
|
||||
mHandler.sendMessageDelayed(msg, PROC_START_TIMEOUT);
|
||||
mHandler.sendMessageDelayed(msg, startResult.usingWrapper
|
||||
? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
|
||||
}
|
||||
} else {
|
||||
app.pid = 0;
|
||||
RuntimeException e = new RuntimeException(
|
||||
"Failure starting process " + app.processName
|
||||
+ ": returned pid=" + pid);
|
||||
Slog.e(TAG, e.getMessage(), e);
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
// XXX do better error recovery.
|
||||
|
||||
@@ -554,12 +554,12 @@ class ActivityRecord extends IApplicationToken.Stub {
|
||||
public long getKeyDispatchingTimeout() {
|
||||
synchronized(service) {
|
||||
ActivityRecord r = getWaitingHistoryRecordLocked();
|
||||
if (r == null || r.app == null
|
||||
|| r.app.instrumentationClass == null) {
|
||||
return ActivityManagerService.KEY_DISPATCHING_TIMEOUT;
|
||||
if (r != null && r.app != null
|
||||
&& (r.app.instrumentationClass != null || r.app.usingWrapper)) {
|
||||
return ActivityManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT;
|
||||
}
|
||||
|
||||
return ActivityManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT;
|
||||
|
||||
return ActivityManagerService.KEY_DISPATCHING_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -74,6 +74,7 @@ class ProcessRecord {
|
||||
IInstrumentationWatcher instrumentationWatcher; // who is waiting
|
||||
Bundle instrumentationArguments;// as given to us
|
||||
ComponentName instrumentationResultClass;// copy of instrumentationClass
|
||||
boolean usingWrapper; // Set to true when process was launched with a wrapper attached
|
||||
BroadcastRecord curReceiver;// receiver currently running in the app
|
||||
long lastWakeTime; // How long proc held wake lock at last check
|
||||
long lastCpuTime; // How long proc has run CPU at last check
|
||||
|
||||
Reference in New Issue
Block a user