/*
* Copyright (C) 2007 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.S_IRWXG;
import static android.system.OsConstants.S_IRWXO;
import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__SECONDARY_ZYGOTE_INIT_START;
import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__ZYGOTE_INIT_START;
import android.app.ApplicationLoaders;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.SharedLibraryInfo;
import android.content.res.Resources;
import android.os.Build;
import android.os.Environment;
import android.os.IInstalld;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.os.ZygoteProcess;
import android.provider.DeviceConfig;
import android.security.keystore2.AndroidKeyStoreProvider;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
import android.system.StructCapUserData;
import android.system.StructCapUserHeader;
import android.text.Hyphenator;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
import android.util.TimingsTraceLog;
import android.webkit.WebViewFactory;
import android.widget.TextView;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.Preconditions;
import dalvik.system.VMRuntime;
import dalvik.system.ZygoteHooks;
import libcore.io.IoUtils;
import java.io.BufferedReader;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.Provider;
import java.security.Security;
/**
* Startup class for the zygote process.
*
* Pre-initializes some classes, and then waits for commands on a UNIX domain socket. Based on these
* commands, forks off child processes that inherit the initial state of the VM.
*
* Please see {@link ZygoteArguments} for documentation on the client protocol.
*
* @hide
*/
public class ZygoteInit {
private static final String TAG = "Zygote";
private static final boolean LOGGING_DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final String PROPERTY_DISABLE_GRAPHICS_DRIVER_PRELOADING =
"ro.zygote.disable_gl_preload";
private static final int LOG_BOOT_PROGRESS_PRELOAD_START = 3020;
private static final int LOG_BOOT_PROGRESS_PRELOAD_END = 3030;
private static final String ABI_LIST_ARG = "--abi-list=";
// TODO (chriswailes): Re-name this --zygote-socket-name= and then add a
// --usap-socket-name parameter.
private static final String SOCKET_NAME_ARG = "--socket-name=";
/**
* The path of a file that contains classes to preload.
*/
private static final String PRELOADED_CLASSES = "/system/etc/preloaded-classes";
private static final int UNPRIVILEGED_UID = 9999;
private static final int UNPRIVILEGED_GID = 9999;
private static final int ROOT_UID = 0;
private static final int ROOT_GID = 0;
private static boolean sPreloadComplete;
/**
* Cached classloader to use for the system server. Will only be populated in the system
* server process.
*/
private static ClassLoader sCachedSystemServerClassLoader = null;
static void preload(TimingsTraceLog bootTimingsTraceLog) {
Log.d(TAG, "begin preload");
bootTimingsTraceLog.traceBegin("BeginPreload");
beginPreload();
bootTimingsTraceLog.traceEnd(); // BeginPreload
bootTimingsTraceLog.traceBegin("PreloadClasses");
preloadClasses();
bootTimingsTraceLog.traceEnd(); // PreloadClasses
bootTimingsTraceLog.traceBegin("CacheNonBootClasspathClassLoaders");
cacheNonBootClasspathClassLoaders();
bootTimingsTraceLog.traceEnd(); // CacheNonBootClasspathClassLoaders
bootTimingsTraceLog.traceBegin("PreloadResources");
Resources.preloadResources();
bootTimingsTraceLog.traceEnd(); // PreloadResources
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadAppProcessHALs");
nativePreloadAppProcessHALs();
Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadGraphicsDriver");
maybePreloadGraphicsDriver();
Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
preloadSharedLibraries();
preloadTextResources();
// Ask the WebViewFactory to do any initialization that must run in the zygote process,
// for memory sharing purposes.
WebViewFactory.prepareWebViewInZygote();
endPreload();
warmUpJcaProviders();
Log.d(TAG, "end preload");
sPreloadComplete = true;
}
static void lazyPreload() {
Preconditions.checkState(!sPreloadComplete);
Log.i(TAG, "Lazily preloading resources.");
preload(new TimingsTraceLog("ZygoteInitTiming_lazy", Trace.TRACE_TAG_DALVIK));
}
private static void beginPreload() {
Log.i(TAG, "Calling ZygoteHooks.beginPreload()");
ZygoteHooks.onBeginPreload();
}
private static void endPreload() {
ZygoteHooks.onEndPreload();
Log.i(TAG, "Called ZygoteHooks.endPreload()");
}
private static void preloadSharedLibraries() {
Log.i(TAG, "Preloading shared libraries...");
System.loadLibrary("android");
System.loadLibrary("jnigraphics");
// TODO(b/206676167): This library is only used for renderscript today. When renderscript is
// removed, this load can be removed as well.
if (!SystemProperties.getBoolean("config.disable_renderscript", false)) {
System.loadLibrary("compiler_rt");
}
}
native private static void nativePreloadAppProcessHALs();
/**
* This call loads the graphics driver by making an OpenGL or Vulkan call. If the driver is
* not currently in memory it will load and initialize it. The OpenGL call itself is relatively
* cheap and pure. This means that it is a low overhead on the initial call, and is safe and
* cheap to call later. Calls after the initial invocation will effectively be no-ops for the
* system.
*/
static native void nativePreloadGraphicsDriver();
private static void maybePreloadGraphicsDriver() {
if (!SystemProperties.getBoolean(PROPERTY_DISABLE_GRAPHICS_DRIVER_PRELOADING, false)) {
nativePreloadGraphicsDriver();
}
}
private static void preloadTextResources() {
Hyphenator.init();
TextView.preloadFontCache();
}
/**
* Register AndroidKeyStoreProvider and warm up the providers that are already registered.
*
* By doing it here we avoid that each app does it when requesting a service from the provider
* for the first time.
*/
private static void warmUpJcaProviders() {
long startTime = SystemClock.uptimeMillis();
Trace.traceBegin(
Trace.TRACE_TAG_DALVIK, "Starting installation of AndroidKeyStoreProvider");
AndroidKeyStoreProvider.install();
Log.i(TAG, "Installed AndroidKeyStoreProvider in "
+ (SystemClock.uptimeMillis() - startTime) + "ms.");
Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
startTime = SystemClock.uptimeMillis();
Trace.traceBegin(
Trace.TRACE_TAG_DALVIK, "Starting warm up of JCA providers");
for (Provider p : Security.getProviders()) {
p.warmUpServiceProvision();
}
Log.i(TAG, "Warmed up JCA providers in "
+ (SystemClock.uptimeMillis() - startTime) + "ms.");
Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
}
private static boolean isExperimentEnabled(String experiment) {
boolean defaultValue = SystemProperties.getBoolean(
"dalvik.vm." + experiment,
/*def=*/false);
// Can't use device_config since we are the zygote, and it's not initialized at this point.
return SystemProperties.getBoolean(
"persist.device_config." + DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT
+ "." + experiment,
defaultValue);
}
/* package-private */ static boolean shouldProfileSystemServer() {
return isExperimentEnabled("profilesystemserver");
}
/**
* Performs Zygote process initialization. Loads and initializes commonly used classes.
*
* Most classes only cause a few hundred bytes to be allocated, but a few will allocate a dozen
* Kbytes (in one case, 500+K).
*/
private static void preloadClasses() {
final VMRuntime runtime = VMRuntime.getRuntime();
InputStream is;
try {
is = new FileInputStream(PRELOADED_CLASSES);
} catch (FileNotFoundException e) {
Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
return;
}
Log.i(TAG, "Preloading classes...");
long startTime = SystemClock.uptimeMillis();
// Drop root perms while running static initializers.
final int reuid = Os.getuid();
final int regid = Os.getgid();
// We need to drop root perms only if we're already root. In the case of "wrapped"
// processes (see WrapperInit), this function is called from an unprivileged uid
// and gid.
boolean droppedPriviliges = false;
if (reuid == ROOT_UID && regid == ROOT_GID) {
try {
Os.setregid(ROOT_GID, UNPRIVILEGED_GID);
Os.setreuid(ROOT_UID, UNPRIVILEGED_UID);
} catch (ErrnoException ex) {
throw new RuntimeException("Failed to drop root", ex);
}
droppedPriviliges = true;
}
try {
BufferedReader br =
new BufferedReader(new InputStreamReader(is), Zygote.SOCKET_BUFFER_SIZE);
int count = 0;
int missingLambdaCount = 0;
String line;
while ((line = br.readLine()) != null) {
// Skip comments and blank lines.
line = line.trim();
if (line.startsWith("#") || line.equals("")) {
continue;
}
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, line);
try {
// Load and explicitly initialize the given class. Use
// Class.forName(String, boolean, ClassLoader) to avoid repeated stack lookups
// (to derive the caller's class-loader). Use true to force initialization, and
// null for the boot classpath class-loader (could as well cache the
// class-loader of this class in a variable).
Class.forName(line, true, null);
count++;
} catch (ClassNotFoundException e) {
if (line.contains("$$Lambda$")) {
if (LOGGING_DEBUG) {
missingLambdaCount++;
}
} else {
Log.w(TAG, "Class not found for preloading: " + line);
}
} catch (UnsatisfiedLinkError e) {
Log.w(TAG, "Problem preloading " + line + ": " + e);
} catch (Throwable t) {
Log.e(TAG, "Error preloading " + line + ".", t);
if (t instanceof Error) {
throw (Error) t;
} else if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new RuntimeException(t);
}
}
Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
}
Log.i(TAG, "...preloaded " + count + " classes in "
+ (SystemClock.uptimeMillis() - startTime) + "ms.");
if (LOGGING_DEBUG && missingLambdaCount != 0) {
Log.i(TAG, "Unresolved lambda preloads: " + missingLambdaCount);
}
} catch (IOException e) {
Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
} finally {
IoUtils.closeQuietly(is);
// Fill in dex caches with classes, fields, and methods brought in by preloading.
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadDexCaches");
runtime.preloadDexCaches();
Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
// If we are profiling the boot image, reset the Jit counters after preloading the
// classes. We want to preload for performance, and we can use method counters to
// infer what clases are used after calling resetJitCounters, for profile purposes.
if (isExperimentEnabled("profilebootclasspath")) {
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "ResetJitCounters");
VMRuntime.resetJitCounters();
Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
}
// Bring back root. We'll need it later if we're in the zygote.
if (droppedPriviliges) {
try {
Os.setreuid(ROOT_UID, ROOT_UID);
Os.setregid(ROOT_GID, ROOT_GID);
} catch (ErrnoException ex) {
throw new RuntimeException("Failed to restore root", ex);
}
}
}
}
/**
* Load in things which are used by many apps but which cannot be put in the boot
* classpath.
*/
private static void cacheNonBootClasspathClassLoaders() {
// These libraries used to be part of the bootclasspath, but had to be removed.
// Old system applications still get them for backwards compatibility reasons,
// so they are cached here in order to preserve performance characteristics.
SharedLibraryInfo hidlBase = new SharedLibraryInfo(
"/system/framework/android.hidl.base-V1.0-java.jar", null /*packageName*/,
null /*codePaths*/, null /*name*/, 0 /*version*/, SharedLibraryInfo.TYPE_BUILTIN,
null /*declaringPackage*/, null /*dependentPackages*/, null /*dependencies*/,
false /*isNative*/);
SharedLibraryInfo hidlManager = new SharedLibraryInfo(
"/system/framework/android.hidl.manager-V1.0-java.jar", null /*packageName*/,
null /*codePaths*/, null /*name*/, 0 /*version*/, SharedLibraryInfo.TYPE_BUILTIN,
null /*declaringPackage*/, null /*dependentPackages*/, null /*dependencies*/,
false /*isNative*/);
SharedLibraryInfo androidTestBase = new SharedLibraryInfo(
"/system/framework/android.test.base.jar", null /*packageName*/,
null /*codePaths*/, null /*name*/, 0 /*version*/, SharedLibraryInfo.TYPE_BUILTIN,
null /*declaringPackage*/, null /*dependentPackages*/, null /*dependencies*/,
false /*isNative*/);
ApplicationLoaders.getDefault().createAndCacheNonBootclasspathSystemClassLoaders(
new SharedLibraryInfo[]{
// ordered dependencies first
hidlBase,
hidlManager,
androidTestBase,
});
}
/**
* Runs several special GCs to try to clean up a few generations of softly- and final-reachable
* objects, along with any other garbage. This is only useful just before a fork().
*/
private static void gcAndFinalize() {
ZygoteHooks.gcAndFinalize();
}
/**
* Finish remaining work for the newly forked system server process.
*/
private static Runnable handleSystemServerProcess(ZygoteArguments parsedArgs) {
// set umask to 0077 so new files and directories will default to owner-only permissions.
Os.umask(S_IRWXG | S_IRWXO);
if (parsedArgs.mNiceName != null) {
Process.setArgV0(parsedArgs.mNiceName);
}
final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
if (systemServerClasspath != null) {
// Capturing profiles is only supported for debug or eng builds since selinux normally
// prevents it.
if (shouldProfileSystemServer() && (Build.IS_USERDEBUG || Build.IS_ENG)) {
try {
Log.d(TAG, "Preparing system server profile");
final String standaloneSystemServerJars =
Os.getenv("STANDALONE_SYSTEMSERVER_JARS");
final String systemServerPaths = standaloneSystemServerJars != null
? String.join(":", systemServerClasspath, standaloneSystemServerJars)
: systemServerClasspath;
prepareSystemServerProfile(systemServerPaths);
} catch (Exception e) {
Log.wtf(TAG, "Failed to set up system server profile", e);
}
}
}
if (parsedArgs.mInvokeWith != null) {
String[] args = parsedArgs.mRemainingArgs;
// If we have a non-null system server class path, we'll have to duplicate the
// existing arguments and append the classpath to it. ART will handle the classpath
// correctly when we exec a new process.
if (systemServerClasspath != null) {
String[] amendedArgs = new String[args.length + 2];
amendedArgs[0] = "-cp";
amendedArgs[1] = systemServerClasspath;
System.arraycopy(args, 0, amendedArgs, 2, args.length);
args = amendedArgs;
}
WrapperInit.execApplication(parsedArgs.mInvokeWith,
parsedArgs.mNiceName, parsedArgs.mTargetSdkVersion,
VMRuntime.getCurrentInstructionSet(), null, args);
throw new IllegalStateException("Unexpected return from WrapperInit.execApplication");
} else {
ClassLoader cl = getOrCreateSystemServerClassLoader();
if (cl != null) {
Thread.currentThread().setContextClassLoader(cl);
}
/*
* Pass the remaining arguments to SystemServer.
*/
return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
parsedArgs.mDisabledCompatChanges,
parsedArgs.mRemainingArgs, cl);
}
/* should never reach here */
}
/**
* Create the classloader for the system server and store it in
* {@link sCachedSystemServerClassLoader}. This function is called through JNI in the forked
* system server process in the zygote SELinux domain.
*/
private static ClassLoader getOrCreateSystemServerClassLoader() {
if (sCachedSystemServerClassLoader == null) {
final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
if (systemServerClasspath != null) {
sCachedSystemServerClassLoader = createPathClassLoader(systemServerClasspath,
VMRuntime.SDK_VERSION_CUR_DEVELOPMENT);
}
}
return sCachedSystemServerClassLoader;
}
/**
* Creates class loaders for standalone system server jars. This function is called through JNI
* in the forked system server process in the zygote SELinux domain.
*/
private static void prefetchStandaloneSystemServerJars() {
if (shouldProfileSystemServer()) {
// We don't prefetch AOT artifacts if we are profiling system server, as we are going to
// JIT it.
// This method only gets called from native and should already be skipped if we profile
// system server. Still, be robust and check it again.
return;
}
String envStr = Os.getenv("STANDALONE_SYSTEMSERVER_JARS");
if (TextUtils.isEmpty(envStr)) {
return;
}
for (String jar : envStr.split(":")) {
try {
SystemServerClassLoaderFactory.createClassLoader(
jar, getOrCreateSystemServerClassLoader());
} catch (Error e) {
// We don't want the process to crash for this error because prefetching is just an
// optimization.
Log.e(TAG,
String.format("Failed to prefetch standalone system server jar \"%s\": %s",
jar, e.toString()));
}
}
}
/**
* Note that preparing the profiles for system server does not require special selinux
* permissions. From the installer perspective the system server is a regular package which can
* capture profile information.
*/
private static void prepareSystemServerProfile(String systemServerPaths)
throws RemoteException {
if (systemServerPaths.isEmpty()) {
return;
}
String[] codePaths = systemServerPaths.split(":");
final IInstalld installd = IInstalld.Stub
.asInterface(ServiceManager.getService("installd"));
String systemServerPackageName = "android";
String systemServerProfileName = "primary.prof";
installd.prepareAppProfile(
systemServerPackageName,
UserHandle.USER_SYSTEM,
UserHandle.getAppId(Process.SYSTEM_UID),
systemServerProfileName,
codePaths[0],
/*dexMetadata*/ null);
File curProfileDir = Environment.getDataProfilesDePackageDirectory(
UserHandle.USER_SYSTEM, systemServerPackageName);
String curProfilePath = new File(curProfileDir, systemServerProfileName).getAbsolutePath();
File refProfileDir = Environment.getDataProfilesDePackageDirectory(
UserHandle.USER_SYSTEM, systemServerPackageName);
String refProfilePath = new File(refProfileDir, systemServerProfileName).getAbsolutePath();
VMRuntime.registerAppInfo(
systemServerPackageName,
curProfilePath,
refProfilePath,
codePaths,
VMRuntime.CODE_PATH_TYPE_PRIMARY_APK);
}
/**
* Sets the list of classes/methods for the hidden API
*/
public static void setApiDenylistExemptions(String[] exemptions) {
VMRuntime.getRuntime().setHiddenApiExemptions(exemptions);
}
public static void setHiddenApiAccessLogSampleRate(int percent) {
VMRuntime.getRuntime().setHiddenApiAccessLogSamplingRate(percent);
}
/**
* Sets the implementation to be used for logging hidden API accesses
* @param logger the implementation of the VMRuntime.HiddenApiUsageLogger interface
*/
public static void setHiddenApiUsageLogger(VMRuntime.HiddenApiUsageLogger logger) {
VMRuntime.getRuntime().setHiddenApiUsageLogger(logger);
}
/**
* Creates a PathClassLoader for the given class path that is associated with a shared
* namespace, i.e., this classloader can access platform-private native libraries.
*
* The classloader will add java.library.path to the native library path for the classloader
* namespace. Since it includes platform locations like /system/lib, this is only appropriate
* for platform code that don't need linker namespace isolation (as opposed to APEXes and apps).
*/
static ClassLoader createPathClassLoader(String classPath, int targetSdkVersion) {
String libraryPath = System.getProperty("java.library.path");
// We use the boot class loader, that's what the runtime expects at AOT.
ClassLoader parent = ClassLoader.getSystemClassLoader().getParent();
return ClassLoaderFactory.createClassLoader(classPath, libraryPath, libraryPath,
parent, targetSdkVersion, true /* isNamespaceShared */, null /* classLoaderName */);
}
/**
* Prepare the arguments and forks for the system server process.
*
* @return A {@code Runnable} that provides an entrypoint into system_server code in the child
* process; {@code null} in the parent.
*/
private static Runnable forkSystemServer(String abiList, String socketName,
ZygoteServer zygoteServer) {
long capabilities = posixCapabilitiesAsBits(
OsConstants.CAP_IPC_LOCK,
OsConstants.CAP_KILL,
OsConstants.CAP_NET_ADMIN,
OsConstants.CAP_NET_BIND_SERVICE,
OsConstants.CAP_NET_BROADCAST,
OsConstants.CAP_NET_RAW,
OsConstants.CAP_SYS_MODULE,
OsConstants.CAP_SYS_NICE,
OsConstants.CAP_SYS_PTRACE,
OsConstants.CAP_SYS_TIME,
OsConstants.CAP_SYS_TTY_CONFIG,
OsConstants.CAP_WAKE_ALARM,
OsConstants.CAP_BLOCK_SUSPEND
);
/* Containers run without some capabilities, so drop any caps that are not available. */
StructCapUserHeader header = new StructCapUserHeader(
OsConstants._LINUX_CAPABILITY_VERSION_3, 0);
StructCapUserData[] data;
try {
data = Os.capget(header);
} catch (ErrnoException ex) {
throw new RuntimeException("Failed to capget()", ex);
}
capabilities &= Integer.toUnsignedLong(data[0].effective) |
(Integer.toUnsignedLong(data[1].effective) << 32);
/* Hardcoded command line to start the system server */
String[] args = {
"--setuid=1000",
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,"
+ "1024,1032,1065,3001,3002,3003,3005,3006,3007,3009,3010,3011,3012",
"--capabilities=" + capabilities + "," + capabilities,
"--nice-name=system_server",
"--runtime-args",
"--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
"com.android.server.SystemServer",
};
ZygoteArguments parsedArgs;
int pid;
try {
ZygoteCommandBuffer commandBuffer = new ZygoteCommandBuffer(args);
try {
parsedArgs = ZygoteArguments.getInstance(commandBuffer);
} catch (EOFException e) {
throw new AssertionError("Unexpected argument error for forking system server", e);
}
commandBuffer.close();
Zygote.applyDebuggerSystemProperty(parsedArgs);
Zygote.applyInvokeWithSystemProperty(parsedArgs);
if (Zygote.nativeSupportsMemoryTagging()) {
String mode = SystemProperties.get("arm64.memtag.process.system_server", "");
if (mode.isEmpty()) {
/* The system server has ASYNC MTE by default, in order to allow
* system services to specify their own MTE level later, as you
* can't re-enable MTE once it's disabled. */
mode = SystemProperties.get("persist.arm64.memtag.default", "async");
}
if (mode.equals("async")) {
parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_ASYNC;
} else if (mode.equals("sync")) {
parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_SYNC;
} else if (!mode.equals("off")) {
/* When we have an invalid memory tag level, keep the current level. */
parsedArgs.mRuntimeFlags |= Zygote.nativeCurrentTaggingLevel();
Slog.e(TAG, "Unknown memory tag level for the system server: \"" + mode + "\"");
}
} else if (Zygote.nativeSupportsTaggedPointers()) {
/* Enable pointer tagging in the system server. Hardware support for this is present
* in all ARMv8 CPUs. */
parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_TBI;
}
/* Enable gwp-asan on the system server with a small probability. This is the same
* policy as applied to native processes and system apps. */
parsedArgs.mRuntimeFlags |= Zygote.GWP_ASAN_LEVEL_LOTTERY;
if (shouldProfileSystemServer()) {
parsedArgs.mRuntimeFlags |= Zygote.PROFILE_SYSTEM_SERVER;
}
/* Request to fork the system server process */
pid = Zygote.forkSystemServer(
parsedArgs.mUid, parsedArgs.mGid,
parsedArgs.mGids,
parsedArgs.mRuntimeFlags,
null,
parsedArgs.mPermittedCapabilities,
parsedArgs.mEffectiveCapabilities);
} catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
}
/* For child process */
if (pid == 0) {
if (hasSecondZygote(abiList)) {
waitForSecondaryZygote(socketName);
}
zygoteServer.closeServerSocket();
return handleSystemServerProcess(parsedArgs);
}
return null;
}
/**
* Gets the bit array representation of the provided list of POSIX capabilities.
*/
private static long posixCapabilitiesAsBits(int... capabilities) {
long result = 0;
for (int capability : capabilities) {
if ((capability < 0) || (capability > OsConstants.CAP_LAST_CAP)) {
throw new IllegalArgumentException(String.valueOf(capability));
}
result |= (1L << capability);
}
return result;
}
/**
* This is the entry point for a Zygote process. It creates the Zygote server, loads resources,
* and handles other tasks related to preparing the process for forking into applications.
*
* This process is started with a nice value of -20 (highest priority). All paths that flow
* into new processes are required to either set the priority to the default value or terminate
* before executing any non-system code. The native side of this occurs in SpecializeCommon,
* while the Java Language priority is changed in ZygoteInit.handleSystemServerProcess,
* ZygoteConnection.handleChildProc, and Zygote.childMain.
*
* @param argv Command line arguments used to specify the Zygote's configuration.
*/
@UnsupportedAppUsage
public static void main(String[] argv) {
ZygoteServer zygoteServer = null;
// Mark zygote start. This ensures that thread creation will throw
// an error.
ZygoteHooks.startZygoteNoThreadCreation();
// Zygote goes into its own process group.
try {
Os.setpgid(0, 0);
} catch (ErrnoException ex) {
throw new RuntimeException("Failed to setpgid(0,0)", ex);
}
Runnable caller;
try {
// Store now for StatsLogging later.
final long startTime = SystemClock.elapsedRealtime();
final boolean isRuntimeRestarted = "1".equals(
SystemProperties.get("sys.boot_completed"));
String bootTimeTag = Process.is64Bit() ? "Zygote64Timing" : "Zygote32Timing";
TimingsTraceLog bootTimingsTraceLog = new TimingsTraceLog(bootTimeTag,
Trace.TRACE_TAG_DALVIK);
bootTimingsTraceLog.traceBegin("ZygoteInit");
RuntimeInit.preForkInit();
boolean startSystemServer = false;
String zygoteSocketName = "zygote";
String abiList = null;
boolean enableLazyPreload = false;
for (int i = 1; i < argv.length; i++) {
if ("start-system-server".equals(argv[i])) {
startSystemServer = true;
} else if ("--enable-lazy-preload".equals(argv[i])) {
enableLazyPreload = true;
} else if (argv[i].startsWith(ABI_LIST_ARG)) {
abiList = argv[i].substring(ABI_LIST_ARG.length());
} else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
zygoteSocketName = argv[i].substring(SOCKET_NAME_ARG.length());
} else {
throw new RuntimeException("Unknown command line argument: " + argv[i]);
}
}
final boolean isPrimaryZygote = zygoteSocketName.equals(Zygote.PRIMARY_SOCKET_NAME);
if (!isRuntimeRestarted) {
if (isPrimaryZygote) {
FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__ZYGOTE_INIT_START,
startTime);
} else if (zygoteSocketName.equals(Zygote.SECONDARY_SOCKET_NAME)) {
FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__SECONDARY_ZYGOTE_INIT_START,
startTime);
}
}
if (abiList == null) {
throw new RuntimeException("No ABI list supplied.");
}
// In some configurations, we avoid preloading resources and classes eagerly.
// In such cases, we will preload things prior to our first fork.
if (!enableLazyPreload) {
bootTimingsTraceLog.traceBegin("ZygotePreload");
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
SystemClock.uptimeMillis());
preload(bootTimingsTraceLog);
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
SystemClock.uptimeMillis());
bootTimingsTraceLog.traceEnd(); // ZygotePreload
}
// Do an initial gc to clean up after startup
bootTimingsTraceLog.traceBegin("PostZygoteInitGC");
gcAndFinalize();
bootTimingsTraceLog.traceEnd(); // PostZygoteInitGC
bootTimingsTraceLog.traceEnd(); // ZygoteInit
Zygote.initNativeState(isPrimaryZygote);
ZygoteHooks.stopZygoteNoThreadCreation();
zygoteServer = new ZygoteServer(isPrimaryZygote);
if (startSystemServer) {
Runnable r = forkSystemServer(abiList, zygoteSocketName, 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");
// 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 fatal exception", ex);
throw ex;
} finally {
if (zygoteServer != null) {
zygoteServer.closeServerSocket();
}
}
// We're in the child process and have exited the select loop. Proceed to execute the
// command.
if (caller != null) {
caller.run();
}
}
/**
* Return {@code true} if this device configuration has another zygote.
*
* We determine this by comparing the device ABI list with this zygotes list. If this zygote
* supports all ABIs this device supports, there won't be another zygote.
*/
private static boolean hasSecondZygote(String abiList) {
return !SystemProperties.get("ro.product.cpu.abilist").equals(abiList);
}
private static void waitForSecondaryZygote(String socketName) {
String otherZygoteName = Zygote.PRIMARY_SOCKET_NAME.equals(socketName)
? Zygote.SECONDARY_SOCKET_NAME : Zygote.PRIMARY_SOCKET_NAME;
ZygoteProcess.waitForConnectionToZygote(otherZygoteName);
}
static boolean isPreloadComplete() {
return sPreloadComplete;
}
/**
* Class not instantiable.
*/
private ZygoteInit() {
}
/**
* The main function called when started through the zygote process. This could be unified with
* main(), if the native code in nativeFinishInit() were rationalized with Zygote startup.<p>
*
* Current recognized args:
* <ul>
* <li> <code> [--] <start class name> <args>
* </ul>
*
* @param targetSdkVersion target SDK version
* @param disabledCompatChanges set of disabled compat changes for the process (all others
* are enabled)
* @param argv arg strings
*/
public static Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges,
String[] argv, ClassLoader classLoader) {
if (RuntimeInit.DEBUG) {
Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
RuntimeInit.redirectLogStreams();
RuntimeInit.commonInit();
ZygoteInit.nativeZygoteInit();
return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv,
classLoader);
}
/**
* The main function called when starting a child zygote process. This is used as an alternative
* to zygoteInit(), which skips calling into initialization routines that start the Binder
* threadpool.
*/
static Runnable childZygoteInit(String[] argv) {
RuntimeInit.Arguments args = new RuntimeInit.Arguments(argv);
return RuntimeInit.findStaticMain(args.startClass, args.startArgs, /* classLoader= */null);
}
private static native void nativeZygoteInit();
}
We serv