Merge changes Id931d441,I83faf974
* changes: Zygote: Improve logging and error handling during connections. Zygote: Fix race condition on package preloads.
This commit is contained in:
@@ -463,8 +463,8 @@ public class ZygoteProcess {
|
|||||||
* Instructs the zygote to pre-load the classes and native libraries at the given paths
|
* Instructs the zygote to pre-load the classes and native libraries at the given paths
|
||||||
* for the specified abi. Not all zygotes support this function.
|
* for the specified abi. Not all zygotes support this function.
|
||||||
*/
|
*/
|
||||||
public void preloadPackageForAbi(String packagePath, String libsPath, String cacheKey,
|
public boolean preloadPackageForAbi(String packagePath, String libsPath, String cacheKey,
|
||||||
String abi) throws ZygoteStartFailedEx, IOException {
|
String abi) throws ZygoteStartFailedEx, IOException {
|
||||||
synchronized(mLock) {
|
synchronized(mLock) {
|
||||||
ZygoteState state = openZygoteSocketIfNeeded(abi);
|
ZygoteState state = openZygoteSocketIfNeeded(abi);
|
||||||
state.writer.write("4");
|
state.writer.write("4");
|
||||||
@@ -483,6 +483,8 @@ public class ZygoteProcess {
|
|||||||
state.writer.newLine();
|
state.writer.newLine();
|
||||||
|
|
||||||
state.writer.flush();
|
state.writer.flush();
|
||||||
|
|
||||||
|
return (state.inputStream.readInt() == 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import android.util.Slog;
|
|||||||
import com.android.internal.logging.AndroidConfig;
|
import com.android.internal.logging.AndroidConfig;
|
||||||
import com.android.server.NetworkManagementSocketTagger;
|
import com.android.server.NetworkManagementSocketTagger;
|
||||||
import dalvik.system.VMRuntime;
|
import dalvik.system.VMRuntime;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
@@ -228,8 +229,8 @@ public class RuntimeInit {
|
|||||||
* @param argv Argument vector for main()
|
* @param argv Argument vector for main()
|
||||||
* @param classLoader the classLoader to load {@className} with
|
* @param classLoader the classLoader to load {@className} with
|
||||||
*/
|
*/
|
||||||
private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)
|
private static Runnable findStaticMain(String className, String[] argv,
|
||||||
throws Zygote.MethodAndArgsCaller {
|
ClassLoader classLoader) {
|
||||||
Class<?> cl;
|
Class<?> cl;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -263,7 +264,7 @@ public class RuntimeInit {
|
|||||||
* clears up all the stack frames that were required in setting
|
* clears up all the stack frames that were required in setting
|
||||||
* up the process.
|
* up the process.
|
||||||
*/
|
*/
|
||||||
throw new Zygote.MethodAndArgsCaller(m, argv);
|
return new MethodAndArgsCaller(m, argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final void main(String[] argv) {
|
public static final void main(String[] argv) {
|
||||||
@@ -286,8 +287,8 @@ public class RuntimeInit {
|
|||||||
if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");
|
if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
|
protected static Runnable applicationInit(int targetSdkVersion, String[] argv,
|
||||||
throws Zygote.MethodAndArgsCaller {
|
ClassLoader classLoader) {
|
||||||
// If the application calls System.exit(), terminate the process
|
// If the application calls System.exit(), terminate the process
|
||||||
// immediately without running any shutdown hooks. It is not possible to
|
// immediately without running any shutdown hooks. It is not possible to
|
||||||
// shutdown an Android application gracefully. Among other things, the
|
// shutdown an Android application gracefully. Among other things, the
|
||||||
@@ -300,20 +301,13 @@ public class RuntimeInit {
|
|||||||
VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
|
VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
|
||||||
VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
|
VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
|
||||||
|
|
||||||
final Arguments args;
|
final Arguments args = new Arguments(argv);
|
||||||
try {
|
|
||||||
args = new Arguments(argv);
|
|
||||||
} catch (IllegalArgumentException ex) {
|
|
||||||
Slog.e(TAG, ex.getMessage());
|
|
||||||
// let the process exit
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The end of of the RuntimeInit event (see #zygoteInit).
|
// The end of of the RuntimeInit event (see #zygoteInit).
|
||||||
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
|
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
|
||||||
|
|
||||||
// Remaining arguments are passed to the start class's static main
|
// Remaining arguments are passed to the start class's static main
|
||||||
invokeStaticMain(args.startClass, args.startArgs, classLoader);
|
return findStaticMain(args.startClass, args.startArgs, classLoader);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -422,4 +416,37 @@ public class RuntimeInit {
|
|||||||
System.arraycopy(args, curArg, startArgs, 0, startArgs.length);
|
System.arraycopy(args, curArg, startArgs, 0, startArgs.length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class which holds a method and arguments and can call them. This is used as part of
|
||||||
|
* a trampoline to get rid of the initial process setup stack frames.
|
||||||
|
*/
|
||||||
|
static class MethodAndArgsCaller implements Runnable {
|
||||||
|
/** method to call */
|
||||||
|
private final Method mMethod;
|
||||||
|
|
||||||
|
/** argument array */
|
||||||
|
private final String[] mArgs;
|
||||||
|
|
||||||
|
public MethodAndArgsCaller(Method method, String[] args) {
|
||||||
|
mMethod = method;
|
||||||
|
mArgs = args;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
mMethod.invoke(null, new Object[] { mArgs });
|
||||||
|
} catch (IllegalAccessException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
} catch (InvocationTargetException ex) {
|
||||||
|
Throwable cause = ex.getCause();
|
||||||
|
if (cause instanceof RuntimeException) {
|
||||||
|
throw (RuntimeException) cause;
|
||||||
|
} else if (cause instanceof Error) {
|
||||||
|
throw (Error) cause;
|
||||||
|
}
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import android.util.Log;
|
|||||||
import android.webkit.WebViewFactory;
|
import android.webkit.WebViewFactory;
|
||||||
import android.webkit.WebViewFactoryProvider;
|
import android.webkit.WebViewFactoryProvider;
|
||||||
|
|
||||||
|
import java.io.DataOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
@@ -68,8 +69,7 @@ class WebViewZygoteInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean handlePreloadPackage(String packagePath, String libsPath,
|
protected void handlePreloadPackage(String packagePath, String libsPath, String cacheKey) {
|
||||||
String cacheKey) {
|
|
||||||
Log.i(TAG, "Beginning package preload");
|
Log.i(TAG, "Beginning package preload");
|
||||||
// Ask ApplicationLoaders to create and cache a classloader for the WebView APK so that
|
// Ask ApplicationLoaders to create and cache a classloader for the WebView APK so that
|
||||||
// our children will reuse the same classloader instead of creating their own.
|
// our children will reuse the same classloader instead of creating their own.
|
||||||
@@ -87,19 +87,28 @@ class WebViewZygoteInit {
|
|||||||
// Once we have the classloader, look up the WebViewFactoryProvider implementation and
|
// Once we have the classloader, look up the WebViewFactoryProvider implementation and
|
||||||
// call preloadInZygote() on it to give it the opportunity to preload the native library
|
// call preloadInZygote() on it to give it the opportunity to preload the native library
|
||||||
// and perform any other initialisation work that should be shared among the children.
|
// and perform any other initialisation work that should be shared among the children.
|
||||||
|
boolean preloadSucceeded = false;
|
||||||
try {
|
try {
|
||||||
Class<WebViewFactoryProvider> providerClass =
|
Class<WebViewFactoryProvider> providerClass =
|
||||||
WebViewFactory.getWebViewProviderClass(loader);
|
WebViewFactory.getWebViewProviderClass(loader);
|
||||||
Object result = providerClass.getMethod("preloadInZygote").invoke(null);
|
Object result = providerClass.getMethod("preloadInZygote").invoke(null);
|
||||||
if (!((Boolean)result).booleanValue()) {
|
preloadSucceeded = ((Boolean) result).booleanValue();
|
||||||
|
if (!preloadSucceeded) {
|
||||||
Log.e(TAG, "preloadInZygote returned false");
|
Log.e(TAG, "preloadInZygote returned false");
|
||||||
}
|
}
|
||||||
} catch (ClassNotFoundException | NoSuchMethodException | SecurityException |
|
} catch (ClassNotFoundException | NoSuchMethodException | SecurityException |
|
||||||
IllegalAccessException | InvocationTargetException e) {
|
IllegalAccessException | InvocationTargetException e) {
|
||||||
Log.e(TAG, "Exception while preloading package", e);
|
Log.e(TAG, "Exception while preloading package", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
DataOutputStream socketOut = getSocketOutputStream();
|
||||||
|
socketOut.writeInt(preloadSucceeded ? 1 : 0);
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
throw new IllegalStateException("Error writing to command socket", ioe);
|
||||||
|
}
|
||||||
|
|
||||||
Log.i(TAG, "Package preload done");
|
Log.i(TAG, "Package preload done");
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,16 +122,23 @@ class WebViewZygoteInit {
|
|||||||
throw new RuntimeException("Failed to setpgid(0,0)", ex);
|
throw new RuntimeException("Failed to setpgid(0,0)", ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final Runnable caller;
|
||||||
try {
|
try {
|
||||||
sServer.registerServerSocket("webview_zygote");
|
sServer.registerServerSocket("webview_zygote");
|
||||||
sServer.runSelectLoop(TextUtils.join(",", Build.SUPPORTED_ABIS));
|
// The select loop returns early in the child process after a fork and
|
||||||
sServer.closeServerSocket();
|
// loops forever in the zygote.
|
||||||
} catch (Zygote.MethodAndArgsCaller caller) {
|
caller = sServer.runSelectLoop(TextUtils.join(",", Build.SUPPORTED_ABIS));
|
||||||
caller.run();
|
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
Log.e(TAG, "Fatal exception:", e);
|
Log.e(TAG, "Fatal exception:", e);
|
||||||
|
throw e;
|
||||||
|
} finally {
|
||||||
|
sServer.closeServerSocket();
|
||||||
}
|
}
|
||||||
|
|
||||||
System.exit(0);
|
// We're in the child process and have exited the select loop. Proceed to execute the
|
||||||
|
// command.
|
||||||
|
if (caller != null) {
|
||||||
|
caller.run();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import android.system.StructCapUserData;
|
|||||||
import android.system.StructCapUserHeader;
|
import android.system.StructCapUserHeader;
|
||||||
import android.util.BootTimingsTraceLog;
|
import android.util.BootTimingsTraceLog;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
import com.android.internal.os.Zygote.MethodAndArgsCaller;
|
|
||||||
import dalvik.system.VMRuntime;
|
import dalvik.system.VMRuntime;
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
@@ -61,37 +60,35 @@ public class WrapperInit {
|
|||||||
* @param args The command-line arguments.
|
* @param args The command-line arguments.
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
try {
|
// Parse our mandatory arguments.
|
||||||
// Parse our mandatory arguments.
|
int fdNum = Integer.parseInt(args[0], 10);
|
||||||
int fdNum = Integer.parseInt(args[0], 10);
|
int targetSdkVersion = Integer.parseInt(args[1], 10);
|
||||||
int targetSdkVersion = Integer.parseInt(args[1], 10);
|
|
||||||
|
|
||||||
// Tell the Zygote what our actual PID is (since it only knows about the
|
// Tell the Zygote what our actual PID is (since it only knows about the
|
||||||
// wrapper that it directly forked).
|
// wrapper that it directly forked).
|
||||||
if (fdNum != 0) {
|
if (fdNum != 0) {
|
||||||
try {
|
try {
|
||||||
FileDescriptor fd = new FileDescriptor();
|
FileDescriptor fd = new FileDescriptor();
|
||||||
fd.setInt$(fdNum);
|
fd.setInt$(fdNum);
|
||||||
DataOutputStream os = new DataOutputStream(new FileOutputStream(fd));
|
DataOutputStream os = new DataOutputStream(new FileOutputStream(fd));
|
||||||
os.writeInt(Process.myPid());
|
os.writeInt(Process.myPid());
|
||||||
os.close();
|
os.close();
|
||||||
IoUtils.closeQuietly(fd);
|
IoUtils.closeQuietly(fd);
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
Slog.d(TAG, "Could not write pid of wrapped process to Zygote pipe.", ex);
|
Slog.d(TAG, "Could not write pid of wrapped process to Zygote pipe.", ex);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mimic system Zygote preloading.
|
|
||||||
ZygoteInit.preload(new BootTimingsTraceLog("WrapperInitTiming",
|
|
||||||
Trace.TRACE_TAG_DALVIK));
|
|
||||||
|
|
||||||
// Launch the application.
|
|
||||||
String[] runtimeArgs = new String[args.length - 2];
|
|
||||||
System.arraycopy(args, 2, runtimeArgs, 0, runtimeArgs.length);
|
|
||||||
WrapperInit.wrapperInit(targetSdkVersion, runtimeArgs);
|
|
||||||
} catch (Zygote.MethodAndArgsCaller caller) {
|
|
||||||
caller.run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mimic system Zygote preloading.
|
||||||
|
ZygoteInit.preload(new BootTimingsTraceLog("WrapperInitTiming",
|
||||||
|
Trace.TRACE_TAG_DALVIK));
|
||||||
|
|
||||||
|
// Launch the application.
|
||||||
|
String[] runtimeArgs = new String[args.length - 2];
|
||||||
|
System.arraycopy(args, 2, runtimeArgs, 0, runtimeArgs.length);
|
||||||
|
Runnable r = wrapperInit(targetSdkVersion, runtimeArgs);
|
||||||
|
|
||||||
|
r.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -142,8 +139,7 @@ public class WrapperInit {
|
|||||||
* @param targetSdkVersion target SDK version
|
* @param targetSdkVersion target SDK version
|
||||||
* @param argv arg strings
|
* @param argv arg strings
|
||||||
*/
|
*/
|
||||||
private static void wrapperInit(int targetSdkVersion, String[] argv)
|
private static Runnable wrapperInit(int targetSdkVersion, String[] argv) {
|
||||||
throws Zygote.MethodAndArgsCaller {
|
|
||||||
if (RuntimeInit.DEBUG) {
|
if (RuntimeInit.DEBUG) {
|
||||||
Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from wrapper");
|
Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from wrapper");
|
||||||
}
|
}
|
||||||
@@ -165,7 +161,7 @@ public class WrapperInit {
|
|||||||
|
|
||||||
// Perform the same initialization that would happen after the Zygote forks.
|
// Perform the same initialization that would happen after the Zygote forks.
|
||||||
Zygote.nativePreApplicationInit();
|
Zygote.nativePreApplicationInit();
|
||||||
RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
|
return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -221,39 +221,4 @@ public final class Zygote {
|
|||||||
command.append(" '").append(arg.replace("'", "'\\''")).append("'");
|
command.append(" '").append(arg.replace("'", "'\\''")).append("'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper exception class which holds a method and arguments and
|
|
||||||
* can call them. This is used as part of a trampoline to get rid of
|
|
||||||
* the initial process setup stack frames.
|
|
||||||
*/
|
|
||||||
public static class MethodAndArgsCaller extends Exception
|
|
||||||
implements Runnable {
|
|
||||||
/** method to call */
|
|
||||||
private final Method mMethod;
|
|
||||||
|
|
||||||
/** argument array */
|
|
||||||
private final String[] mArgs;
|
|
||||||
|
|
||||||
public MethodAndArgsCaller(Method method, String[] args) {
|
|
||||||
mMethod = method;
|
|
||||||
mArgs = args;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
mMethod.invoke(null, new Object[] { mArgs });
|
|
||||||
} catch (IllegalAccessException ex) {
|
|
||||||
throw new RuntimeException(ex);
|
|
||||||
} catch (InvocationTargetException ex) {
|
|
||||||
Throwable cause = ex.getCause();
|
|
||||||
if (cause instanceof RuntimeException) {
|
|
||||||
throw (RuntimeException) cause;
|
|
||||||
} else if (cause instanceof Error) {
|
|
||||||
throw (Error) cause;
|
|
||||||
}
|
|
||||||
throw new RuntimeException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ import android.net.Credentials;
|
|||||||
import android.net.LocalSocket;
|
import android.net.LocalSocket;
|
||||||
import android.os.FactoryTest;
|
import android.os.FactoryTest;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.os.SELinux;
|
|
||||||
import android.os.SystemProperties;
|
import android.os.SystemProperties;
|
||||||
import android.os.Trace;
|
import android.os.Trace;
|
||||||
import android.system.ErrnoException;
|
import android.system.ErrnoException;
|
||||||
@@ -42,14 +41,13 @@ import java.io.BufferedReader;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.EOFException;
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.PrintStream;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import libcore.io.IoUtils;
|
import libcore.io.IoUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -73,6 +71,7 @@ class ZygoteConnection {
|
|||||||
private final BufferedReader mSocketReader;
|
private final BufferedReader mSocketReader;
|
||||||
private final Credentials peer;
|
private final Credentials peer;
|
||||||
private final String abiList;
|
private final String abiList;
|
||||||
|
private boolean isEof;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs instance from connected socket.
|
* Constructs instance from connected socket.
|
||||||
@@ -99,6 +98,8 @@ class ZygoteConnection {
|
|||||||
Log.e(TAG, "Cannot read peer credentials", ex);
|
Log.e(TAG, "Cannot read peer credentials", ex);
|
||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isEof = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -111,21 +112,14 @@ class ZygoteConnection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads one start command from the command socket. If successful,
|
* Reads one start command from the command socket. If successful, a child is forked and a
|
||||||
* a child is forked and a {@link Zygote.MethodAndArgsCaller}
|
* {@code Runnable} that calls the childs main method (or equivalent) is returned in the child
|
||||||
* exception is thrown in that child while in the parent process,
|
* process. {@code null} is always returned in the parent process (the zygote).
|
||||||
* the method returns normally. On failure, the child is not
|
|
||||||
* spawned and messages are printed to the log and stderr. Returns
|
|
||||||
* a boolean status value indicating whether an end-of-file on the command
|
|
||||||
* socket has been encountered.
|
|
||||||
*
|
*
|
||||||
* @return false if command socket should continue to be read from, or
|
* If the client closes the socket, an {@code EOF} condition is set, which callers can test
|
||||||
* true if an end-of-file has been encountered.
|
* for by calling {@code ZygoteConnection.isClosedByPeer}.
|
||||||
* @throws Zygote.MethodAndArgsCaller trampoline to invoke main()
|
|
||||||
* method in child process
|
|
||||||
*/
|
*/
|
||||||
boolean runOnce(ZygoteServer zygoteServer) throws Zygote.MethodAndArgsCaller {
|
Runnable processOneCommand(ZygoteServer zygoteServer) {
|
||||||
|
|
||||||
String args[];
|
String args[];
|
||||||
Arguments parsedArgs = null;
|
Arguments parsedArgs = null;
|
||||||
FileDescriptor[] descriptors;
|
FileDescriptor[] descriptors;
|
||||||
@@ -134,130 +128,120 @@ class ZygoteConnection {
|
|||||||
args = readArgumentList();
|
args = readArgumentList();
|
||||||
descriptors = mSocket.getAncillaryFileDescriptors();
|
descriptors = mSocket.getAncillaryFileDescriptors();
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
Log.w(TAG, "IOException on command socket " + ex.getMessage());
|
throw new IllegalStateException("IOException on command socket", ex);
|
||||||
closeSocket();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// readArgumentList returns null only when it has reached EOF with no available
|
||||||
|
// data to read. This will only happen when the remote socket has disconnected.
|
||||||
if (args == null) {
|
if (args == null) {
|
||||||
// EOF reached.
|
isEof = true;
|
||||||
closeSocket();
|
return null;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** the stderr of the most recent request, if avail */
|
|
||||||
PrintStream newStderr = null;
|
|
||||||
|
|
||||||
if (descriptors != null && descriptors.length >= 3) {
|
|
||||||
newStderr = new PrintStream(
|
|
||||||
new FileOutputStream(descriptors[2]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int pid = -1;
|
int pid = -1;
|
||||||
FileDescriptor childPipeFd = null;
|
FileDescriptor childPipeFd = null;
|
||||||
FileDescriptor serverPipeFd = null;
|
FileDescriptor serverPipeFd = null;
|
||||||
|
|
||||||
try {
|
parsedArgs = new Arguments(args);
|
||||||
parsedArgs = new Arguments(args);
|
|
||||||
|
|
||||||
if (parsedArgs.abiListQuery) {
|
if (parsedArgs.abiListQuery) {
|
||||||
return handleAbiListQuery();
|
handleAbiListQuery();
|
||||||
}
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (parsedArgs.preloadDefault) {
|
if (parsedArgs.preloadDefault) {
|
||||||
return handlePreload();
|
handlePreload();
|
||||||
}
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (parsedArgs.preloadPackage != null) {
|
if (parsedArgs.preloadPackage != null) {
|
||||||
return handlePreloadPackage(parsedArgs.preloadPackage,
|
handlePreloadPackage(parsedArgs.preloadPackage, parsedArgs.preloadPackageLibs,
|
||||||
parsedArgs.preloadPackageLibs, parsedArgs.preloadPackageCacheKey);
|
parsedArgs.preloadPackageCacheKey);
|
||||||
}
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (parsedArgs.permittedCapabilities != 0 || parsedArgs.effectiveCapabilities != 0) {
|
if (parsedArgs.permittedCapabilities != 0 || parsedArgs.effectiveCapabilities != 0) {
|
||||||
throw new ZygoteSecurityException("Client may not specify capabilities: " +
|
throw new ZygoteSecurityException("Client may not specify capabilities: " +
|
||||||
"permitted=0x" + Long.toHexString(parsedArgs.permittedCapabilities) +
|
"permitted=0x" + Long.toHexString(parsedArgs.permittedCapabilities) +
|
||||||
", effective=0x" + Long.toHexString(parsedArgs.effectiveCapabilities));
|
", effective=0x" + Long.toHexString(parsedArgs.effectiveCapabilities));
|
||||||
}
|
}
|
||||||
|
|
||||||
applyUidSecurityPolicy(parsedArgs, peer);
|
applyUidSecurityPolicy(parsedArgs, peer);
|
||||||
applyInvokeWithSecurityPolicy(parsedArgs, peer);
|
applyInvokeWithSecurityPolicy(parsedArgs, peer);
|
||||||
|
|
||||||
applyDebuggerSystemProperty(parsedArgs);
|
applyDebuggerSystemProperty(parsedArgs);
|
||||||
applyInvokeWithSystemProperty(parsedArgs);
|
applyInvokeWithSystemProperty(parsedArgs);
|
||||||
|
|
||||||
int[][] rlimits = null;
|
int[][] rlimits = null;
|
||||||
|
|
||||||
if (parsedArgs.rlimits != null) {
|
if (parsedArgs.rlimits != null) {
|
||||||
rlimits = parsedArgs.rlimits.toArray(intArray2d);
|
rlimits = parsedArgs.rlimits.toArray(intArray2d);
|
||||||
}
|
}
|
||||||
|
|
||||||
int[] fdsToIgnore = null;
|
int[] fdsToIgnore = null;
|
||||||
|
|
||||||
if (parsedArgs.invokeWith != null) {
|
if (parsedArgs.invokeWith != null) {
|
||||||
|
try {
|
||||||
FileDescriptor[] pipeFds = Os.pipe2(O_CLOEXEC);
|
FileDescriptor[] pipeFds = Os.pipe2(O_CLOEXEC);
|
||||||
childPipeFd = pipeFds[1];
|
childPipeFd = pipeFds[1];
|
||||||
serverPipeFd = pipeFds[0];
|
serverPipeFd = pipeFds[0];
|
||||||
Os.fcntlInt(childPipeFd, F_SETFD, 0);
|
Os.fcntlInt(childPipeFd, F_SETFD, 0);
|
||||||
fdsToIgnore = new int[] { childPipeFd.getInt$(), serverPipeFd.getInt$() };
|
fdsToIgnore = new int[]{childPipeFd.getInt$(), serverPipeFd.getInt$()};
|
||||||
|
} catch (ErrnoException errnoEx) {
|
||||||
|
throw new IllegalStateException("Unable to set up pipe for invoke-with", errnoEx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* In order to avoid leaking descriptors to the Zygote child,
|
|
||||||
* the native code must close the two Zygote socket descriptors
|
|
||||||
* in the child process before it switches from Zygote-root to
|
|
||||||
* the UID and privileges of the application being launched.
|
|
||||||
*
|
|
||||||
* In order to avoid "bad file descriptor" errors when the
|
|
||||||
* two LocalSocket objects are closed, the Posix file
|
|
||||||
* descriptors are released via a dup2() call which closes
|
|
||||||
* the socket and substitutes an open descriptor to /dev/null.
|
|
||||||
*/
|
|
||||||
|
|
||||||
int [] fdsToClose = { -1, -1 };
|
|
||||||
|
|
||||||
FileDescriptor fd = mSocket.getFileDescriptor();
|
|
||||||
|
|
||||||
if (fd != null) {
|
|
||||||
fdsToClose[0] = fd.getInt$();
|
|
||||||
}
|
|
||||||
|
|
||||||
fd = zygoteServer.getServerSocketFileDescriptor();
|
|
||||||
|
|
||||||
if (fd != null) {
|
|
||||||
fdsToClose[1] = fd.getInt$();
|
|
||||||
}
|
|
||||||
|
|
||||||
fd = null;
|
|
||||||
|
|
||||||
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
|
|
||||||
parsedArgs.runtimeFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
|
|
||||||
parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.instructionSet,
|
|
||||||
parsedArgs.appDataDir);
|
|
||||||
} catch (ErrnoException ex) {
|
|
||||||
logAndPrintError(newStderr, "Exception creating pipe", ex);
|
|
||||||
} catch (IllegalArgumentException ex) {
|
|
||||||
logAndPrintError(newStderr, "Invalid zygote arguments", ex);
|
|
||||||
} catch (ZygoteSecurityException ex) {
|
|
||||||
logAndPrintError(newStderr,
|
|
||||||
"Zygote security policy prevents request: ", ex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In order to avoid leaking descriptors to the Zygote child,
|
||||||
|
* the native code must close the two Zygote socket descriptors
|
||||||
|
* in the child process before it switches from Zygote-root to
|
||||||
|
* the UID and privileges of the application being launched.
|
||||||
|
*
|
||||||
|
* In order to avoid "bad file descriptor" errors when the
|
||||||
|
* two LocalSocket objects are closed, the Posix file
|
||||||
|
* descriptors are released via a dup2() call which closes
|
||||||
|
* the socket and substitutes an open descriptor to /dev/null.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int [] fdsToClose = { -1, -1 };
|
||||||
|
|
||||||
|
FileDescriptor fd = mSocket.getFileDescriptor();
|
||||||
|
|
||||||
|
if (fd != null) {
|
||||||
|
fdsToClose[0] = fd.getInt$();
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = zygoteServer.getServerSocketFileDescriptor();
|
||||||
|
|
||||||
|
if (fd != null) {
|
||||||
|
fdsToClose[1] = fd.getInt$();
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = null;
|
||||||
|
|
||||||
|
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
|
||||||
|
parsedArgs.runtimeFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
|
||||||
|
parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.instructionSet,
|
||||||
|
parsedArgs.appDataDir);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
// in child
|
// in child
|
||||||
|
zygoteServer.setForkChild();
|
||||||
|
|
||||||
zygoteServer.closeServerSocket();
|
zygoteServer.closeServerSocket();
|
||||||
IoUtils.closeQuietly(serverPipeFd);
|
IoUtils.closeQuietly(serverPipeFd);
|
||||||
serverPipeFd = null;
|
serverPipeFd = null;
|
||||||
handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
|
|
||||||
|
|
||||||
// should never get here, the child is expected to either
|
return handleChildProc(parsedArgs, descriptors, childPipeFd);
|
||||||
// throw Zygote.MethodAndArgsCaller or exec().
|
|
||||||
return true;
|
|
||||||
} else {
|
} else {
|
||||||
// in parent...pid of < 0 means failure
|
// In the parent. A pid < 0 indicates a failure and will be handled in
|
||||||
|
// handleParentProc.
|
||||||
IoUtils.closeQuietly(childPipeFd);
|
IoUtils.closeQuietly(childPipeFd);
|
||||||
childPipeFd = null;
|
childPipeFd = null;
|
||||||
return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
|
handleParentProc(pid, descriptors, serverPipeFd);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
IoUtils.closeQuietly(childPipeFd);
|
IoUtils.closeQuietly(childPipeFd);
|
||||||
@@ -265,15 +249,13 @@ class ZygoteConnection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean handleAbiListQuery() {
|
private void handleAbiListQuery() {
|
||||||
try {
|
try {
|
||||||
final byte[] abiListBytes = abiList.getBytes(StandardCharsets.US_ASCII);
|
final byte[] abiListBytes = abiList.getBytes(StandardCharsets.US_ASCII);
|
||||||
mSocketOutStream.writeInt(abiListBytes.length);
|
mSocketOutStream.writeInt(abiListBytes.length);
|
||||||
mSocketOutStream.write(abiListBytes);
|
mSocketOutStream.write(abiListBytes);
|
||||||
return false;
|
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
Log.e(TAG, "Error writing to command socket", ioe);
|
throw new IllegalStateException("Error writing to command socket", ioe);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,7 +265,7 @@ class ZygoteConnection {
|
|||||||
* if no preload was initiated. The latter implies that the zygote is not configured to load
|
* if no preload was initiated. The latter implies that the zygote is not configured to load
|
||||||
* resources lazy or that the zygote has already handled a previous request to handlePreload.
|
* resources lazy or that the zygote has already handled a previous request to handlePreload.
|
||||||
*/
|
*/
|
||||||
private boolean handlePreload() {
|
private void handlePreload() {
|
||||||
try {
|
try {
|
||||||
if (isPreloadComplete()) {
|
if (isPreloadComplete()) {
|
||||||
mSocketOutStream.writeInt(1);
|
mSocketOutStream.writeInt(1);
|
||||||
@@ -291,11 +273,8 @@ class ZygoteConnection {
|
|||||||
preload();
|
preload();
|
||||||
mSocketOutStream.writeInt(0);
|
mSocketOutStream.writeInt(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
Log.e(TAG, "Error writing to command socket", ioe);
|
throw new IllegalStateException("Error writing to command socket", ioe);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,7 +286,11 @@ class ZygoteConnection {
|
|||||||
return ZygoteInit.isPreloadComplete();
|
return ZygoteInit.isPreloadComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean handlePreloadPackage(String packagePath, String libsPath, String cacheKey) {
|
protected DataOutputStream getSocketOutputStream() {
|
||||||
|
return mSocketOutStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void handlePreloadPackage(String packagePath, String libsPath, String cacheKey) {
|
||||||
throw new RuntimeException("Zyogte does not support package preloading");
|
throw new RuntimeException("Zyogte does not support package preloading");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,6 +306,10 @@ class ZygoteConnection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isClosedByPeer() {
|
||||||
|
return isEof;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles argument parsing for args related to the zygote spawner.
|
* Handles argument parsing for args related to the zygote spawner.
|
||||||
*
|
*
|
||||||
@@ -753,15 +740,9 @@ class ZygoteConnection {
|
|||||||
* @param parsedArgs non-null; zygote args
|
* @param parsedArgs non-null; zygote args
|
||||||
* @param descriptors null-ok; new file descriptors for stdio if available.
|
* @param descriptors null-ok; new file descriptors for stdio if available.
|
||||||
* @param pipeFd null-ok; pipe for communication back to Zygote.
|
* @param pipeFd null-ok; pipe for communication back to Zygote.
|
||||||
* @param newStderr null-ok; stream to use for stderr until stdio
|
|
||||||
* is reopened.
|
|
||||||
*
|
|
||||||
* @throws Zygote.MethodAndArgsCaller on success to
|
|
||||||
* trampoline to code that invokes static main.
|
|
||||||
*/
|
*/
|
||||||
private void handleChildProc(Arguments parsedArgs,
|
private Runnable handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors,
|
||||||
FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
|
FileDescriptor pipeFd) {
|
||||||
throws Zygote.MethodAndArgsCaller {
|
|
||||||
/**
|
/**
|
||||||
* By the time we get here, the native code has closed the two actual Zygote
|
* By the time we get here, the native code has closed the two actual Zygote
|
||||||
* socket connections, and substituted /dev/null in their place. The LocalSocket
|
* socket connections, and substituted /dev/null in their place. The LocalSocket
|
||||||
@@ -778,7 +759,6 @@ class ZygoteConnection {
|
|||||||
for (FileDescriptor fd: descriptors) {
|
for (FileDescriptor fd: descriptors) {
|
||||||
IoUtils.closeQuietly(fd);
|
IoUtils.closeQuietly(fd);
|
||||||
}
|
}
|
||||||
newStderr = System.err;
|
|
||||||
} catch (ErrnoException ex) {
|
} catch (ErrnoException ex) {
|
||||||
Log.e(TAG, "Error reopening stdio", ex);
|
Log.e(TAG, "Error reopening stdio", ex);
|
||||||
}
|
}
|
||||||
@@ -795,9 +775,12 @@ class ZygoteConnection {
|
|||||||
parsedArgs.niceName, parsedArgs.targetSdkVersion,
|
parsedArgs.niceName, parsedArgs.targetSdkVersion,
|
||||||
VMRuntime.getCurrentInstructionSet(),
|
VMRuntime.getCurrentInstructionSet(),
|
||||||
pipeFd, parsedArgs.remainingArgs);
|
pipeFd, parsedArgs.remainingArgs);
|
||||||
|
|
||||||
|
// Should not get here.
|
||||||
|
throw new IllegalStateException("WrapperInit.execApplication unexpectedly returned");
|
||||||
} else {
|
} else {
|
||||||
ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion,
|
return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs,
|
||||||
parsedArgs.remainingArgs, null /* classLoader */);
|
null /* classLoader */);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -809,13 +792,8 @@ class ZygoteConnection {
|
|||||||
* @param descriptors null-ok; file descriptors for child's new stdio if
|
* @param descriptors null-ok; file descriptors for child's new stdio if
|
||||||
* specified.
|
* specified.
|
||||||
* @param pipeFd null-ok; pipe for communication with child.
|
* @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,
|
private void handleParentProc(int pid, FileDescriptor[] descriptors, FileDescriptor pipeFd) {
|
||||||
FileDescriptor[] descriptors, FileDescriptor pipeFd, Arguments parsedArgs) {
|
|
||||||
|
|
||||||
if (pid > 0) {
|
if (pid > 0) {
|
||||||
setChildPgid(pid);
|
setChildPgid(pid);
|
||||||
}
|
}
|
||||||
@@ -907,11 +885,8 @@ class ZygoteConnection {
|
|||||||
mSocketOutStream.writeInt(pid);
|
mSocketOutStream.writeInt(pid);
|
||||||
mSocketOutStream.writeBoolean(usingWrapper);
|
mSocketOutStream.writeBoolean(usingWrapper);
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
Log.e(TAG, "Error writing to command socket", ex);
|
throw new IllegalStateException("Error writing to command socket", ex);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setChildPgid(int pid) {
|
private void setChildPgid(int pid) {
|
||||||
@@ -927,20 +902,4 @@ class ZygoteConnection {
|
|||||||
+ "normal if peer is not in our session");
|
+ "normal if peer is not in our session");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Logs an error message and prints it to the specified stream, if
|
|
||||||
* provided
|
|
||||||
*
|
|
||||||
* @param newStderr null-ok; a standard error stream
|
|
||||||
* @param message non-null; error message
|
|
||||||
* @param ex null-ok an exception
|
|
||||||
*/
|
|
||||||
private static void logAndPrintError (PrintStream newStderr,
|
|
||||||
String message, Throwable ex) {
|
|
||||||
Log.e(TAG, message, ex);
|
|
||||||
if (newStderr != null) {
|
|
||||||
newStderr.println(message + (ex == null ? "" : ex));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import android.content.res.TypedArray;
|
|||||||
import android.icu.impl.CacheValue;
|
import android.icu.impl.CacheValue;
|
||||||
import android.icu.text.DecimalFormatSymbols;
|
import android.icu.text.DecimalFormatSymbols;
|
||||||
import android.icu.util.ULocale;
|
import android.icu.util.ULocale;
|
||||||
import android.net.LocalServerSocket;
|
|
||||||
import android.opengl.EGL14;
|
import android.opengl.EGL14;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.IInstalld;
|
import android.os.IInstalld;
|
||||||
@@ -447,10 +446,7 @@ public class ZygoteInit {
|
|||||||
/**
|
/**
|
||||||
* Finish remaining work for the newly forked system server process.
|
* Finish remaining work for the newly forked system server process.
|
||||||
*/
|
*/
|
||||||
private static void handleSystemServerProcess(
|
private static Runnable handleSystemServerProcess(ZygoteConnection.Arguments parsedArgs) {
|
||||||
ZygoteConnection.Arguments parsedArgs)
|
|
||||||
throws Zygote.MethodAndArgsCaller {
|
|
||||||
|
|
||||||
// set umask to 0077 so new files and directories will default to owner-only permissions.
|
// set umask to 0077 so new files and directories will default to owner-only permissions.
|
||||||
Os.umask(S_IRWXG | S_IRWXO);
|
Os.umask(S_IRWXG | S_IRWXO);
|
||||||
|
|
||||||
@@ -496,6 +492,8 @@ public class ZygoteInit {
|
|||||||
WrapperInit.execApplication(parsedArgs.invokeWith,
|
WrapperInit.execApplication(parsedArgs.invokeWith,
|
||||||
parsedArgs.niceName, parsedArgs.targetSdkVersion,
|
parsedArgs.niceName, parsedArgs.targetSdkVersion,
|
||||||
VMRuntime.getCurrentInstructionSet(), null, args);
|
VMRuntime.getCurrentInstructionSet(), null, args);
|
||||||
|
|
||||||
|
throw new IllegalStateException("Unexpected return from WrapperInit.execApplication");
|
||||||
} else {
|
} else {
|
||||||
ClassLoader cl = null;
|
ClassLoader cl = null;
|
||||||
if (systemServerClasspath != null) {
|
if (systemServerClasspath != null) {
|
||||||
@@ -507,7 +505,7 @@ public class ZygoteInit {
|
|||||||
/*
|
/*
|
||||||
* Pass the remaining arguments to SystemServer.
|
* Pass the remaining arguments to SystemServer.
|
||||||
*/
|
*/
|
||||||
ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
|
return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* should never reach here */
|
/* should never reach here */
|
||||||
@@ -589,10 +587,13 @@ public class ZygoteInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepare the arguments and fork for the system server process.
|
* Prepare the arguments and forks for the system server process.
|
||||||
|
*
|
||||||
|
* Returns an {@code Runnable} that provides an entrypoint into system_server code in the
|
||||||
|
* child process, and {@code null} in the parent.
|
||||||
*/
|
*/
|
||||||
private static boolean startSystemServer(String abiList, String socketName, ZygoteServer zygoteServer)
|
private static Runnable forkSystemServer(String abiList, String socketName,
|
||||||
throws Zygote.MethodAndArgsCaller, RuntimeException {
|
ZygoteServer zygoteServer) {
|
||||||
long capabilities = posixCapabilitiesAsBits(
|
long capabilities = posixCapabilitiesAsBits(
|
||||||
OsConstants.CAP_IPC_LOCK,
|
OsConstants.CAP_IPC_LOCK,
|
||||||
OsConstants.CAP_KILL,
|
OsConstants.CAP_KILL,
|
||||||
@@ -657,10 +658,10 @@ public class ZygoteInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
zygoteServer.closeServerSocket();
|
zygoteServer.closeServerSocket();
|
||||||
handleSystemServerProcess(parsedArgs);
|
return handleSystemServerProcess(parsedArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -691,6 +692,7 @@ public class ZygoteInit {
|
|||||||
throw new RuntimeException("Failed to setpgid(0,0)", ex);
|
throw new RuntimeException("Failed to setpgid(0,0)", ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final Runnable caller;
|
||||||
try {
|
try {
|
||||||
// Report Zygote start time to tron unless it is a runtime restart
|
// Report Zygote start time to tron unless it is a runtime restart
|
||||||
if (!"1".equals(SystemProperties.get("sys.boot_completed"))) {
|
if (!"1".equals(SystemProperties.get("sys.boot_completed"))) {
|
||||||
@@ -760,19 +762,32 @@ public class ZygoteInit {
|
|||||||
ZygoteHooks.stopZygoteNoThreadCreation();
|
ZygoteHooks.stopZygoteNoThreadCreation();
|
||||||
|
|
||||||
if (startSystemServer) {
|
if (startSystemServer) {
|
||||||
startSystemServer(abiList, socketName, zygoteServer);
|
Runnable r = forkSystemServer(abiList, socketName, zygoteServer);
|
||||||
|
|
||||||
|
// {@code r == null} in the parent (zygote) process, and {@code r != null} in the
|
||||||
|
// child (system_server) process.
|
||||||
|
if (r != null) {
|
||||||
|
r.run();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.i(TAG, "Accepting command socket connections");
|
Log.i(TAG, "Accepting command socket connections");
|
||||||
zygoteServer.runSelectLoop(abiList);
|
|
||||||
|
|
||||||
zygoteServer.closeServerSocket();
|
// The select loop returns early in the child process after a fork and
|
||||||
} catch (Zygote.MethodAndArgsCaller caller) {
|
// loops forever in the zygote.
|
||||||
caller.run();
|
caller = zygoteServer.runSelectLoop(abiList);
|
||||||
} catch (Throwable ex) {
|
} catch (Throwable ex) {
|
||||||
Log.e(TAG, "System zygote died with exception", ex);
|
Log.e(TAG, "System zygote died with exception", ex);
|
||||||
zygoteServer.closeServerSocket();
|
|
||||||
throw ex;
|
throw ex;
|
||||||
|
} finally {
|
||||||
|
zygoteServer.closeServerSocket();
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're in the child process and have exited the select loop. Proceed to execute the
|
||||||
|
// command.
|
||||||
|
if (caller != null) {
|
||||||
|
caller.run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -830,8 +845,7 @@ public class ZygoteInit {
|
|||||||
* @param targetSdkVersion target SDK version
|
* @param targetSdkVersion target SDK version
|
||||||
* @param argv arg strings
|
* @param argv arg strings
|
||||||
*/
|
*/
|
||||||
public static final void zygoteInit(int targetSdkVersion, String[] argv,
|
public static final Runnable zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) {
|
||||||
ClassLoader classLoader) throws Zygote.MethodAndArgsCaller {
|
|
||||||
if (RuntimeInit.DEBUG) {
|
if (RuntimeInit.DEBUG) {
|
||||||
Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
|
Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
|
||||||
}
|
}
|
||||||
@@ -841,7 +855,7 @@ public class ZygoteInit {
|
|||||||
|
|
||||||
RuntimeInit.commonInit();
|
RuntimeInit.commonInit();
|
||||||
ZygoteInit.nativeZygoteInit();
|
ZygoteInit.nativeZygoteInit();
|
||||||
RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
|
return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final native void nativeZygoteInit();
|
private static final native void nativeZygoteInit();
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import android.system.ErrnoException;
|
|||||||
import android.system.StructPollfd;
|
import android.system.StructPollfd;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import android.util.Slog;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -45,9 +46,18 @@ class ZygoteServer {
|
|||||||
|
|
||||||
private LocalServerSocket mServerSocket;
|
private LocalServerSocket mServerSocket;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set by the child process, immediately after a call to {@code Zygote.forkAndSpecialize}.
|
||||||
|
*/
|
||||||
|
private boolean mIsForkChild;
|
||||||
|
|
||||||
ZygoteServer() {
|
ZygoteServer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setForkChild() {
|
||||||
|
mIsForkChild = true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a server socket for zygote command connections
|
* Registers a server socket for zygote command connections
|
||||||
*
|
*
|
||||||
@@ -129,11 +139,8 @@ class ZygoteServer {
|
|||||||
* Runs the zygote process's select loop. Accepts new connections as
|
* Runs the zygote process's select loop. Accepts new connections as
|
||||||
* they happen, and reads commands from connections one spawn-request's
|
* they happen, and reads commands from connections one spawn-request's
|
||||||
* worth at a time.
|
* worth at a time.
|
||||||
*
|
|
||||||
* @throws Zygote.MethodAndArgsCaller in a child process when a main()
|
|
||||||
* should be executed.
|
|
||||||
*/
|
*/
|
||||||
void runSelectLoop(String abiList) throws Zygote.MethodAndArgsCaller {
|
Runnable runSelectLoop(String abiList) {
|
||||||
ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
|
ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
|
||||||
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
|
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
|
||||||
|
|
||||||
@@ -156,15 +163,62 @@ class ZygoteServer {
|
|||||||
if ((pollFds[i].revents & POLLIN) == 0) {
|
if ((pollFds[i].revents & POLLIN) == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
ZygoteConnection newPeer = acceptCommandPeer(abiList);
|
ZygoteConnection newPeer = acceptCommandPeer(abiList);
|
||||||
peers.add(newPeer);
|
peers.add(newPeer);
|
||||||
fds.add(newPeer.getFileDesciptor());
|
fds.add(newPeer.getFileDesciptor());
|
||||||
} else {
|
} else {
|
||||||
boolean done = peers.get(i).runOnce(this);
|
try {
|
||||||
if (done) {
|
ZygoteConnection connection = peers.get(i);
|
||||||
peers.remove(i);
|
final Runnable command = connection.processOneCommand(this);
|
||||||
fds.remove(i);
|
|
||||||
|
if (mIsForkChild) {
|
||||||
|
// We're in the child. We should always have a command to run at this
|
||||||
|
// stage if processOneCommand hasn't called "exec".
|
||||||
|
if (command == null) {
|
||||||
|
throw new IllegalStateException("command == null");
|
||||||
|
}
|
||||||
|
|
||||||
|
return command;
|
||||||
|
} else {
|
||||||
|
// We're in the server - we should never have any commands to run.
|
||||||
|
if (command != null) {
|
||||||
|
throw new IllegalStateException("command != null");
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't know whether the remote side of the socket was closed or
|
||||||
|
// not until we attempt to read from it from processOneCommand. This shows up as
|
||||||
|
// a regular POLLIN event in our regular processing loop.
|
||||||
|
if (connection.isClosedByPeer()) {
|
||||||
|
connection.closeSocket();
|
||||||
|
peers.remove(i);
|
||||||
|
fds.remove(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (!mIsForkChild) {
|
||||||
|
// We're in the server so any exception here is one that has taken place
|
||||||
|
// pre-fork while processing commands or reading / writing from the
|
||||||
|
// control socket. Make a loud noise about any such exceptions so that
|
||||||
|
// we know exactly what failed and why.
|
||||||
|
|
||||||
|
Slog.e(TAG, "Exception executing zygote command: ", e);
|
||||||
|
|
||||||
|
// Make sure the socket is closed so that the other end knows immediately
|
||||||
|
// that something has gone wrong and doesn't time out waiting for a
|
||||||
|
// response.
|
||||||
|
ZygoteConnection conn = peers.remove(i);
|
||||||
|
conn.closeSocket();
|
||||||
|
|
||||||
|
fds.remove(i);
|
||||||
|
} else {
|
||||||
|
// We're in the child so any exception caught here has happened post
|
||||||
|
// fork and before we execute ActivityThread.main (or any other main()
|
||||||
|
// method). Log the details of the exception and bring down the process.
|
||||||
|
Log.e(TAG, "Caught post-fork exception in child process.", e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user