Merge "jni: liblog reading error API incorrect"
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp
index bdbb08c..3e8d6a0 100644
--- a/cmds/app_process/app_main.cpp
+++ b/cmds/app_process/app_main.cpp
@@ -10,8 +10,9 @@
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <utils/Log.h>
-#include <cutils/process_name.h>
#include <cutils/memory.h>
+#include <cutils/process_name.h>
+#include <cutils/properties.h>
#include <cutils/trace.h>
#include <android_runtime/AndroidRuntime.h>
@@ -32,30 +33,20 @@
public:
AppRuntime(char* argBlockStart, const size_t argBlockLength)
: AndroidRuntime(argBlockStart, argBlockLength)
- , mParentDir(NULL)
- , mClassName(NULL)
, mClass(NULL)
- , mArgC(0)
- , mArgV(NULL)
{
}
-#if 0
- // this appears to be unused
- const char* getParentDir() const
- {
- return mParentDir;
- }
-#endif
-
- const char* getClassName() const
- {
- return mClassName;
+ void setClassNameAndArgs(const String8& className, int argc, char * const *argv) {
+ mClassName = className;
+ for (int i = 0; i < argc; ++i) {
+ mArgs.add(String8(argv[i]));
+ }
}
virtual void onVmCreated(JNIEnv* env)
{
- if (mClassName == NULL) {
+ if (mClassName.isEmpty()) {
return; // Zygote. Nothing to do here.
}
@@ -72,10 +63,10 @@
* executing boot class Java code and thereby deny ourselves access to
* non-boot classes.
*/
- char* slashClassName = toSlashClassName(mClassName);
+ char* slashClassName = toSlashClassName(mClassName.string());
mClass = env->FindClass(slashClassName);
if (mClass == NULL) {
- ALOGE("ERROR: could not find class '%s'\n", mClassName);
+ ALOGE("ERROR: could not find class '%s'\n", mClassName.string());
}
free(slashClassName);
@@ -89,7 +80,7 @@
proc->startThreadPool();
AndroidRuntime* ar = AndroidRuntime::getRuntime();
- ar->callMain(mClassName, mClass, mArgC, mArgV);
+ ar->callMain(mClassName, mClass, mArgs);
IPCThreadState::self()->stopProcess();
}
@@ -115,11 +106,9 @@
}
- const char* mParentDir;
- const char* mClassName;
+ String8 mClassName;
+ Vector<String8> mArgs;
jclass mClass;
- int mArgC;
- const char* const* mArgV;
};
}
@@ -147,6 +136,12 @@
return (end - start);
}
+#if defined(__LP64__)
+static const char ABI_LIST_PROPERTY[] = "ro.product.cpu.abilist64";
+#else
+static const char ABI_LIST_PROPERTY[] = "ro.product.cpu.abilist32";
+#endif
+
int main(int argc, char* const argv[])
{
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
@@ -155,7 +150,26 @@
argc--;
argv++;
- // Everything up to '--' or first non '-' arg goes to the vm
+ // Everything up to '--' or first non '-' arg goes to the vm.
+ //
+ // The first argument after the VM args is the "parent dir", which
+ // is currently unused.
+ //
+ // After the parent dir, we expect one or more the following internal
+ // arguments :
+ //
+ // --zygote : Start in zygote mode
+ // --start-system-server : Start the system server.
+ // --application : Start in application (stand alone, non zygote) mode.
+ // --nice-name : The nice name for this process.
+ //
+ // For non zygote starts, these arguments will be followed by
+ // the main class name. All remaining arguments are passed to
+ // the main method of this class.
+ //
+ // For zygote starts, all remaining arguments are passed to the zygote.
+ // main function.
+
int i = runtime.addVmArguments(argc, argv);
@@ -163,14 +177,13 @@
bool zygote = false;
bool startSystemServer = false;
bool application = false;
- const char* parentDir = NULL;
const char* niceName = NULL;
- const char* className = NULL;
+ String8 className;
+
+ ++i; // Skip unused "parent dir" argument.
while (i < argc) {
const char* arg = argv[i++];
- if (!parentDir) {
- parentDir = arg;
- } else if (strcmp(arg, "--zygote") == 0) {
+ if (strcmp(arg, "--zygote") == 0) {
zygote = true;
niceName = "zygote";
} else if (strcmp(arg, "--start-system-server") == 0) {
@@ -180,28 +193,52 @@
} else if (strncmp(arg, "--nice-name=", 12) == 0) {
niceName = arg + 12;
} else {
- className = arg;
+ className.setTo(arg);
break;
}
}
+ Vector<String8> args;
+ if (!className.isEmpty()) {
+ // We're not in zygote mode, the only argument we need to pass
+ // to RuntimeInit is the application argument.
+ //
+ // The Remainder of args get passed to startup class main(). Make
+ // copies of them before we overwrite them with the process name.
+ args.add(application ? String8("application") : String8("tool"));
+ runtime.setClassNameAndArgs(className, argc - i, argv + i);
+ } else {
+ if (startSystemServer) {
+ args.add(String8("start-system-server"));
+ }
+
+ char prop[PROP_VALUE_MAX];
+ if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) {
+ LOG_ALWAYS_FATAL("app_process: Unable to deterimine ABI list from property %s.",
+ ABI_LIST_PROPERTY);
+ return 11;
+ }
+
+ String8 abiFlag("--abi-list=");
+ abiFlag.append(prop);
+ args.add(abiFlag);
+
+ // In zygote mode, pass all remaining arguments to the zygote
+ // main() method.
+ for (; i < argc; ++i) {
+ args.add(String8(argv[i]));
+ }
+ }
+
if (niceName && *niceName) {
runtime.setArgv0(niceName);
set_process_name(niceName);
}
- runtime.mParentDir = parentDir;
-
if (zygote) {
- runtime.start("com.android.internal.os.ZygoteInit",
- startSystemServer ? "start-system-server" : "");
+ runtime.start("com.android.internal.os.ZygoteInit", args);
} 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");
+ runtime.start("com.android.internal.os.RuntimeInit", args);
} else {
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
app_usage();
diff --git a/core/java/android/os/MemoryFile.java b/core/java/android/os/MemoryFile.java
index ee7a4c6..6cec55a 100644
--- a/core/java/android/os/MemoryFile.java
+++ b/core/java/android/os/MemoryFile.java
@@ -63,12 +63,17 @@
* Allocates a new ashmem region. The region is initially not purgable.
*
* @param name optional name for the file (can be null).
- * @param length of the memory file in bytes.
+ * @param length of the memory file in bytes, must be non-negative.
* @throws IOException if the memory file could not be created.
*/
public MemoryFile(String name, int length) throws IOException {
mLength = length;
- mFD = native_open(name, length);
+ if (length >= 0) {
+ mFD = native_open(name, length);
+ } else {
+ throw new IOException("Invalid length: " + length);
+ }
+
if (length > 0) {
mAddress = native_mmap(mFD, length, PROT_READ | PROT_WRITE);
} else {
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 4cf8767..7d2d051 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -19,15 +19,15 @@
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.util.Log;
-
import com.android.internal.os.Zygote;
-
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
-
+import java.util.Arrays;
+import java.util.List;
import libcore.io.Libcore;
/*package*/ class ZygoteStartFailedEx extends Exception {
@@ -48,17 +48,7 @@
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";
+ private static final String SECONDARY_ZYGOTE_SOCKET = "zygote_secondary";
/**
* Defines the UID/GID under which system code runs.
@@ -345,15 +335,112 @@
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;
+ /**
+ * State for communicating with the zygote process.
+ */
+ static class ZygoteState {
+ final LocalSocket socket;
+ final DataInputStream inputStream;
+ final BufferedWriter writer;
+ final List<String> abiList;
- /** true if previous zygote open failed */
- static boolean sPreviousZygoteOpenFailed;
+ boolean mClosed;
+
+ private ZygoteState(LocalSocket socket, DataInputStream inputStream,
+ BufferedWriter writer, List<String> abiList) {
+ this.socket = socket;
+ this.inputStream = inputStream;
+ this.writer = writer;
+ this.abiList = abiList;
+ }
+
+ static ZygoteState connect(String socketAddress, int tries) throws ZygoteStartFailedEx {
+ LocalSocket zygoteSocket = null;
+ DataInputStream zygoteInputStream = null;
+ BufferedWriter zygoteWriter = null;
+
+ /*
+ * 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...
+ *
+ * TODO: This bug was filed in 2007. Get rid of this code. The zygote
+ * forks the system_server so it shouldn't be possible for the zygote
+ * socket to be brought up after the system_server is.
+ */
+ for (int i = 0; i < tries; i++) {
+ if (i > 0) {
+ try {
+ Log.i("Zygote", "Zygote not up yet, sleeping...");
+ Thread.sleep(ZYGOTE_RETRY_MILLIS);
+ } catch (InterruptedException ex) {
+ throw new ZygoteStartFailedEx(ex);
+ }
+ }
+
+ try {
+ zygoteSocket = new LocalSocket();
+ zygoteSocket.connect(new LocalSocketAddress(socketAddress,
+ LocalSocketAddress.Namespace.RESERVED));
+
+ zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream());
+
+ zygoteWriter = new BufferedWriter(new OutputStreamWriter(
+ zygoteSocket.getOutputStream()), 256);
+ break;
+ } catch (IOException ex) {
+ if (zygoteSocket != null) {
+ try {
+ zygoteSocket.close();
+ } catch (IOException ex2) {
+ Log.e(LOG_TAG,"I/O exception on close after exception", ex2);
+ }
+ }
+
+ zygoteSocket = null;
+ }
+ }
+
+ if (zygoteSocket == null) {
+ throw new ZygoteStartFailedEx("connect failed");
+ }
+
+ String abiListString = getAbiList(zygoteWriter, zygoteInputStream);
+ Log.i("Zygote", "Process: zygote socket opened, supported ABIS: " + abiListString);
+
+ return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
+ Arrays.asList(abiListString.split(",")));
+ }
+
+ boolean matches(String abi) {
+ return abiList.contains(abi);
+ }
+
+ void close() {
+ try {
+ socket.close();
+ } catch (IOException ex) {
+ Log.e(LOG_TAG,"I/O exception on routine close", ex);
+ }
+
+ mClosed = true;
+ }
+
+ boolean isClosed() {
+ return mClosed;
+ }
+ }
+
+ /**
+ * The state of the connection to the primary zygote.
+ */
+ static ZygoteState primaryZygoteState;
+
+ /**
+ * The state of the connection to the secondary zygote.
+ */
+ static ZygoteState secondaryZygoteState;
/**
* Start a new process.
@@ -395,7 +482,9 @@
String[] zygoteArgs) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
- debugFlags, mountExternal, targetSdkVersion, seInfo, zygoteArgs);
+ debugFlags, mountExternal, targetSdkVersion, seInfo,
+ null, /* zygoteAbi TODO: Replace this with the real ABI */
+ zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
@@ -408,78 +497,31 @@
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.
+ * Queries the zygote for the list of ABIS it supports.
+ *
+ * @throws ZygoteStartFailedEx if the query failed.
*/
- private static void openZygoteSocketIfNeeded()
+ private static String getAbiList(BufferedWriter writer, DataInputStream inputStream)
throws ZygoteStartFailedEx {
+ try {
- int retryCount;
+ // Each query starts with the argument count (1 in this case)
+ writer.write("1");
+ // ... followed by a new-line.
+ writer.newLine();
+ // ... followed by our only argument.
+ writer.write("--query-abi-list");
+ writer.newLine();
+ writer.flush();
- if (sPreviousZygoteOpenFailed) {
- /*
- * If we've failed before, expect that we'll fail again and
- * don't pause for retries.
- */
- retryCount = 0;
- } else {
- retryCount = 10;
- }
+ // The response is a length prefixed stream of ASCII bytes.
+ int numBytes = inputStream.readInt();
+ byte[] bytes = new byte[numBytes];
+ inputStream.readFully(bytes);
- /*
- * 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");
+ return new String(bytes, StandardCharsets.US_ASCII);
+ } catch (IOException ioe) {
+ throw new ZygoteStartFailedEx(ioe);
}
}
@@ -487,14 +529,12 @@
* 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 An object that describes the result of the attempt to start the process.
+ *
* @throws ZygoteStartFailedEx if process start failed for any reason
*/
- private static ProcessStartResult zygoteSendArgsAndGetResult(ArrayList<String> args)
+ private static ProcessStartResult zygoteSendArgsAndGetResult(
+ ZygoteState zygoteState, ArrayList<String> args)
throws ZygoteStartFailedEx {
- openZygoteSocketIfNeeded();
-
try {
/**
* See com.android.internal.os.ZygoteInit.readArgumentList()
@@ -506,9 +546,11 @@
* the child or -1 on failure, followed by boolean to
* indicate whether a wrapper process was used.
*/
+ final BufferedWriter writer = zygoteState.writer;
+ final DataInputStream inputStream = zygoteState.inputStream;
- sZygoteWriter.write(Integer.toString(args.size()));
- sZygoteWriter.newLine();
+ writer.write(Integer.toString(args.size()));
+ writer.newLine();
int sz = args.size();
for (int i = 0; i < sz; i++) {
@@ -517,32 +559,22 @@
throw new ZygoteStartFailedEx(
"embedded newlines not allowed");
}
- sZygoteWriter.write(arg);
- sZygoteWriter.newLine();
+ writer.write(arg);
+ writer.newLine();
}
- sZygoteWriter.flush();
+ writer.flush();
// Should there be a timeout on this?
ProcessStartResult result = new ProcessStartResult();
- result.pid = sZygoteInputStream.readInt();
+ result.pid = inputStream.readInt();
if (result.pid < 0) {
throw new ZygoteStartFailedEx("fork() failed");
}
- result.usingWrapper = sZygoteInputStream.readBoolean();
+ result.usingWrapper = inputStream.readBoolean();
return result;
} catch (IOException ex) {
- try {
- if (sZygoteSocket != null) {
- sZygoteSocket.close();
- }
- } catch (IOException ex2) {
- // we're going to fail anyway
- Log.e(LOG_TAG,"I/O exception on routine close", ex2);
- }
-
- sZygoteSocket = null;
-
+ zygoteState.close();
throw new ZygoteStartFailedEx(ex);
}
}
@@ -559,6 +591,7 @@
* @param debugFlags Additional flags.
* @param targetSdkVersion The target SDK version for the app.
* @param seInfo null-ok SELinux information for the new process.
+ * @param abi the ABI the process should use.
* @param extraArgs Additional arguments to supply to the zygote process.
* @return An object that describes the result of the attempt to start the process.
* @throws ZygoteStartFailedEx if process start failed for any reason
@@ -570,6 +603,7 @@
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
+ String abi,
String[] extraArgs)
throws ZygoteStartFailedEx {
synchronized(Process.class) {
@@ -637,10 +671,61 @@
}
}
- return zygoteSendArgsAndGetResult(argsForZygote);
+ return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
}
}
-
+
+ /**
+ * Returns the number of times we attempt a connection to the zygote. We
+ * sleep for {@link #ZYGOTE_RETRY_MILLIS} milliseconds between each try.
+ *
+ * This could probably be removed, see TODO in {@code ZygoteState#connect}.
+ */
+ private static int getNumTries(ZygoteState state) {
+ // Retry 10 times for the first connection to each zygote.
+ if (state == null) {
+ return 11;
+ }
+
+ // This means the connection has already been established, but subsequently
+ // closed, possibly due to an IOException. We retry just once if that's the
+ // case.
+ return 1;
+ }
+
+ /**
+ * Tries to open socket to Zygote process if not already open. If
+ * already open, does nothing. May block and retry.
+ */
+ private static ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
+ if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
+ primaryZygoteState = ZygoteState.connect(ZYGOTE_SOCKET, getNumTries(primaryZygoteState));
+ }
+
+ // TODO: Revert this temporary change. This is required to test
+ // and submit this change ahead of the package manager changes
+ // that supply this abi.
+ if (abi == null) {
+ return primaryZygoteState;
+ }
+
+ if (primaryZygoteState.matches(abi)) {
+ return primaryZygoteState;
+ }
+
+ // The primary zygote didn't match. Try the secondary.
+ if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
+ secondaryZygoteState = ZygoteState.connect(SECONDARY_ZYGOTE_SOCKET,
+ getNumTries(secondaryZygoteState));
+ }
+
+ if (secondaryZygoteState.matches(abi)) {
+ return secondaryZygoteState;
+ }
+
+ throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);
+ }
+
/**
* Returns elapsed milliseconds of the time this process has run.
* @return Returns the number of milliseconds this process has return.
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index aad534c..58a8e62 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -22,9 +22,7 @@
import android.os.SELinux;
import android.os.SystemProperties;
import android.util.Log;
-
import dalvik.system.PathClassLoader;
-
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@@ -34,8 +32,8 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
-
import libcore.io.ErrnoException;
import libcore.io.IoUtils;
import libcore.io.Libcore;
@@ -59,7 +57,7 @@
private static final int CONNECTION_TIMEOUT_MILLIS = 1000;
/** max number of arguments that a connection can specify */
- private static final int MAX_ZYGOTE_ARGC=1024;
+ private static final int MAX_ZYGOTE_ARGC = 1024;
/**
* The command socket.
@@ -73,15 +71,18 @@
private final BufferedReader mSocketReader;
private final Credentials peer;
private final String peerSecurityContext;
+ private final String abiList;
/**
* Constructs instance from connected socket.
*
* @param socket non-null; connected socket
+ * @param abiList non-null; a list of ABIs this zygote supports.
* @throws IOException
*/
- ZygoteConnection(LocalSocket socket) throws IOException {
+ ZygoteConnection(LocalSocket socket, String abiList) throws IOException {
mSocket = socket;
+ this.abiList = abiList;
mSocketOutStream
= new DataOutputStream(socket.getOutputStream());
@@ -111,43 +112,6 @@
}
/**
- * Reads start commands from an open command socket.
- * Start commands are presently a pair of newline-delimited lines
- * indicating a) class to invoke main() on b) nice name to set argv[0] to.
- * Continues to read commands and forkAndSpecialize children until
- * the socket is closed. This method is used in ZYGOTE_FORK_MODE
- *
- * @throws ZygoteInit.MethodAndArgsCaller trampoline to invoke main()
- * method in child process
- */
- void run() throws ZygoteInit.MethodAndArgsCaller {
-
- int loopCount = ZygoteInit.GC_LOOP_COUNT;
-
- while (true) {
- /*
- * Call gc() before we block in readArgumentList().
- * It's work that has to be done anyway, and it's better
- * to avoid making every child do it. It will also
- * madvise() any free memory as a side-effect.
- *
- * Don't call it every time, because walking the entire
- * heap is a lot of overhead to free a few hundred bytes.
- */
- if (loopCount <= 0) {
- ZygoteInit.gc();
- loopCount = ZygoteInit.GC_LOOP_COUNT;
- } else {
- loopCount--;
- }
-
- if (runOnce()) {
- break;
- }
- }
- }
-
- /**
* Reads one start command from the command socket. If successful,
* a child is forked and a {@link ZygoteInit.MethodAndArgsCaller}
* exception is thrown in that child while in the parent process,
@@ -196,6 +160,11 @@
try {
parsedArgs = new Arguments(args);
+
+ if (parsedArgs.abiListQuery) {
+ return handleAbiListQuery();
+ }
+
if (parsedArgs.permittedCapabilities != 0 || parsedArgs.effectiveCapabilities != 0) {
throw new ZygoteSecurityException("Client may not specify capabilities: " +
"permitted=0x" + Long.toHexString(parsedArgs.permittedCapabilities) +
@@ -287,6 +256,18 @@
}
}
+ private boolean handleAbiListQuery() {
+ try {
+ final byte[] abiListBytes = abiList.getBytes(StandardCharsets.US_ASCII);
+ mSocketOutStream.writeInt(abiListBytes.length);
+ mSocketOutStream.write(abiListBytes);
+ return false;
+ } catch (IOException ioe) {
+ Log.e(TAG, "Error writing to command socket", ioe);
+ return true;
+ }
+ }
+
/**
* Closes socket associated with this connection.
*/
@@ -388,6 +369,11 @@
String remainingArgs[];
/**
+ * Whether the current arguments constitute an ABI list query.
+ */
+ boolean abiListQuery;
+
+ /**
* Constructs instance and parses args
* @param args zygote command-line args
* @throws IllegalArgumentException
@@ -540,6 +526,8 @@
mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER;
} else if (arg.equals("--mount-external-multiuser-all")) {
mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER_ALL;
+ } else if (arg.equals("--query-abi-list")) {
+ abiListQuery = true;
} else {
break;
}
@@ -975,7 +963,7 @@
mSocketOutStream.writeInt(pid);
mSocketOutStream.writeBoolean(usingWrapper);
} catch (IOException ex) {
- Log.e(TAG, "Error reading from command socket", ex);
+ Log.e(TAG, "Error writing to command socket", ex);
return true;
}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 5be29b4..495cc5f 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -64,7 +64,7 @@
private static final String PROPERTY_DISABLE_OPENGL_PRELOADING = "ro.zygote.disable_gl_preload";
- private static final String ANDROID_SOCKET_ENV = "ANDROID_SOCKET_zygote";
+ private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
private static final int LOG_BOOT_PROGRESS_PRELOAD_START = 3020;
private static final int LOG_BOOT_PROGRESS_PRELOAD_END = 3030;
@@ -72,8 +72,9 @@
/** when preloading, GC after allocating this many bytes */
private static final int PRELOAD_GC_THRESHOLD = 50000;
- public static final String USAGE_STRING =
- " <\"start-system-server\"|\"\" for startSystemServer>";
+ private static final String ABI_LIST_ARG = "--abi-list=";
+
+ private static final String SOCKET_NAME_ARG = "--socket-name=";
private static LocalServerSocket sServerSocket;
@@ -150,15 +151,15 @@
*
* @throws RuntimeException when open fails
*/
- private static void registerZygoteSocket() {
+ private static void registerZygoteSocket(String socketName) {
if (sServerSocket == null) {
int fileDesc;
+ final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
try {
- String env = System.getenv(ANDROID_SOCKET_ENV);
+ String env = System.getenv(fullSocketName);
fileDesc = Integer.parseInt(env);
} catch (RuntimeException ex) {
- throw new RuntimeException(
- ANDROID_SOCKET_ENV + " unset or invalid", ex);
+ throw new RuntimeException(fullSocketName + " unset or invalid", ex);
}
try {
@@ -175,9 +176,9 @@
* Waits for and accepts a single command connection. Throws
* RuntimeException on failure.
*/
- private static ZygoteConnection acceptCommandPeer() {
+ private static ZygoteConnection acceptCommandPeer(String abiList) {
try {
- return new ZygoteConnection(sServerSocket.accept());
+ return new ZygoteConnection(sServerSocket.accept(), abiList);
} catch (IOException ex) {
throw new RuntimeException(
"IOException during accept()", ex);
@@ -567,7 +568,26 @@
// Start profiling the zygote initialization.
SamplingProfilerIntegration.start();
- registerZygoteSocket();
+ boolean startSystemServer = false;
+ String socketName = "zygote";
+ String abiList = null;
+ for (int i = 1; i < argv.length; i++) {
+ if ("start-system-server".equals(argv[i])) {
+ startSystemServer = true;
+ } else if (argv[i].startsWith(ABI_LIST_ARG)) {
+ abiList = argv[i].substring(ABI_LIST_ARG.length());
+ } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
+ socketName = argv[i].substring(SOCKET_NAME_ARG.length());
+ } else {
+ throw new RuntimeException("Unknown command line argument: " + argv[i]);
+ }
+ }
+
+ if (abiList == null) {
+ throw new RuntimeException("No ABI list supplied.");
+ }
+
+ registerZygoteSocket(socketName);
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
SystemClock.uptimeMillis());
preload();
@@ -584,20 +604,12 @@
// Zygote.
Trace.setTracingEnabled(false);
- // If requested, start system server directly from Zygote
- if (argv.length != 2) {
- throw new RuntimeException(argv[0] + USAGE_STRING);
- }
-
- if (argv[1].equals("start-system-server")) {
+ if (startSystemServer) {
startSystemServer();
- } else if (!argv[1].equals("")) {
- throw new RuntimeException(argv[0] + USAGE_STRING);
}
Log.i(TAG, "Accepting command socket connections");
-
- runSelectLoop();
+ runSelectLoop(abiList);
closeServerSocket();
} catch (MethodAndArgsCaller caller) {
@@ -617,7 +629,7 @@
* @throws MethodAndArgsCaller in a child process when a main() should
* be executed.
*/
- private static void runSelectLoop() throws MethodAndArgsCaller {
+ private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
FileDescriptor[] fdArray = new FileDescriptor[4];
@@ -656,7 +668,7 @@
if (index < 0) {
throw new RuntimeException("Error in select()");
} else if (index == 0) {
- ZygoteConnection newPeer = acceptCommandPeer();
+ ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index cce4e0d..362a54e 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -270,13 +270,13 @@
strlcpy(mArgBlockStart, argv0, mArgBlockLength);
}
-status_t AndroidRuntime::callMain(const char* className,
- jclass clazz, int argc, const char* const argv[])
+status_t AndroidRuntime::callMain(const String8& className, jclass clazz,
+ const Vector<String8>& args)
{
JNIEnv* env;
jmethodID methodId;
- ALOGD("Calling main entry %s", className);
+ ALOGD("Calling main entry %s", className.string());
env = getJNIEnv();
if (clazz == NULL || env == NULL) {
@@ -285,7 +285,7 @@
methodId = env->GetStaticMethodID(clazz, "main", "([Ljava/lang/String;)V");
if (methodId == NULL) {
- ALOGE("ERROR: could not find method %s.main(String[])\n", className);
+ ALOGE("ERROR: could not find method %s.main(String[])\n", className.string());
return UNKNOWN_ERROR;
}
@@ -296,11 +296,12 @@
jclass stringClass;
jobjectArray strArray;
+ const size_t numArgs = args.size();
stringClass = env->FindClass("java/lang/String");
- strArray = env->NewObjectArray(argc, stringClass, NULL);
+ strArray = env->NewObjectArray(numArgs, stringClass, NULL);
- for (int i = 0; i < argc; i++) {
- jstring argStr = env->NewStringUTF(argv[i]);
+ for (size_t i = 0; i < numArgs; i++) {
+ jstring argStr = env->NewStringUTF(args[i].string());
env->SetObjectArrayElement(strArray, i, argStr);
}
@@ -872,20 +873,23 @@
* Passes the main function two arguments, the class name and the specified
* options string.
*/
-void AndroidRuntime::start(const char* className, const char* options)
+void AndroidRuntime::start(const char* className, const Vector<String8>& options)
{
ALOGD("\n>>>>>> AndroidRuntime START %s <<<<<<\n",
className != NULL ? className : "(unknown)");
+ static const String8 startSystemServer("start-system-server");
+
/*
* 'startSystemServer == true' means runtime is obsolete and not run from
* init.rc anymore, so we print out the boot start event here.
*/
- 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,
- ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
+ for (size_t i = 0; i < options.size(); ++i) {
+ if (options[i] == startSystemServer) {
+ /* track our progress through the boot sequence */
+ const int LOG_BOOT_PROGRESS_START = 3000;
+ LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
+ }
}
const char* rootDir = getenv("ANDROID_ROOT");
@@ -926,17 +930,20 @@
jclass stringClass;
jobjectArray strArray;
jstring classNameStr;
- jstring optionsStr;
stringClass = env->FindClass("java/lang/String");
assert(stringClass != NULL);
- strArray = env->NewObjectArray(2, stringClass, NULL);
+ strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
assert(strArray != NULL);
classNameStr = env->NewStringUTF(className);
assert(classNameStr != NULL);
env->SetObjectArrayElement(strArray, 0, classNameStr);
- optionsStr = env->NewStringUTF(options);
- env->SetObjectArrayElement(strArray, 1, optionsStr);
+
+ for (size_t i = 0; i < options.size(); ++i) {
+ jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
+ assert(optionsStr != NULL);
+ env->SetObjectArrayElement(strArray, i + 1, optionsStr);
+ }
/*
* Start VM. This thread becomes the main thread of the VM, and will
diff --git a/include/android_runtime/AndroidRuntime.h b/include/android_runtime/AndroidRuntime.h
index 6f2af90..3dfdb46 100644
--- a/include/android_runtime/AndroidRuntime.h
+++ b/include/android_runtime/AndroidRuntime.h
@@ -55,8 +55,7 @@
/**
* Call a class's static main method with the given arguments,
*/
- status_t callMain(const char* className, jclass clazz, int argc,
- const char* const argv[]);
+ status_t callMain(const String8& className, jclass clazz, const Vector<String8>& args);
/**
* Find a class, with the input either of the form
@@ -66,7 +65,7 @@
int addVmArguments(int argc, const char* const argv[]);
- void start(const char *classname, const char* options);
+ void start(const char *classname, const Vector<String8>& options);
void exit(int code);