From 8f8d187a78b48109911a800ba5993b645d3015c5 Mon Sep 17 00:00:00 2001 From: Robert Sesek Date: Fri, 18 Mar 2016 16:52:57 -0400 Subject: [PATCH] Split the zygote logic out of android.os.Process into a new ZygoteProcess class. There is no functional change. This is to support adding new types of zygotes that all operate using the same protocol. Bug: 21643067 (cherry picked from commit 94e824bc1b5f83024b7c9a228a8f2d0cd1347d11) (cherry picked from commit 96b49848e7784bc4b40ffd01e2524461e1efcab7) Change-Id: I2e12057e4c2e7567f909d699b487e70b1664cca8 --- core/java/android/os/Process.java | 361 +------------- core/java/android/os/ZygoteProcess.java | 447 ++++++++++++++++++ .../com/android/internal/os/ZygoteInit.java | 4 +- .../server/am/ActivityManagerService.java | 2 +- 4 files changed, 456 insertions(+), 358 deletions(-) create mode 100644 core/java/android/os/ZygoteProcess.java diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index f664e70cf7be2..6aa9fac8d48bd 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -16,34 +16,9 @@ package android.os; -import android.net.LocalSocket; -import android.net.LocalSocketAddress; import android.system.Os; import android.util.Log; -import com.android.internal.os.Zygote; import dalvik.system.VMRuntime; -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; - -/*package*/ class ZygoteStartFailedEx extends Exception { - ZygoteStartFailedEx(String s) { - super(s); - } - - ZygoteStartFailedEx(Throwable cause) { - super(cause); - } - - ZygoteStartFailedEx(String s, Throwable cause) { - super(s, cause); - } -} /** * Tools for managing OS processes. @@ -387,83 +362,11 @@ public class Process { private static long sStartUptimeMillis; /** - * State for communicating with the zygote process. - * - * @hide for internal use only. + * State associated with the zygote process. + * @hide */ - public static class ZygoteState { - final LocalSocket socket; - final DataInputStream inputStream; - final BufferedWriter writer; - final List abiList; - - boolean mClosed; - - private ZygoteState(LocalSocket socket, DataInputStream inputStream, - BufferedWriter writer, List abiList) { - this.socket = socket; - this.inputStream = inputStream; - this.writer = writer; - this.abiList = abiList; - } - - public static ZygoteState connect(String socketAddress) throws IOException { - DataInputStream zygoteInputStream = null; - BufferedWriter zygoteWriter = null; - final LocalSocket zygoteSocket = new LocalSocket(); - - try { - zygoteSocket.connect(new LocalSocketAddress(socketAddress, - LocalSocketAddress.Namespace.RESERVED)); - - zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream()); - - zygoteWriter = new BufferedWriter(new OutputStreamWriter( - zygoteSocket.getOutputStream()), 256); - } catch (IOException ex) { - try { - zygoteSocket.close(); - } catch (IOException ignore) { - } - - throw ex; - } - - 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); - } - - public 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; + public static final ZygoteProcess zygoteProcess = + new ZygoteProcess(ZYGOTE_SOCKET, SECONDARY_ZYGOTE_SOCKET); /** * Start a new process. @@ -509,263 +412,9 @@ public class Process { String instructionSet, String appDataDir, String[] zygoteArgs) { - try { - return startViaZygote(processClass, niceName, uid, gid, gids, + return zygoteProcess.start(processClass, niceName, uid, gid, gids, debugFlags, mountExternal, targetSdkVersion, seInfo, abi, instructionSet, appDataDir, zygoteArgs); - } catch (ZygoteStartFailedEx ex) { - Log.e(LOG_TAG, - "Starting VM process through Zygote failed"); - throw new RuntimeException( - "Starting VM process through Zygote failed", ex); - } - } - - /** retry interval for opening a zygote socket */ - static final int ZYGOTE_RETRY_MILLIS = 500; - - /** - * Queries the zygote for the list of ABIS it supports. - * - * @throws ZygoteStartFailedEx if the query failed. - */ - private static String getAbiList(BufferedWriter writer, DataInputStream inputStream) - throws IOException { - // 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(); - - // The response is a length prefixed stream of ASCII bytes. - int numBytes = inputStream.readInt(); - byte[] bytes = new byte[numBytes]; - inputStream.readFully(bytes); - - return new String(bytes, StandardCharsets.US_ASCII); - } - - /** - * 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. - * - * @throws ZygoteStartFailedEx if process start failed for any reason - */ - private static ProcessStartResult zygoteSendArgsAndGetResult( - ZygoteState zygoteState, ArrayList args) - throws ZygoteStartFailedEx { - try { - /** - * See com.android.internal.os.ZygoteInit.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.writer; - final DataInputStream inputStream = zygoteState.inputStream; - - writer.write(Integer.toString(args.size())); - writer.newLine(); - - int sz = args.size(); - for (int i = 0; i < sz; i++) { - String arg = args.get(i); - if (arg.indexOf('\n') >= 0) { - throw new ZygoteStartFailedEx( - "embedded newlines not allowed"); - } - writer.write(arg); - writer.newLine(); - } - - writer.flush(); - - // Should there be a timeout on this? - ProcessStartResult result = new ProcessStartResult(); - result.pid = inputStream.readInt(); - if (result.pid < 0) { - throw new ZygoteStartFailedEx("fork() failed"); - } - result.usingWrapper = inputStream.readBoolean(); - return result; - } catch (IOException ex) { - zygoteState.close(); - throw new ZygoteStartFailedEx(ex); - } - } - - /** - * Starts a new process via the zygote mechanism. - * - * @param processClass Class name whose static main() to run - * @param niceName 'nice' process name to appear in ps - * @param uid a POSIX uid that the new process should setuid() to - * @param gid a POSIX gid that the new process shuold setgid() to - * @param gids null-ok; a list of supplementary group IDs that the - * new process should setgroup() to. - * @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 instructionSet null-ok the instruction set to use. - * @param appDataDir null-ok the data directory of the app. - * @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 - */ - private static ProcessStartResult startViaZygote(final String processClass, - final String niceName, - final int uid, final int gid, - final int[] gids, - int debugFlags, int mountExternal, - int targetSdkVersion, - String seInfo, - String abi, - String instructionSet, - String appDataDir, - String[] extraArgs) - throws ZygoteStartFailedEx { - synchronized(Process.class) { - ArrayList argsForZygote = new ArrayList(); - - // --runtime-args, --setuid=, --setgid=, - // and --setgroups= must go first - argsForZygote.add("--runtime-args"); - argsForZygote.add("--setuid=" + uid); - argsForZygote.add("--setgid=" + gid); - if ((debugFlags & Zygote.DEBUG_ENABLE_JNI_LOGGING) != 0) { - argsForZygote.add("--enable-jni-logging"); - } - if ((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE) != 0) { - argsForZygote.add("--enable-safemode"); - } - if ((debugFlags & Zygote.DEBUG_ENABLE_DEBUGGER) != 0) { - argsForZygote.add("--enable-debugger"); - } - if ((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) != 0) { - argsForZygote.add("--enable-checkjni"); - } - if ((debugFlags & Zygote.DEBUG_GENERATE_DEBUG_INFO) != 0) { - argsForZygote.add("--generate-debug-info"); - } - if ((debugFlags & Zygote.DEBUG_ALWAYS_JIT) != 0) { - argsForZygote.add("--always-jit"); - } - if ((debugFlags & Zygote.DEBUG_NATIVE_DEBUGGABLE) != 0) { - argsForZygote.add("--native-debuggable"); - } - if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) { - argsForZygote.add("--enable-assert"); - } - if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) { - argsForZygote.add("--mount-external-default"); - } else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) { - argsForZygote.add("--mount-external-read"); - } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) { - argsForZygote.add("--mount-external-write"); - } - argsForZygote.add("--target-sdk-version=" + targetSdkVersion); - - //TODO optionally enable debuger - //argsForZygote.add("--enable-debugger"); - - // --setgroups is a comma-separated list - if (gids != null && gids.length > 0) { - StringBuilder sb = new StringBuilder(); - sb.append("--setgroups="); - - int sz = gids.length; - for (int i = 0; i < sz; i++) { - if (i != 0) { - sb.append(','); - } - sb.append(gids[i]); - } - - argsForZygote.add(sb.toString()); - } - - if (niceName != null) { - argsForZygote.add("--nice-name=" + niceName); - } - - if (seInfo != null) { - argsForZygote.add("--seinfo=" + seInfo); - } - - if (instructionSet != null) { - argsForZygote.add("--instruction-set=" + instructionSet); - } - - if (appDataDir != null) { - argsForZygote.add("--app-data-dir=" + appDataDir); - } - - argsForZygote.add(processClass); - - if (extraArgs != null) { - for (String arg : extraArgs) { - argsForZygote.add(arg); - } - } - - return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote); - } - } - - /** - * Tries to establish a connection to the zygote that handles a given {@code abi}. Might block and retry if the - * zygote is unresponsive. This method is a no-op if a connection is already open. - * - * @hide - */ - public static void establishZygoteConnectionForAbi(String abi) { - try { - openZygoteSocketIfNeeded(abi); - } catch (ZygoteStartFailedEx ex) { - throw new RuntimeException("Unable to connect to zygote for abi: " + abi, ex); - } - } - - /** - * 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()) { - try { - primaryZygoteState = ZygoteState.connect(ZYGOTE_SOCKET); - } catch (IOException ioe) { - throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe); - } - } - - if (primaryZygoteState.matches(abi)) { - return primaryZygoteState; - } - - // The primary zygote didn't match. Try the secondary. - if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) { - try { - secondaryZygoteState = ZygoteState.connect(SECONDARY_ZYGOTE_SOCKET); - } catch (IOException ioe) { - throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe); - } - } - - if (secondaryZygoteState.matches(abi)) { - return secondaryZygoteState; - } - - throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi); } /** diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java new file mode 100644 index 0000000000000..d7a72960f4c49 --- /dev/null +++ b/core/java/android/os/ZygoteProcess.java @@ -0,0 +1,447 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import android.net.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; + +/*package*/ class ZygoteStartFailedEx extends Exception { + ZygoteStartFailedEx(String s) { + super(s); + } + + ZygoteStartFailedEx(Throwable cause) { + super(cause); + } + + ZygoteStartFailedEx(String s, Throwable cause) { + super(s, cause); + } +} + +/** + * Maintains communication state with the zygote processes. This class is responsible + * for the sockets opened to the zygotes and for starting processes on behalf of the + * {@link android.os.Process} class. + * + * {@hide} + */ +public class ZygoteProcess { + private static final String LOG_TAG = "ZygoteProcess"; + + /** + * The name of the socket used to communicate with the primary zygote. + */ + private final String mSocket; + + /** + * The name of the secondary (alternate ABI) zygote socket. + */ + private final String mSecondarySocket; + + public ZygoteProcess(String primarySocket, String secondarySocket) { + mSocket = primarySocket; + mSecondarySocket = secondarySocket; + } + + /** + * State for communicating with the zygote process. + */ + public static class ZygoteState { + final LocalSocket socket; + final DataInputStream inputStream; + final BufferedWriter writer; + final List abiList; + + boolean mClosed; + + private ZygoteState(LocalSocket socket, DataInputStream inputStream, + BufferedWriter writer, List abiList) { + this.socket = socket; + this.inputStream = inputStream; + this.writer = writer; + this.abiList = abiList; + } + + public static ZygoteState connect(String socketAddress) throws IOException { + DataInputStream zygoteInputStream = null; + BufferedWriter zygoteWriter = null; + final LocalSocket zygoteSocket = new LocalSocket(); + + try { + zygoteSocket.connect(new LocalSocketAddress(socketAddress, + LocalSocketAddress.Namespace.RESERVED)); + + zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream()); + + zygoteWriter = new BufferedWriter(new OutputStreamWriter( + zygoteSocket.getOutputStream()), 256); + } catch (IOException ex) { + try { + zygoteSocket.close(); + } catch (IOException ignore) { + } + + throw ex; + } + + 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); + } + + public 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. + */ + private ZygoteState primaryZygoteState; + + /** + * The state of the connection to the secondary zygote. + */ + private ZygoteState secondaryZygoteState; + + /** + * Start a new process. + * + *

If processes are enabled, a new process is created and the + * static main() function of a processClass is executed there. + * The process will continue running after this function returns. + * + *

If processes are not enabled, a new thread in the caller's + * process is created and main() of processClass called there. + * + *

The niceName parameter, if not an empty string, is a custom name to + * give to the process instead of using processClass. This allows you to + * make easily identifyable processes even if you are using the same base + * processClass to start them. + * + * @param processClass The class to use as the process's main entry + * point. + * @param niceName A more readable name to use for the process. + * @param uid The user-id under which the process will run. + * @param gid The group-id under which the process will run. + * @param gids Additional group-ids associated with the process. + * @param debugFlags Additional flags. + * @param targetSdkVersion The target SDK version for the app. + * @param seInfo null-ok SELinux information for the new process. + * @param abi non-null the ABI this app should be started with. + * @param instructionSet null-ok the instruction set to use. + * @param appDataDir null-ok the data directory of the app. + * @param zygoteArgs Additional arguments to supply to the zygote process. + * + * @return An object that describes the result of the attempt to start the process. + * @throws RuntimeException on fatal start failure + */ + public final Process.ProcessStartResult start(final String processClass, + final String niceName, + int uid, int gid, int[] gids, + int debugFlags, int mountExternal, + int targetSdkVersion, + String seInfo, + String abi, + String instructionSet, + String appDataDir, + String[] zygoteArgs) { + try { + return startViaZygote(processClass, niceName, uid, gid, gids, + debugFlags, mountExternal, targetSdkVersion, seInfo, + abi, instructionSet, appDataDir, zygoteArgs); + } catch (ZygoteStartFailedEx ex) { + Log.e(LOG_TAG, + "Starting VM process through Zygote failed"); + throw new RuntimeException( + "Starting VM process through Zygote failed", ex); + } + } + + /** retry interval for opening a zygote socket */ + static final int ZYGOTE_RETRY_MILLIS = 500; + + /** + * Queries the zygote for the list of ABIS it supports. + * + * @throws ZygoteStartFailedEx if the query failed. + */ + private static String getAbiList(BufferedWriter writer, DataInputStream inputStream) + throws IOException { + // 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(); + + // The response is a length prefixed stream of ASCII bytes. + int numBytes = inputStream.readInt(); + byte[] bytes = new byte[numBytes]; + inputStream.readFully(bytes); + + return new String(bytes, StandardCharsets.US_ASCII); + } + + /** + * 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. + * + * @throws ZygoteStartFailedEx if process start failed for any reason + */ + private static Process.ProcessStartResult zygoteSendArgsAndGetResult( + ZygoteState zygoteState, ArrayList args) + throws ZygoteStartFailedEx { + try { + /** + * 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.writer; + final DataInputStream inputStream = zygoteState.inputStream; + + writer.write(Integer.toString(args.size())); + writer.newLine(); + + int sz = args.size(); + for (int i = 0; i < sz; i++) { + String arg = args.get(i); + if (arg.indexOf('\n') >= 0) { + throw new ZygoteStartFailedEx( + "embedded newlines not allowed"); + } + writer.write(arg); + writer.newLine(); + } + + writer.flush(); + + // Should there be a timeout on this? + Process.ProcessStartResult result = new Process.ProcessStartResult(); + result.pid = inputStream.readInt(); + if (result.pid < 0) { + throw new ZygoteStartFailedEx("fork() failed"); + } + result.usingWrapper = inputStream.readBoolean(); + return result; + } catch (IOException ex) { + zygoteState.close(); + throw new ZygoteStartFailedEx(ex); + } + } + + /** + * Starts a new process via the zygote mechanism. + * + * @param processClass Class name whose static main() to run + * @param niceName 'nice' process name to appear in ps + * @param uid a POSIX uid that the new process should setuid() to + * @param gid a POSIX gid that the new process shuold setgid() to + * @param gids null-ok; a list of supplementary group IDs that the + * new process should setgroup() to. + * @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 instructionSet null-ok the instruction set to use. + * @param appDataDir null-ok the data directory of the app. + * @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 + */ + private Process.ProcessStartResult startViaZygote(final String processClass, + final String niceName, + final int uid, final int gid, + final int[] gids, + int debugFlags, int mountExternal, + int targetSdkVersion, + String seInfo, + String abi, + String instructionSet, + String appDataDir, + String[] extraArgs) + throws ZygoteStartFailedEx { + synchronized(Process.class) { + ArrayList argsForZygote = new ArrayList(); + + // --runtime-args, --setuid=, --setgid=, + // and --setgroups= must go first + argsForZygote.add("--runtime-args"); + argsForZygote.add("--setuid=" + uid); + argsForZygote.add("--setgid=" + gid); + if ((debugFlags & Zygote.DEBUG_ENABLE_JNI_LOGGING) != 0) { + argsForZygote.add("--enable-jni-logging"); + } + if ((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE) != 0) { + argsForZygote.add("--enable-safemode"); + } + if ((debugFlags & Zygote.DEBUG_ENABLE_DEBUGGER) != 0) { + argsForZygote.add("--enable-debugger"); + } + if ((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) != 0) { + argsForZygote.add("--enable-checkjni"); + } + if ((debugFlags & Zygote.DEBUG_GENERATE_DEBUG_INFO) != 0) { + argsForZygote.add("--generate-debug-info"); + } + if ((debugFlags & Zygote.DEBUG_ALWAYS_JIT) != 0) { + argsForZygote.add("--always-jit"); + } + if ((debugFlags & Zygote.DEBUG_NATIVE_DEBUGGABLE) != 0) { + argsForZygote.add("--native-debuggable"); + } + if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) { + argsForZygote.add("--enable-assert"); + } + if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) { + argsForZygote.add("--mount-external-default"); + } else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) { + argsForZygote.add("--mount-external-read"); + } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) { + argsForZygote.add("--mount-external-write"); + } + argsForZygote.add("--target-sdk-version=" + targetSdkVersion); + + //TODO optionally enable debuger + //argsForZygote.add("--enable-debugger"); + + // --setgroups is a comma-separated list + if (gids != null && gids.length > 0) { + StringBuilder sb = new StringBuilder(); + sb.append("--setgroups="); + + int sz = gids.length; + for (int i = 0; i < sz; i++) { + if (i != 0) { + sb.append(','); + } + sb.append(gids[i]); + } + + argsForZygote.add(sb.toString()); + } + + if (niceName != null) { + argsForZygote.add("--nice-name=" + niceName); + } + + if (seInfo != null) { + argsForZygote.add("--seinfo=" + seInfo); + } + + if (instructionSet != null) { + argsForZygote.add("--instruction-set=" + instructionSet); + } + + if (appDataDir != null) { + argsForZygote.add("--app-data-dir=" + appDataDir); + } + + argsForZygote.add(processClass); + + if (extraArgs != null) { + for (String arg : extraArgs) { + argsForZygote.add(arg); + } + } + + return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote); + } + } + + /** + * Tries to establish a connection to the zygote that handles a given {@code abi}. Might block + * and retry if the zygote is unresponsive. This method is a no-op if a connection is + * already open. + */ + public void establishZygoteConnectionForAbi(String abi) { + try { + openZygoteSocketIfNeeded(abi); + } catch (ZygoteStartFailedEx ex) { + throw new RuntimeException("Unable to connect to zygote for abi: " + abi, ex); + } + } + + /** + * Tries to open socket to Zygote process if not already open. If + * already open, does nothing. May block and retry. + */ + private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx { + if (primaryZygoteState == null || primaryZygoteState.isClosed()) { + try { + primaryZygoteState = ZygoteState.connect(mSocket); + } catch (IOException ioe) { + throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe); + } + } + + if (primaryZygoteState.matches(abi)) { + return primaryZygoteState; + } + + // The primary zygote didn't match. Try the secondary. + if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) { + try { + secondaryZygoteState = ZygoteState.connect(mSecondarySocket); + } catch (IOException ioe) { + throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe); + } + } + + if (secondaryZygoteState.matches(abi)) { + return secondaryZygoteState; + } + + throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi); + } +} diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 6fe653a56327d..1c307c9410f30 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -30,6 +30,7 @@ import android.os.Process; import android.os.SystemClock; import android.os.SystemProperties; import android.os.Trace; +import android.os.ZygoteProcess; import android.security.keystore.AndroidKeyStoreProvider; import android.system.ErrnoException; import android.system.Os; @@ -700,7 +701,8 @@ public class ZygoteInit { Process.SECONDARY_ZYGOTE_SOCKET : Process.ZYGOTE_SOCKET; while (true) { try { - final Process.ZygoteState zs = Process.ZygoteState.connect(otherZygoteName); + final ZygoteProcess.ZygoteState zs = + ZygoteProcess.ZygoteState.connect(otherZygoteName); zs.close(); break; } catch (IOException ioe) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index e46fba45c7893..eb5bf228b852b 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -6707,7 +6707,7 @@ public final class ActivityManagerService extends ActivityManagerNative ArraySet completedIsas = new ArraySet(); for (String abi : Build.SUPPORTED_ABIS) { - Process.establishZygoteConnectionForAbi(abi); + Process.zygoteProcess.establishZygoteConnectionForAbi(abi); final String instructionSet = VMRuntime.getInstructionSet(abi); if (!completedIsas.contains(instructionSet)) { try {