Merge "Enables the use of the blastula pool."

am: 33a09948e5

Change-Id: Ibe2aa45a1df74674b82127f0c8588694c39461cf
This commit is contained in:
Christian Wailes
2019-01-24 18:22:15 -08:00
committed by android-build-merger
7 changed files with 335 additions and 122 deletions

View File

@@ -498,7 +498,8 @@ public class Process {
String[] zygoteArgs) {
return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
abi, instructionSet, appDataDir, invokeWith,
/*useBlastulaPool=*/ true, zygoteArgs);
}
/** @hide */
@@ -515,7 +516,8 @@ public class Process {
String[] zygoteArgs) {
return WebViewZygote.getProcess().start(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
abi, instructionSet, appDataDir, invokeWith,
/*useBlastulaPool=*/ false, zygoteArgs);
}
/**

View File

@@ -69,6 +69,16 @@ public class ZygoteProcess {
*/
public static final String ZYGOTE_SECONDARY_SOCKET_NAME = "zygote_secondary";
/**
* @hide for internal use only
*/
public static final String BLASTULA_POOL_SOCKET_NAME = "blastula_pool";
/**
* @hide for internal use only
*/
public static final String BLASTULA_POOL_SECONDARY_SOCKET_NAME = "blastula_pool_secondary";
/**
* @hide for internal use only
*/
@@ -83,6 +93,15 @@ public class ZygoteProcess {
* The name of the secondary (alternate ABI) zygote socket.
*/
private final LocalSocketAddress mZygoteSecondarySocketAddress;
/**
* The name of the socket used to communicate with the primary blastula pool.
*/
private final LocalSocketAddress mBlastulaPoolSocketAddress;
/**
* The name of the socket used to communicate with the secondary (alternate ABI) blastula pool.
*/
private final LocalSocketAddress mBlastulaPoolSecondarySocketAddress;
public ZygoteProcess() {
mZygoteSocketAddress =
@@ -90,12 +109,22 @@ public class ZygoteProcess {
mZygoteSecondarySocketAddress =
new LocalSocketAddress(ZYGOTE_SECONDARY_SOCKET_NAME,
LocalSocketAddress.Namespace.RESERVED);
mBlastulaPoolSocketAddress =
new LocalSocketAddress(BLASTULA_POOL_SOCKET_NAME,
LocalSocketAddress.Namespace.RESERVED);
mBlastulaPoolSecondarySocketAddress =
new LocalSocketAddress(BLASTULA_POOL_SECONDARY_SOCKET_NAME,
LocalSocketAddress.Namespace.RESERVED);
}
public ZygoteProcess(LocalSocketAddress primarySocketAddress,
LocalSocketAddress secondarySocketAddress) {
mZygoteSocketAddress = primarySocketAddress;
mZygoteSecondarySocketAddress = secondarySocketAddress;
mBlastulaPoolSocketAddress = null;
mBlastulaPoolSecondarySocketAddress = null;
}
public LocalSocketAddress getPrimarySocketAddress() {
@@ -107,6 +136,7 @@ public class ZygoteProcess {
*/
public static class ZygoteState {
final LocalSocketAddress mZygoteSocketAddress;
final LocalSocketAddress mBlastulaSocketAddress;
private final LocalSocket mZygoteSessionSocket;
@@ -118,11 +148,13 @@ public class ZygoteProcess {
private boolean mClosed;
private ZygoteState(LocalSocketAddress zygoteSocketAddress,
LocalSocketAddress blastulaSocketAddress,
LocalSocket zygoteSessionSocket,
DataInputStream zygoteInputStream,
BufferedWriter zygoteOutputWriter,
List<String> abiList) {
this.mZygoteSocketAddress = zygoteSocketAddress;
this.mBlastulaSocketAddress = blastulaSocketAddress;
this.mZygoteSessionSocket = zygoteSessionSocket;
this.mZygoteInputStream = zygoteInputStream;
this.mZygoteOutputWriter = zygoteOutputWriter;
@@ -130,14 +162,17 @@ public class ZygoteProcess {
}
/**
* Create a new ZygoteState object by connecting to the given Zygote socket.
* Create a new ZygoteState object by connecting to the given Zygote socket and saving the
* given blastula socket address.
*
* @param zygoteSocketAddress Zygote socket to connect to
* @param blastulaSocketAddress Blastula socket address to save for later
* @return A new ZygoteState object containing a session socket for the given Zygote socket
* address
* @throws IOException
*/
public static ZygoteState connect(LocalSocketAddress zygoteSocketAddress)
public static ZygoteState connect(LocalSocketAddress zygoteSocketAddress,
LocalSocketAddress blastulaSocketAddress)
throws IOException {
DataInputStream zygoteInputStream = null;
@@ -150,7 +185,7 @@ public class ZygoteProcess {
zygoteOutputWriter =
new BufferedWriter(
new OutputStreamWriter(zygoteSessionSocket.getOutputStream()),
256);
Zygote.SOCKET_BUFFER_SIZE);
} catch (IOException ex) {
try {
zygoteSessionSocket.close();
@@ -159,11 +194,18 @@ public class ZygoteProcess {
throw ex;
}
return new ZygoteState(zygoteSocketAddress,
return new ZygoteState(zygoteSocketAddress, blastulaSocketAddress,
zygoteSessionSocket, zygoteInputStream, zygoteOutputWriter,
getAbiList(zygoteOutputWriter, zygoteInputStream));
}
LocalSocket getBlastulaSessionSocket() throws IOException {
final LocalSocket blastulaSessionSocket = new LocalSocket();
blastulaSessionSocket.connect(this.mBlastulaSocketAddress);
return blastulaSessionSocket;
}
boolean matches(String abi) {
return mABIList.contains(abi);
}
@@ -259,12 +301,14 @@ public class ZygoteProcess {
String instructionSet,
String appDataDir,
String invokeWith,
boolean useBlastulaPool,
String[] zygoteArgs) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith,
/*startChildZygote=*/false, zygoteArgs);
/*startChildZygote=*/false,
useBlastulaPool, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
@@ -312,59 +356,127 @@ public class ZygoteProcess {
*/
@GuardedBy("mLock")
private static Process.ProcessStartResult zygoteSendArgsAndGetResult(
ZygoteState zygoteState, ArrayList<String> args)
ZygoteState zygoteState, boolean useBlastulaPool, ArrayList<String> args)
throws ZygoteStartFailedEx {
try {
// Throw early if any of the arguments are malformed. This means we can
// avoid writing a partial response to the zygote.
int sz = args.size();
for (int i = 0; i < sz; i++) {
if (args.get(i).indexOf('\n') >= 0) {
throw new ZygoteStartFailedEx("embedded newlines not allowed");
// Throw early if any of the arguments are malformed. This means we can
// avoid writing a partial response to the zygote.
for (String arg : args) {
if (arg.indexOf('\n') >= 0) {
throw new ZygoteStartFailedEx("embedded newlines not allowed");
}
}
/**
* See com.android.internal.os.SystemZygoteInit.readArgumentList()
* Presently the wire format to the zygote process is:
* a) a count of arguments (argc, in essence)
* 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, followed by boolean to
* indicate whether a wrapper process was used.
*/
String msgStr = Integer.toString(args.size()) + "\n"
+ String.join("\n", args) + "\n";
// Should there be a timeout on this?
Process.ProcessStartResult result = new Process.ProcessStartResult();
// TODO (chriswailes): Move branch body into separate function.
if (useBlastulaPool && Zygote.BLASTULA_POOL_ENABLED && isValidBlastulaCommand(args)) {
LocalSocket blastulaSessionSocket = null;
try {
blastulaSessionSocket = zygoteState.getBlastulaSessionSocket();
final BufferedWriter blastulaWriter =
new BufferedWriter(
new OutputStreamWriter(blastulaSessionSocket.getOutputStream()),
Zygote.SOCKET_BUFFER_SIZE);
final DataInputStream blastulaReader =
new DataInputStream(blastulaSessionSocket.getInputStream());
blastulaWriter.write(msgStr);
blastulaWriter.flush();
result.pid = blastulaReader.readInt();
// Blastulas can't be used to spawn processes that need wrappers.
result.usingWrapper = false;
if (result.pid < 0) {
throw new ZygoteStartFailedEx("Blastula specialization failed");
}
return result;
} catch (IOException ex) {
// If there was an IOException using the blastula pool we will log the error and
// attempt to start the process through the Zygote.
Log.e(LOG_TAG, "IO Exception while communicating with blastula pool - "
+ ex.toString());
} finally {
try {
blastulaSessionSocket.close();
} catch (IOException ex) {
Log.e(LOG_TAG, "Failed to close blastula session socket: " + ex.getMessage());
}
}
}
/**
* See com.android.internal.os.SystemZygoteInit.readArgumentList()
* Presently the wire format to the zygote process is:
* a) a count of arguments (argc, in essence)
* 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, followed by boolean to
* indicate whether a wrapper process was used.
*/
final BufferedWriter writer = zygoteState.mZygoteOutputWriter;
final DataInputStream inputStream = zygoteState.mZygoteInputStream;
try {
final BufferedWriter zygoteWriter = zygoteState.mZygoteOutputWriter;
final DataInputStream zygoteInputStream = zygoteState.mZygoteInputStream;
writer.write(Integer.toString(args.size()));
writer.newLine();
for (int i = 0; i < sz; i++) {
String arg = args.get(i);
writer.write(arg);
writer.newLine();
}
writer.flush();
// Should there be a timeout on this?
Process.ProcessStartResult result = new Process.ProcessStartResult();
zygoteWriter.write(msgStr);
zygoteWriter.flush();
// Always read the entire result from the input stream to avoid leaving
// bytes in the stream for future process starts to accidentally stumble
// upon.
result.pid = inputStream.readInt();
result.usingWrapper = inputStream.readBoolean();
if (result.pid < 0) {
throw new ZygoteStartFailedEx("fork() failed");
}
return result;
result.pid = zygoteInputStream.readInt();
result.usingWrapper = zygoteInputStream.readBoolean();
} catch (IOException ex) {
zygoteState.close();
Log.e(LOG_TAG, "IO Exception while communicating with Zygote - "
+ ex.toString());
throw new ZygoteStartFailedEx(ex);
}
if (result.pid < 0) {
throw new ZygoteStartFailedEx("fork() failed");
}
return result;
}
/**
* Flags that may not be passed to a blastula.
*/
private static final String[] INVALID_BLASTULA_FLAGS = {
"--query-abi-list",
"--get-pid",
"--preload-default",
"--preload-package",
"--start-child-zygote",
"--set-api-blacklist-exemptions",
"--hidden-api-log-sampling-rate",
"--invoke-with"
};
/**
* Tests a command list to see if it is valid to send to a blastula.
* @param args Zygote/Blastula command arguments
* @return True if the command can be passed to a blastula; false otherwise
*/
private static boolean isValidBlastulaCommand(ArrayList<String> args) {
for (String flag : args) {
for (String badFlag : INVALID_BLASTULA_FLAGS) {
if (flag.startsWith(badFlag)) {
return false;
}
}
}
return true;
}
/**
@@ -400,6 +512,7 @@ public class ZygoteProcess {
String appDataDir,
String invokeWith,
boolean startChildZygote,
boolean useBlastulaPool,
String[] extraArgs)
throws ZygoteStartFailedEx {
ArrayList<String> argsForZygote = new ArrayList<String>();
@@ -469,7 +582,9 @@ public class ZygoteProcess {
}
synchronized(mLock) {
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi),
useBlastulaPool,
argsForZygote);
}
}
@@ -633,7 +748,7 @@ public class ZygoteProcess {
if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
try {
primaryZygoteState =
ZygoteState.connect(mZygoteSocketAddress);
ZygoteState.connect(mZygoteSocketAddress, mBlastulaPoolSocketAddress);
} catch (IOException ioe) {
throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
}
@@ -650,7 +765,8 @@ public class ZygoteProcess {
if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
try {
secondaryZygoteState =
ZygoteState.connect(mZygoteSecondarySocketAddress);
ZygoteState.connect(mZygoteSecondarySocketAddress,
mBlastulaPoolSecondarySocketAddress);
} catch (IOException ioe) {
throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
}
@@ -737,7 +853,7 @@ public class ZygoteProcess {
for (int n = 20; n >= 0; n--) {
try {
final ZygoteState zs =
ZygoteState.connect(zygoteSocketAddress);
ZygoteState.connect(zygoteSocketAddress, null);
zs.close();
return;
} catch (IOException ioe) {
@@ -778,7 +894,7 @@ public class ZygoteProcess {
result = startViaZygote(processClass, niceName, uid, gid,
gids, runtimeFlags, 0 /* mountExternal */, 0 /* targetSdkVersion */, seInfo,
abi, instructionSet, null /* appDataDir */, null /* invokeWith */,
true /* startChildZygote */, extraArgs);
true /* startChildZygote */, false /* useBlastulaPool */, extraArgs);
} catch (ZygoteStartFailedEx ex) {
throw new RuntimeException("Starting child-zygote through Zygote failed", ex);
}

View File

@@ -105,7 +105,13 @@ public final class Zygote {
/** Number of bytes sent to the Zygote over blastula pipes or the pool event FD */
public static final int BLASTULA_MANAGEMENT_MESSAGE_BYTES = 8;
/** If the blastula pool should be created and used to start applications */
/**
* If the blastula pool should be created and used to start applications.
*
* Setting this value to false will disable the creation, maintenance, and use of the blastula
* pool. When the blastula pool is disabled the application lifecycle will be identical to
* previous versions of Android.
*/
public static final boolean BLASTULA_POOL_ENABLED = false;
/**
@@ -155,6 +161,11 @@ public final class Zygote {
// TODO (chriswailes): This must be updated at the same time as sBlastulaPoolMax.
static int sBlastulaPoolRefillThreshold = (sBlastulaPoolMax / 2);
/**
* @hide for internal use only
*/
public static final int SOCKET_BUFFER_SIZE = 256;
private static LocalServerSocket sBlastulaPoolSocket = null;
/** a prototype instance for a future List.toArray() */

View File

@@ -224,7 +224,7 @@ class ZygoteConnection {
fdsToClose[0] = fd.getInt$();
}
fd = zygoteServer.getServerSocketFileDescriptor();
fd = zygoteServer.getZygoteSocketFileDescriptor();
if (fd != null) {
fdsToClose[1] = fd.getInt$();

View File

@@ -269,7 +269,7 @@ public class ZygoteInit {
try {
BufferedReader br =
new BufferedReader(new InputStreamReader(is), 256);
new BufferedReader(new InputStreamReader(is), Zygote.SOCKET_BUFFER_SIZE);
int count = 0;
String line;
@@ -750,7 +750,7 @@ public class ZygoteInit {
throw new RuntimeException("Failed to setpgid(0,0)", ex);
}
final Runnable caller;
Runnable caller;
try {
// Report Zygote start time to tron unless it is a runtime restart
if (!"1".equals(SystemProperties.get("sys.boot_completed"))) {
@@ -786,7 +786,17 @@ public class ZygoteInit {
throw new RuntimeException("No ABI list supplied.");
}
zygoteServer.registerServerSocketFromEnv(socketName);
// TODO (chriswailes): Wrap these three calls in a helper function?
final String blastulaSocketName =
socketName.equals(ZygoteProcess.ZYGOTE_SOCKET_NAME)
? ZygoteProcess.BLASTULA_POOL_SOCKET_NAME
: ZygoteProcess.BLASTULA_POOL_SECONDARY_SOCKET_NAME;
zygoteServer.createZygoteSocket(socketName);
Zygote.createBlastulaSocket(blastulaSocketName);
Zygote.getSocketFDs(socketName.equals(ZygoteProcess.ZYGOTE_SOCKET_NAME));
// In some configurations, we avoid preloading resources and classes eagerly.
// In such cases, we will preload things prior to our first fork.
if (!enableLazyPreload) {
@@ -829,11 +839,18 @@ public class ZygoteInit {
}
}
Log.i(TAG, "Accepting command socket connections");
// If the return value is null then this is the zygote process
// returning to the normal control flow. If it returns a Runnable
// object then this is a blastula that has finished specializing.
caller = Zygote.initBlastulaPool();
// The select loop returns early in the child process after a fork and
// loops forever in the zygote.
caller = zygoteServer.runSelectLoop(abiList);
if (caller == null) {
Log.i(TAG, "Accepting command socket connections");
// The select loop returns early in the child process after a fork and
// loops forever in the zygote.
caller = zygoteServer.runSelectLoop(abiList);
}
} catch (Throwable ex) {
Log.e(TAG, "System zygote died with exception", ex);
throw ex;

View File

@@ -26,6 +26,8 @@ import android.system.StructPollfd;
import android.util.Log;
import android.util.Slog;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.FileDescriptor;
import java.io.IOException;
import java.util.ArrayList;
@@ -40,18 +42,17 @@ import java.util.ArrayList;
* client protocol.
*/
class ZygoteServer {
// TODO (chriswailes): Change this so it is set with Zygote or ZygoteSecondary as appropriate
public static final String TAG = "ZygoteServer";
private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
/**
* Listening socket that accepts new server connections.
*/
private LocalServerSocket mServerSocket;
private LocalServerSocket mZygoteSocket;
/**
* Whether or not mServerSocket's underlying FD should be closed directly.
* If mServerSocket is created with an existing FD, closing the socket does
* Whether or not mZygoteSocket's underlying FD should be closed directly.
* If mZygoteSocket is created with an existing FD, closing the socket does
* not close the FD and it must be closed explicitly. If the socket is created
* with a name instead, then closing the socket will close the underlying FD
* and it should not be double-closed.
@@ -70,31 +71,17 @@ class ZygoteServer {
}
/**
* Registers a server socket for zygote command connections. This locates the server socket
* file descriptor through an ANDROID_SOCKET_ environment variable.
* Creates a managed object representing the Zygote socket that has already
* been initialized and bound by init.
*
* TODO (chriswailes): Move the name selection logic into this function.
*
* @throws RuntimeException when open fails
*/
void registerServerSocketFromEnv(String socketName) {
if (mServerSocket == null) {
int fileDesc;
final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
try {
String env = System.getenv(fullSocketName);
fileDesc = Integer.parseInt(env);
} catch (RuntimeException ex) {
throw new RuntimeException(fullSocketName + " unset or invalid", ex);
}
try {
FileDescriptor fd = new FileDescriptor();
fd.setInt$(fileDesc);
mServerSocket = new LocalServerSocket(fd);
mCloseSocketFd = true;
} catch (IOException ex) {
throw new RuntimeException(
"Error binding to local socket '" + fileDesc + "'", ex);
}
void createZygoteSocket(String socketName) {
if (mZygoteSocket == null) {
mZygoteSocket = Zygote.createManagedSocketFromInitSocket(socketName);
mCloseSocketFd = true;
}
}
@@ -103,9 +90,9 @@ class ZygoteServer {
* at the specified name in the abstract socket namespace.
*/
void registerServerSocketAtAbstractName(String socketName) {
if (mServerSocket == null) {
if (mZygoteSocket == null) {
try {
mServerSocket = new LocalServerSocket(socketName);
mZygoteSocket = new LocalServerSocket(socketName);
mCloseSocketFd = false;
} catch (IOException ex) {
throw new RuntimeException(
@@ -120,7 +107,7 @@ class ZygoteServer {
*/
private ZygoteConnection acceptCommandPeer(String abiList) {
try {
return createNewConnection(mServerSocket.accept(), abiList);
return createNewConnection(mZygoteSocket.accept(), abiList);
} catch (IOException ex) {
throw new RuntimeException(
"IOException during accept()", ex);
@@ -138,9 +125,9 @@ class ZygoteServer {
*/
void closeServerSocket() {
try {
if (mServerSocket != null) {
FileDescriptor fd = mServerSocket.getFileDescriptor();
mServerSocket.close();
if (mZygoteSocket != null) {
FileDescriptor fd = mZygoteSocket.getFileDescriptor();
mZygoteSocket.close();
if (fd != null && mCloseSocketFd) {
Os.close(fd);
}
@@ -151,7 +138,7 @@ class ZygoteServer {
Log.e(TAG, "Zygote: error closing descriptor", ex);
}
mServerSocket = null;
mZygoteSocket = null;
}
/**
@@ -160,8 +147,8 @@ class ZygoteServer {
* closure after a child process is forked off.
*/
FileDescriptor getServerSocketFileDescriptor() {
return mServerSocket.getFileDescriptor();
FileDescriptor getZygoteSocketFileDescriptor() {
return mZygoteSocket.getFileDescriptor();
}
/**
@@ -170,36 +157,67 @@ class ZygoteServer {
* worth at a time.
*/
Runnable runSelectLoop(String abiList) {
ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
ArrayList<FileDescriptor> socketFDs = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
fds.add(mServerSocket.getFileDescriptor());
socketFDs.add(mZygoteSocket.getFileDescriptor());
peers.add(null);
while (true) {
StructPollfd[] pollFds = new StructPollfd[fds.size()];
for (int i = 0; i < pollFds.length; ++i) {
pollFds[i] = new StructPollfd();
pollFds[i].fd = fds.get(i);
pollFds[i].events = (short) POLLIN;
int[] blastulaPipeFDs = Zygote.getBlastulaPipeFDs();
// Space for all of the socket FDs, the Blastula Pool Event FD, and
// all of the open blastula read pipe FDs.
StructPollfd[] pollFDs =
new StructPollfd[socketFDs.size() + 1 + blastulaPipeFDs.length];
int pollIndex = 0;
for (FileDescriptor socketFD : socketFDs) {
pollFDs[pollIndex] = new StructPollfd();
pollFDs[pollIndex].fd = socketFD;
pollFDs[pollIndex].events = (short) POLLIN;
++pollIndex;
}
final int blastulaPoolEventFDIndex = pollIndex;
pollFDs[pollIndex] = new StructPollfd();
pollFDs[pollIndex].fd = Zygote.sBlastulaPoolEventFD;
pollFDs[pollIndex].events = (short) POLLIN;
++pollIndex;
for (int blastulaPipeFD : blastulaPipeFDs) {
FileDescriptor managedFd = new FileDescriptor();
managedFd.setInt$(blastulaPipeFD);
pollFDs[pollIndex] = new StructPollfd();
pollFDs[pollIndex].fd = managedFd;
pollFDs[pollIndex].events = (short) POLLIN;
++pollIndex;
}
try {
Os.poll(pollFds, -1);
Os.poll(pollFDs, -1);
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}
for (int i = pollFds.length - 1; i >= 0; --i) {
if ((pollFds[i].revents & POLLIN) == 0) {
while (--pollIndex >= 0) {
if ((pollFDs[pollIndex].revents & POLLIN) == 0) {
continue;
}
if (i == 0) {
if (pollIndex == 0) {
// Zygote server socket
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
fds.add(newPeer.getFileDescriptor());
} else {
socketFDs.add(newPeer.getFileDescriptor());
} else if (pollIndex < blastulaPoolEventFDIndex) {
// Session socket accepted from the Zygote server socket
try {
ZygoteConnection connection = peers.get(i);
ZygoteConnection connection = peers.get(pollIndex);
final Runnable command = connection.processOneCommand(this);
if (mIsForkChild) {
@@ -217,12 +235,12 @@ class ZygoteServer {
}
// 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.
// 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);
peers.remove(pollIndex);
socketFDs.remove(pollIndex);
}
}
} catch (Exception e) {
@@ -234,13 +252,13 @@ class ZygoteServer {
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);
// 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(pollIndex);
conn.closeSocket();
fds.remove(i);
socketFDs.remove(pollIndex);
} 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()
@@ -254,6 +272,55 @@ class ZygoteServer {
// is returned.
mIsForkChild = false;
}
} else {
// Either the blastula pool event FD or a blastula reporting pipe.
// If this is the event FD the payload will be the number of blastulas removed.
// If this is a reporting pipe FD the payload will be the PID of the blastula
// that was just specialized.
long messagePayload = -1;
try {
byte[] buffer = new byte[Zygote.BLASTULA_MANAGEMENT_MESSAGE_BYTES];
int readBytes = Os.read(pollFDs[pollIndex].fd, buffer, 0, buffer.length);
if (readBytes == Zygote.BLASTULA_MANAGEMENT_MESSAGE_BYTES) {
DataInputStream inputStream =
new DataInputStream(new ByteArrayInputStream(buffer));
messagePayload = inputStream.readLong();
} else {
Log.e(TAG, "Incomplete read from blastula management FD of size "
+ readBytes);
continue;
}
} catch (Exception ex) {
if (pollIndex == blastulaPoolEventFDIndex) {
Log.e(TAG, "Failed to read from blastula pool event FD: "
+ ex.getMessage());
} else {
Log.e(TAG, "Failed to read from blastula reporting pipe: "
+ ex.getMessage());
}
continue;
}
if (pollIndex > blastulaPoolEventFDIndex) {
Zygote.removeBlastulaTableEntry((int) messagePayload);
}
int[] sessionSocketRawFDs =
socketFDs.subList(1, socketFDs.size())
.stream()
.mapToInt(fd -> fd.getInt$())
.toArray();
final Runnable command = Zygote.fillBlastulaPool(sessionSocketRawFDs);
if (command != null) {
return command;
}
}
}
}

View File

@@ -1252,7 +1252,7 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
fds_to_close.insert(fds_to_close.end(), blastula_pipes.begin(), blastula_pipes.end());
fds_to_ignore.insert(fds_to_ignore.end(), blastula_pipes.begin(), blastula_pipes.end());
// fds_to_close.push_back(gBlastulaPoolSocketFD);
fds_to_close.push_back(gBlastulaPoolSocketFD);
if (gBlastulaPoolEventFD != -1) {
fds_to_close.push_back(gBlastulaPoolEventFD);
@@ -1277,7 +1277,7 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer(
std::vector<int> fds_to_close(MakeBlastulaPipeReadFDVector()),
fds_to_ignore(fds_to_close);
// fds_to_close.push_back(gBlastulaPoolSocketFD);
fds_to_close.push_back(gBlastulaPoolSocketFD);
if (gBlastulaPoolEventFD != -1) {
fds_to_close.push_back(gBlastulaPoolEventFD);