blob: bcb6c0f9ae87a99d0cb5592b81bf6988a739af61 [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.os;
import static android.system.OsConstants.O_CLOEXEC;
import static com.android.internal.os.ZygoteConnectionConstants.MAX_ZYGOTE_ARGC;
import android.content.pm.ApplicationInfo;
import android.net.Credentials;
import android.net.LocalServerSocket;
import android.net.LocalSocket;
import android.os.Build;
import android.os.FactoryTest;
import android.os.IVold;
import android.os.Process;
import android.os.SystemProperties;
import android.os.Trace;
import android.provider.DeviceConfig;
import android.system.ErrnoException;
import android.system.Os;
import android.util.Log;
import dalvik.annotation.optimization.FastNative;
import dalvik.system.ZygoteHooks;
import libcore.io.IoUtils;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStreamReader;
/** @hide */
public final class Zygote {
/*
* Bit values for "runtimeFlags" argument. The definitions are duplicated
* in the native code.
*/
/** enable debugging over JDWP */
public static final int DEBUG_ENABLE_JDWP = 1;
/** enable JNI checks */
public static final int DEBUG_ENABLE_CHECKJNI = 1 << 1;
/** enable Java programming language "assert" statements */
public static final int DEBUG_ENABLE_ASSERT = 1 << 2;
/** disable the AOT compiler and JIT */
public static final int DEBUG_ENABLE_SAFEMODE = 1 << 3;
/** Enable logging of third-party JNI activity. */
public static final int DEBUG_ENABLE_JNI_LOGGING = 1 << 4;
/** Force generation of native debugging information. */
public static final int DEBUG_GENERATE_DEBUG_INFO = 1 << 5;
/** Always use JIT-ed code. */
public static final int DEBUG_ALWAYS_JIT = 1 << 6;
/** Make the code native debuggable by turning off some optimizations. */
public static final int DEBUG_NATIVE_DEBUGGABLE = 1 << 7;
/** Make the code Java debuggable by turning off some optimizations. */
public static final int DEBUG_JAVA_DEBUGGABLE = 1 << 8;
/** Turn off the verifier. */
public static final int DISABLE_VERIFIER = 1 << 9;
/** Only use oat files located in /system. Otherwise use dex/jar/apk . */
public static final int ONLY_USE_SYSTEM_OAT_FILES = 1 << 10;
/** Force generation of native debugging information for backtraces. */
public static final int DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 11;
/**
* Hidden API access restrictions. This is a mask for bits representing the API enforcement
* policy, defined by {@code @ApplicationInfo.HiddenApiEnforcementPolicy}.
*/
public static final int API_ENFORCEMENT_POLICY_MASK = (1 << 12) | (1 << 13);
/**
* Bit shift for use with {@link #API_ENFORCEMENT_POLICY_MASK}.
*
* (flags & API_ENFORCEMENT_POLICY_MASK) >> API_ENFORCEMENT_POLICY_SHIFT gives
* @ApplicationInfo.ApiEnforcementPolicy values.
*/
public static final int API_ENFORCEMENT_POLICY_SHIFT =
Integer.numberOfTrailingZeros(API_ENFORCEMENT_POLICY_MASK);
/**
* Enable system server ART profiling.
*/
public static final int PROFILE_SYSTEM_SERVER = 1 << 14;
/**
* Enable profiling from shell.
*/
public static final int PROFILE_FROM_SHELL = 1 << 15;
/*
* Enable using the ART app image startup cache
*/
public static final int USE_APP_IMAGE_STARTUP_CACHE = 1 << 16;
/**
* When set, application specified signal handlers are not chained (i.e, ignored)
* by the runtime.
*
* Used for debugging only. Usage: set debug.ignoreappsignalhandler to 1.
*/
public static final int DEBUG_IGNORE_APP_SIGNAL_HANDLER = 1 << 17;
/**
* Disable runtime access to {@link android.annotation.TestApi} annotated members.
*
* <p>This only takes effect if Hidden API access restrictions are enabled as well.
*/
public static final int DISABLE_TEST_API_ENFORCEMENT_POLICY = 1 << 18;
/** No external storage should be mounted. */
public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE;
/** Default external storage should be mounted. */
public static final int MOUNT_EXTERNAL_DEFAULT = IVold.REMOUNT_MODE_DEFAULT;
/** Read-only external storage should be mounted. */
public static final int MOUNT_EXTERNAL_READ = IVold.REMOUNT_MODE_READ;
/** Read-write external storage should be mounted. */
public static final int MOUNT_EXTERNAL_WRITE = IVold.REMOUNT_MODE_WRITE;
/**
* Mount mode for apps that are already installed on the device before the isolated_storage
* feature is enabled.
*/
public static final int MOUNT_EXTERNAL_LEGACY = IVold.REMOUNT_MODE_LEGACY;
/**
* Mount mode for package installers which should give them access to
* all obb dirs in addition to their package sandboxes
*/
public static final int MOUNT_EXTERNAL_INSTALLER = IVold.REMOUNT_MODE_INSTALLER;
/** Read-write external storage should be mounted instead of package sandbox */
public static final int MOUNT_EXTERNAL_FULL = IVold.REMOUNT_MODE_FULL;
/** Number of bytes sent to the Zygote over USAP pipes or the pool event FD */
public static final int USAP_MANAGEMENT_MESSAGE_BYTES = 8;
/**
* An extraArg passed when a zygote process is forking a child-zygote, specifying a name
* in the abstract socket namespace. This socket name is what the new child zygote
* should listen for connections on.
*/
public static final String CHILD_ZYGOTE_SOCKET_NAME_ARG = "--zygote-socket=";
/**
* An extraArg passed when a zygote process is forking a child-zygote, specifying the
* requested ABI for the child Zygote.
*/
public static final String CHILD_ZYGOTE_ABI_LIST_ARG = "--abi-list=";
/**
* An extraArg passed when a zygote process is forking a child-zygote, specifying the
* start of the UID range the children of the Zygote may setuid()/setgid() to. This
* will be enforced with a seccomp filter.
*/
public static final String CHILD_ZYGOTE_UID_RANGE_START = "--uid-range-start=";
/**
* An extraArg passed when a zygote process is forking a child-zygote, specifying the
* end of the UID range the children of the Zygote may setuid()/setgid() to. This
* will be enforced with a seccomp filter.
*/
public static final String CHILD_ZYGOTE_UID_RANGE_END = "--uid-range-end=";
/** Prefix prepended to socket names created by init */
private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
/**
* The duration to wait before re-checking Zygote related system properties.
*
* One minute in milliseconds.
*/
public static final long PROPERTY_CHECK_INTERVAL = 60000;
/**
* @hide for internal use only
*/
public static final int SOCKET_BUFFER_SIZE = 256;
/**
* @hide for internal use only
*/
private static final int PRIORITY_MAX = -20;
/** a prototype instance for a future List.toArray() */
protected static final int[][] INT_ARRAY_2D = new int[0][0];
/**
* @hide for internal use only.
*/
public static final String PRIMARY_SOCKET_NAME = "zygote";
/**
* @hide for internal use only.
*/
public static final String SECONDARY_SOCKET_NAME = "zygote_secondary";
/**
* @hide for internal use only
*/
public static final String USAP_POOL_PRIMARY_SOCKET_NAME = "usap_pool_primary";
/**
* @hide for internal use only
*/
public static final String USAP_POOL_SECONDARY_SOCKET_NAME = "usap_pool_secondary";
private Zygote() {}
/**
* Forks a new VM instance. The current VM must have been started
* with the -Xzygote flag. <b>NOTE: new instance keeps all
* root capabilities. The new process is expected to call capset()</b>.
*
* @param uid the UNIX uid that the new process should setuid() to after
* fork()ing and and before spawning any threads.
* @param gid the UNIX gid that the new process should setgid() to after
* fork()ing and and before spawning any threads.
* @param gids null-ok; a list of UNIX gids that the new process should
* setgroups() to after fork and before spawning any threads.
* @param runtimeFlags bit flags that enable ART features.
* @param rlimits null-ok an array of rlimit tuples, with the second
* dimension having a length of 3 and representing
* (resource, rlim_cur, rlim_max). These are set via the posix
* setrlimit(2) call.
* @param seInfo null-ok a string specifying SELinux information for
* the new process.
* @param niceName null-ok a string specifying the process name.
* @param fdsToClose an array of ints, holding one or more POSIX
* file descriptor numbers that are to be closed by the child
* (and replaced by /dev/null) after forking. An integer value
* of -1 in any entry in the array means "ignore this one".
* @param fdsToIgnore null-ok an array of ints, either null or holding
* one or more POSIX file descriptor numbers that are to be ignored
* in the file descriptor table check.
* @param startChildZygote if true, the new child process will itself be a
* new zygote process.
* @param instructionSet null-ok the instruction set to use.
* @param appDataDir null-ok the data directory of the app.
*
* @return 0 if this is the child, pid of the child
* if this is the parent, or -1 on error.
*/
public static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
int targetSdkVersion) {
ZygoteHooks.preFork();
int pid = nativeForkAndSpecialize(
uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
fdsToIgnore, startChildZygote, instructionSet, appDataDir);
if (pid == 0) {
Zygote.disableExecuteOnly(targetSdkVersion);
// Note that this event ends at the end of handleChildProc,
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
}
// Set the Java Language thread priority to the default value for new apps.
Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
ZygoteHooks.postForkCommon();
return pid;
}
private static native int nativeForkAndSpecialize(int uid, int gid, int[] gids,
int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet,
String appDataDir);
/**
* Specialize an unspecialized app process. The current VM must have been started
* with the -Xzygote flag.
*
* @param uid The UNIX uid that the new process should setuid() to before spawning any threads
* @param gid The UNIX gid that the new process should setgid() to before spawning any threads
* @param gids null-ok; A list of UNIX gids that the new process should
* setgroups() to before spawning any threads
* @param runtimeFlags Bit flags that enable ART features
* @param rlimits null-ok An array of rlimit tuples, with the second
* dimension having a length of 3 and representing
* (resource, rlim_cur, rlim_max). These are set via the posix
* setrlimit(2) call.
* @param seInfo null-ok A string specifying SELinux information for
* the new process.
* @param niceName null-ok A string specifying the process name.
* @param startChildZygote If true, the new child process will itself be a
* new zygote process.
* @param instructionSet null-ok The instruction set to use.
* @param appDataDir null-ok The data directory of the app.
*/
public static void specializeAppProcess(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName,
boolean startChildZygote, String instructionSet, String appDataDir) {
nativeSpecializeAppProcess(uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo,
niceName, startChildZygote, instructionSet, appDataDir);
// Note that this event ends at the end of handleChildProc.
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
// Set the Java Language thread priority to the default value for new apps.
Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
/*
* This is called here (instead of after the fork but before the specialize) to maintain
* consistancy with the code paths for forkAndSpecialize.
*
* TODO (chriswailes): Look into moving this to immediately after the fork.
*/
ZygoteHooks.postForkCommon();
}
private static native void nativeSpecializeAppProcess(int uid, int gid, int[] gids,
int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
boolean startChildZygote, String instructionSet, String appDataDir);
/**
* Called to do any initialization before starting an application.
*/
static native void nativePreApplicationInit();
/**
* Special method to start the system server process. In addition to the
* common actions performed in forkAndSpecialize, the pid of the child
* process is recorded such that the death of the child process will cause
* zygote to exit.
*
* @param uid the UNIX uid that the new process should setuid() to after
* fork()ing and and before spawning any threads.
* @param gid the UNIX gid that the new process should setgid() to after
* fork()ing and and before spawning any threads.
* @param gids null-ok; a list of UNIX gids that the new process should
* setgroups() to after fork and before spawning any threads.
* @param runtimeFlags bit flags that enable ART features.
* @param rlimits null-ok an array of rlimit tuples, with the second
* dimension having a length of 3 and representing
* (resource, rlim_cur, rlim_max). These are set via the posix
* setrlimit(2) call.
* @param permittedCapabilities argument for setcap()
* @param effectiveCapabilities argument for setcap()
*
* @return 0 if this is the child, pid of the child
* if this is the parent, or -1 on error.
*/
public static int forkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
ZygoteHooks.preFork();
int pid = nativeForkSystemServer(
uid, gid, gids, runtimeFlags, rlimits,
permittedCapabilities, effectiveCapabilities);
// Set the Java Language thread priority to the default value for new apps.
Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
ZygoteHooks.postForkCommon();
return pid;
}
private static native int nativeForkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, long permittedCapabilities, long effectiveCapabilities);
/**
* Lets children of the zygote inherit open file descriptors to this path.
*/
protected static native void nativeAllowFileAcrossFork(String path);
/**
* Lets children of the zygote inherit open file descriptors that belong to the
* ApplicationInfo that is passed in.
*
* @param appInfo ApplicationInfo of the application
*/
protected static void allowAppFilesAcrossFork(ApplicationInfo appInfo) {
for (String path : appInfo.getAllApkPaths()) {
Zygote.nativeAllowFileAcrossFork(path);
}
}
/**
* Installs a seccomp filter that limits setresuid()/setresgid() to the passed-in range
* @param uidGidMin The smallest allowed uid/gid
* @param uidGidMax The largest allowed uid/gid
*/
native protected static void nativeInstallSeccompUidGidFilter(int uidGidMin, int uidGidMax);
/**
* Initialize the native state of the Zygote. This inclues
* - Fetching socket FDs from the environment
* - Initializing security properties
* - Unmounting storage as appropriate
* - Loading necessary performance profile information
*
* @param isPrimary True if this is the zygote process, false if it is zygote_secondary
*/
static void initNativeState(boolean isPrimary) {
nativeInitNativeState(isPrimary);
}
protected static native void nativeInitNativeState(boolean isPrimary);
/**
* Returns the raw string value of a system property.
*
* Note that Device Config is not available without an application so SystemProperties is used
* instead.
*
* TODO (chriswailes): Cache the system property location in native code and then write a JNI
* function to fetch it.
*/
public static String getConfigurationProperty(String propertyName, String defaultValue) {
return SystemProperties.get(
String.join(".",
"persist.device_config",
DeviceConfig.NAMESPACE_RUNTIME_NATIVE,
propertyName),
defaultValue);
}
protected static void emptyUsapPool() {
nativeEmptyUsapPool();
}
private static native void nativeEmptyUsapPool();
/**
* Returns the value of a system property converted to a boolean using specific logic.
*
* Note that Device Config is not available without an application so SystemProperties is used
* instead.
*
* @see SystemProperties.getBoolean
*
* TODO (chriswailes): Cache the system property location in native code and then write a JNI
* function to fetch it.
* TODO (chriswailes): Move into ZygoteConfig.java once the necessary CL lands (go/ag/6580627)
*/
public static boolean getConfigurationPropertyBoolean(
String propertyName, Boolean defaultValue) {
return SystemProperties.getBoolean(
String.join(".",
"persist.device_config",
DeviceConfig.NAMESPACE_RUNTIME_NATIVE,
propertyName),
defaultValue);
}
/**
* @return Number of unspecialized app processes currently in the pool
*/
static int getUsapPoolCount() {
return nativeGetUsapPoolCount();
}
private static native int nativeGetUsapPoolCount();
/**
* @return The event FD used for communication between the signal handler and the ZygoteServer
* poll loop
*/
static FileDescriptor getUsapPoolEventFD() {
FileDescriptor fd = new FileDescriptor();
fd.setInt$(nativeGetUsapPoolEventFD());
return fd;
}
private static native int nativeGetUsapPoolEventFD();
/**
* Fork a new unspecialized app process from the zygote
*
* @param usapPoolSocket The server socket the USAP will call accept on
* @param sessionSocketRawFDs Anonymous session sockets that are currently open
* @param isPriorityFork Value controlling the process priority level until accept is called
* @return In the Zygote process this function will always return null; in unspecialized app
* processes this function will return a Runnable object representing the new
* application that is passed up from usapMain.
*/
static Runnable forkUsap(LocalServerSocket usapPoolSocket,
int[] sessionSocketRawFDs,
boolean isPriorityFork) {
FileDescriptor[] pipeFDs = null;
try {
pipeFDs = Os.pipe2(O_CLOEXEC);
} catch (ErrnoException errnoEx) {
throw new IllegalStateException("Unable to create USAP pipe.", errnoEx);
}
int pid =
nativeForkUsap(pipeFDs[0].getInt$(), pipeFDs[1].getInt$(),
sessionSocketRawFDs, isPriorityFork);
if (pid == 0) {
IoUtils.closeQuietly(pipeFDs[0]);
return usapMain(usapPoolSocket, pipeFDs[1]);
} else {
// The read-end of the pipe will be closed by the native code.
// See removeUsapTableEntry();
IoUtils.closeQuietly(pipeFDs[1]);
return null;
}
}
private static native int nativeForkUsap(int readPipeFD,
int writePipeFD,
int[] sessionSocketRawFDs,
boolean isPriorityFork);
/**
* This function is used by unspecialized app processes to wait for specialization requests from
* the system server.
*
* @param writePipe The write end of the reporting pipe used to communicate with the poll loop
* of the ZygoteServer.
* @return A runnable oject representing the new application.
*/
private static Runnable usapMain(LocalServerSocket usapPoolSocket,
FileDescriptor writePipe) {
final int pid = Process.myPid();
Process.setArgV0(Process.is64Bit() ? "usap64" : "usap32");
LocalSocket sessionSocket = null;
DataOutputStream usapOutputStream = null;
Credentials peerCredentials = null;
ZygoteArguments args = null;
// Change the priority to max before calling accept so we can respond to new specialization
// requests as quickly as possible. This will be reverted to the default priority in the
// native specialization code.
boostUsapPriority();
while (true) {
try {
sessionSocket = usapPoolSocket.accept();
// Block SIGTERM so we won't be killed if the Zygote flushes the USAP pool.
blockSigTerm();
BufferedReader usapReader =
new BufferedReader(new InputStreamReader(sessionSocket.getInputStream()));
usapOutputStream =
new DataOutputStream(sessionSocket.getOutputStream());
peerCredentials = sessionSocket.getPeerCredentials();
String[] argStrings = readArgumentList(usapReader);
if (argStrings != null) {
args = new ZygoteArguments(argStrings);
// TODO (chriswailes): Should this only be run for debug builds?
validateUsapCommand(args);
break;
} else {
Log.e("USAP", "Truncated command received.");
IoUtils.closeQuietly(sessionSocket);
// Re-enable SIGTERM so the USAP can be flushed from the pool if necessary.
unblockSigTerm();
}
} catch (Exception ex) {
Log.e("USAP", ex.getMessage());
IoUtils.closeQuietly(sessionSocket);
// Re-enable SIGTERM so the USAP can be flushed from the pool if necessary.
unblockSigTerm();
}
}
try {
// SIGTERM is blocked on loop exit. This prevents a USAP that is specializing from
// being killed during a pool flush.
setAppProcessName(args, "USAP");
applyUidSecurityPolicy(args, peerCredentials);
applyDebuggerSystemProperty(args);
int[][] rlimits = null;
if (args.mRLimits != null) {
rlimits = args.mRLimits.toArray(INT_ARRAY_2D);
}
// This must happen before the SELinux policy for this process is
// changed when specializing.
try {
// Used by ZygoteProcess.zygoteSendArgsAndGetResult to fill in a
// Process.ProcessStartResult object.
usapOutputStream.writeInt(pid);
} catch (IOException ioEx) {
Log.e("USAP", "Failed to write response to session socket: "
+ ioEx.getMessage());
throw new RuntimeException(ioEx);
} finally {
IoUtils.closeQuietly(sessionSocket);
try {
// This socket is closed using Os.close due to an issue with the implementation
// of LocalSocketImp.close(). Because the raw FD is created by init and then
// loaded from an environment variable (as opposed to being created by the
// LocalSocketImpl itself) the current implementation will not actually close
// the underlying FD.
//
// See b/130309968 for discussion of this issue.
Os.close(usapPoolSocket.getFileDescriptor());
} catch (ErrnoException ex) {
Log.e("USAP", "Failed to close USAP pool socket");
throw new RuntimeException(ex);
}
}
try {
ByteArrayOutputStream buffer =
new ByteArrayOutputStream(Zygote.USAP_MANAGEMENT_MESSAGE_BYTES);
DataOutputStream outputStream = new DataOutputStream(buffer);
// This is written as a long so that the USAP reporting pipe and USAP pool event FD
// handlers in ZygoteServer.runSelectLoop can be unified. These two cases should
// both send/receive 8 bytes.
outputStream.writeLong(pid);
outputStream.flush();
Os.write(writePipe, buffer.toByteArray(), 0, buffer.size());
} catch (Exception ex) {
Log.e("USAP",
String.format("Failed to write PID (%d) to pipe (%d): %s",
pid, writePipe.getInt$(), ex.getMessage()));
throw new RuntimeException(ex);
} finally {
IoUtils.closeQuietly(writePipe);
}
specializeAppProcess(args.mUid, args.mGid, args.mGids,
args.mRuntimeFlags, rlimits, args.mMountExternal,
args.mSeInfo, args.mNiceName, args.mStartChildZygote,
args.mInstructionSet, args.mAppDataDir);
disableExecuteOnly(args.mTargetSdkVersion);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
return ZygoteInit.zygoteInit(args.mTargetSdkVersion,
args.mDisabledCompatChanges,
args.mRemainingArgs,
null /* classLoader */);
} finally {
// Unblock SIGTERM to restore the process to default behavior.
unblockSigTerm();
}
}
private static void blockSigTerm() {
nativeBlockSigTerm();
}
private static native void nativeBlockSigTerm();
private static void unblockSigTerm() {
nativeUnblockSigTerm();
}
private static native void nativeUnblockSigTerm();
private static void boostUsapPriority() {
nativeBoostUsapPriority();
}
private static native void nativeBoostUsapPriority();
static void setAppProcessName(ZygoteArguments args, String loggingTag) {
if (args.mNiceName != null) {
Process.setArgV0(args.mNiceName);
} else if (args.mPackageName != null) {
Process.setArgV0(args.mPackageName);
} else {
Log.w(loggingTag, "Unable to set package name.");
}
}
private static final String USAP_ERROR_PREFIX = "Invalid command to USAP: ";
/**
* Checks a set of zygote arguments to see if they can be handled by a USAP. Throws an
* exception if an invalid arugment is encountered.
* @param args The arguments to test
*/
private static void validateUsapCommand(ZygoteArguments args) {
if (args.mAbiListQuery) {
throw new IllegalArgumentException(USAP_ERROR_PREFIX + "--query-abi-list");
} else if (args.mPidQuery) {
throw new IllegalArgumentException(USAP_ERROR_PREFIX + "--get-pid");
} else if (args.mPreloadDefault) {
throw new IllegalArgumentException(USAP_ERROR_PREFIX + "--preload-default");
} else if (args.mPreloadPackage != null) {
throw new IllegalArgumentException(USAP_ERROR_PREFIX + "--preload-package");
} else if (args.mPreloadApp != null) {
throw new IllegalArgumentException(USAP_ERROR_PREFIX + "--preload-app");
} else if (args.mStartChildZygote) {
throw new IllegalArgumentException(USAP_ERROR_PREFIX + "--start-child-zygote");
} else if (args.mApiBlacklistExemptions != null) {
throw new IllegalArgumentException(
USAP_ERROR_PREFIX + "--set-api-blacklist-exemptions");
} else if (args.mHiddenApiAccessLogSampleRate != -1) {
throw new IllegalArgumentException(
USAP_ERROR_PREFIX + "--hidden-api-log-sampling-rate=");
} else if (args.mHiddenApiAccessStatslogSampleRate != -1) {
throw new IllegalArgumentException(
USAP_ERROR_PREFIX + "--hidden-api-statslog-sampling-rate=");
} else if (args.mInvokeWith != null) {
throw new IllegalArgumentException(USAP_ERROR_PREFIX + "--invoke-with");
} else if (args.mPermittedCapabilities != 0 || args.mEffectiveCapabilities != 0) {
throw new ZygoteSecurityException("Client may not specify capabilities: "
+ "permitted=0x" + Long.toHexString(args.mPermittedCapabilities)
+ ", effective=0x" + Long.toHexString(args.mEffectiveCapabilities));
}
}
/**
* Mark execute-only segments of libraries read+execute for apps with targetSdkVersion<Q.
*/
protected static void disableExecuteOnly(int targetSdkVersion) {
if ((targetSdkVersion < Build.VERSION_CODES.Q) && !nativeDisableExecuteOnly()) {
Log.e("Zygote", "Failed to set libraries to read+execute.");
}
}
private static native boolean nativeDisableExecuteOnly();
/**
* @return Raw file descriptors for the read-end of USAP reporting pipes.
*/
protected static int[] getUsapPipeFDs() {
return nativeGetUsapPipeFDs();
}
private static native int[] nativeGetUsapPipeFDs();
/**
* Remove the USAP table entry for the provided process ID.
*
* @param usapPID Process ID of the entry to remove
* @return True if the entry was removed; false if it doesn't exist
*/
protected static boolean removeUsapTableEntry(int usapPID) {
return nativeRemoveUsapTableEntry(usapPID);
}
private static native boolean nativeRemoveUsapTableEntry(int usapPID);
/**
* uid 1000 (Process.SYSTEM_UID) may specify any uid &gt; 1000 in normal
* operation. It may also specify any gid and setgroups() list it chooses.
* In factory test mode, it may specify any UID.
*
* @param args non-null; zygote spawner arguments
* @param peer non-null; peer credentials
* @throws ZygoteSecurityException
*/
protected static void applyUidSecurityPolicy(ZygoteArguments args, Credentials peer)
throws ZygoteSecurityException {
if (peer.getUid() == Process.SYSTEM_UID) {
/* In normal operation, SYSTEM_UID can only specify a restricted
* set of UIDs. In factory test mode, SYSTEM_UID may specify any uid.
*/
boolean uidRestricted = FactoryTest.getMode() == FactoryTest.FACTORY_TEST_OFF;
if (uidRestricted && args.mUidSpecified && (args.mUid < Process.SYSTEM_UID)) {
throw new ZygoteSecurityException(
"System UID may not launch process with UID < "
+ Process.SYSTEM_UID);
}
}
// If not otherwise specified, uid and gid are inherited from peer
if (!args.mUidSpecified) {
args.mUid = peer.getUid();
args.mUidSpecified = true;
}
if (!args.mGidSpecified) {
args.mGid = peer.getGid();
args.mGidSpecified = true;
}
}
/**
* Applies debugger system properties to the zygote arguments.
*
* If "ro.debuggable" is "1", all apps are debuggable. Otherwise,
* the debugger state is specified via the "--enable-jdwp" flag
* in the spawn request.
*
* @param args non-null; zygote spawner args
*/
protected static void applyDebuggerSystemProperty(ZygoteArguments args) {
if (RoSystemProperties.DEBUGGABLE) {
args.mRuntimeFlags |= Zygote.DEBUG_ENABLE_JDWP;
}
}
/**
* Applies zygote security policy.
* Based on the credentials of the process issuing a zygote command:
* <ol>
* <li> uid 0 (root) may specify --invoke-with to launch Zygote with a
* wrapper command.
* <li> Any other uid may not specify any invoke-with argument.
* </ul>
*
* @param args non-null; zygote spawner arguments
* @param peer non-null; peer credentials
* @throws ZygoteSecurityException
*/
protected static void applyInvokeWithSecurityPolicy(ZygoteArguments args, Credentials peer)
throws ZygoteSecurityException {
int peerUid = peer.getUid();
if (args.mInvokeWith != null && peerUid != 0
&& (args.mRuntimeFlags & Zygote.DEBUG_ENABLE_JDWP) == 0) {
throw new ZygoteSecurityException("Peer is permitted to specify an "
+ "explicit invoke-with wrapper command only for debuggable "
+ "applications.");
}
}
/**
* Applies invoke-with system properties to the zygote arguments.
*
* @param args non-null; zygote args
*/
protected static void applyInvokeWithSystemProperty(ZygoteArguments args) {
if (args.mInvokeWith == null && args.mNiceName != null) {
String property = "wrap." + args.mNiceName;
args.mInvokeWith = SystemProperties.get(property);
if (args.mInvokeWith != null && args.mInvokeWith.length() == 0) {
args.mInvokeWith = null;
}
}
}
/**
* Reads an argument list from the provided socket
* @return Argument list or null if EOF is reached
* @throws IOException passed straight through
*/
static String[] readArgumentList(BufferedReader socketReader) throws IOException {
int argc;
try {
String argc_string = socketReader.readLine();
if (argc_string == null) {
// EOF reached.
return null;
}
argc = Integer.parseInt(argc_string);
} catch (NumberFormatException ex) {
Log.e("Zygote", "Invalid Zygote wire format: non-int at argc");
throw new IOException("Invalid wire format");
}
// See bug 1092107: large argc can be used for a DOS attack
if (argc > MAX_ZYGOTE_ARGC) {
throw new IOException("Max arg count exceeded");
}
String[] args = new String[argc];
for (int arg_index = 0; arg_index < argc; arg_index++) {
args[arg_index] = socketReader.readLine();
if (args[arg_index] == null) {
// We got an unexpected EOF.
throw new IOException("Truncated request");
}
}
return args;
}
/**
* Creates a managed LocalServerSocket object using a file descriptor
* created by an init.rc script. The init scripts that specify the
* sockets name can be found in system/core/rootdir. The socket is bound
* to the file system in the /dev/sockets/ directory, and the file
* descriptor is shared via the ANDROID_SOCKET_<socketName> environment
* variable.
*/
static LocalServerSocket createManagedSocketFromInitSocket(String socketName) {
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("Socket unset or invalid: " + fullSocketName, ex);
}
try {
FileDescriptor fd = new FileDescriptor();
fd.setInt$(fileDesc);
return new LocalServerSocket(fd);
} catch (IOException ex) {
throw new RuntimeException(
"Error building socket from file descriptor: " + fileDesc, ex);
}
}
private static void callPostForkSystemServerHooks(int runtimeFlags) {
// SystemServer specific post fork hooks run before child post fork hooks.
ZygoteHooks.postForkSystemServer(runtimeFlags);
}
private static void callPostForkChildHooks(int runtimeFlags, boolean isSystemServer,
boolean isZygote, String instructionSet) {
ZygoteHooks.postForkChild(runtimeFlags, isSystemServer, isZygote, instructionSet);
}
/**
* Executes "/system/bin/sh -c &lt;command&gt;" using the exec() system call.
* This method throws a runtime exception if exec() failed, otherwise, this
* method never returns.
*
* @param command The shell command to execute.
*/
public static void execShell(String command) {
String[] args = { "/system/bin/sh", "-c", command };
try {
Os.execv(args[0], args);
} catch (ErrnoException e) {
throw new RuntimeException(e);
}
}
/**
* Appends quotes shell arguments to the specified string builder.
* The arguments are quoted using single-quotes, escaped if necessary,
* prefixed with a space, and appended to the command.
*
* @param command A string builder for the shell command being constructed.
* @param args An array of argument strings to be quoted and appended to the command.
* @see #execShell(String)
*/
public static void appendQuotedShellArgs(StringBuilder command, String[] args) {
for (String arg : args) {
command.append(" '").append(arg.replace("'", "'\\''")).append("'");
}
}
/**
* Parse the given unsolicited zygote message as type SIGCHLD,
* extract the payload information into the given output buffer.
*
* @param in The unsolicited zygote message to be parsed
* @param length The number of bytes in the message
* @param out The output buffer where the payload information will be placed
* @return Number of elements being place into output buffer, or -1 if
* either the message is malformed or not the type as expected here.
*
* @hide
*/
@FastNative
public static native int nativeParseSigChld(byte[] in, int length, int[] out);
}