/* * 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 android.content.res.Resources; import android.content.res.TypedArray; import android.icu.impl.CacheValue; import android.icu.text.DecimalFormatSymbols; import android.icu.util.ULocale; import android.net.LocalServerSocket; import android.opengl.EGL14; import android.os.IInstalld; import android.os.Process; import android.os.RemoteException; import android.os.Seccomp; import android.os.ServiceManager; import android.os.ServiceSpecificException; import android.os.SystemClock; import android.os.SystemProperties; import android.os.Trace; import android.os.ZygoteProcess; import android.os.storage.StorageManager; import android.security.keystore.AndroidKeyStoreProvider; import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; import android.text.Hyphenator; import android.util.EventLog; import android.util.Log; import android.util.Slog; import android.webkit.WebViewFactory; import android.widget.TextView; import dalvik.system.DexFile; import dalvik.system.PathClassLoader; import dalvik.system.VMRuntime; import dalvik.system.ZygoteHooks; import libcore.io.IoUtils; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.security.Security; import java.security.Provider; /** * 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 ZygoteConnection.Arguments} for documentation on the * client protocol. * * @hide */ public class ZygoteInit { private static final String TAG = "Zygote"; private static final String PROPERTY_DISABLE_OPENGL_PRELOADING = "ro.zygote.disable_gl_preload"; private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0"; private static final String PROPERTY_RUNNING_IN_CONTAINER = "ro.boot.container"; private static final int LOG_BOOT_PROGRESS_PRELOAD_START = 3020; private static final int LOG_BOOT_PROGRESS_PRELOAD_END = 3030; /** when preloading, GC after allocating this many bytes */ private static final int PRELOAD_GC_THRESHOLD = 50000; private static final String ABI_LIST_ARG = "--abi-list="; private static final String SOCKET_NAME_ARG = "--socket-name="; /** * Used to pre-load resources. */ private static Resources mResources; /** * The path of a file that contains classes to preload. */ private static final String PRELOADED_CLASSES = "/system/etc/preloaded-classes"; /** Controls whether we should preload resources during zygote init. */ public static final boolean PRELOAD_RESOURCES = true; 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; static void preload() { Log.d(TAG, "begin preload"); Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "BeginIcuCachePinning"); beginIcuCachePinning(); Trace.traceEnd(Trace.TRACE_TAG_DALVIK); Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadClasses"); preloadClasses(); Trace.traceEnd(Trace.TRACE_TAG_DALVIK); Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadResources"); preloadResources(); Trace.traceEnd(Trace.TRACE_TAG_DALVIK); Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadOpenGL"); preloadOpenGL(); 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(); endIcuCachePinning(); warmUpJcaProviders(); Log.d(TAG, "end preload"); } private static void beginIcuCachePinning() { // Pin ICU data in memory from this point that would normally be held by soft references. // Without this, any references created immediately below or during class preloading // would be collected when the Zygote GC runs in gcAndFinalize(). Log.i(TAG, "Installing ICU cache reference pinning..."); CacheValue.setStrength(CacheValue.Strength.STRONG); Log.i(TAG, "Preloading ICU data..."); // Explicitly exercise code to cache data apps are likely to need. ULocale[] localesToPin = { ULocale.ROOT, ULocale.US, ULocale.getDefault() }; for (ULocale uLocale : localesToPin) { new DecimalFormatSymbols(uLocale); } } private static void endIcuCachePinning() { // All cache references created by ICU from this point will be soft. CacheValue.setStrength(CacheValue.Strength.SOFT); Log.i(TAG, "Uninstalled ICU cache reference pinning..."); } private static void preloadSharedLibraries() { Log.i(TAG, "Preloading shared libraries..."); System.loadLibrary("android"); System.loadLibrary("compiler_rt"); System.loadLibrary("jnigraphics"); } private static void preloadOpenGL() { String driverPackageName = SystemProperties.get(PROPERTY_GFX_DRIVER); if (!SystemProperties.getBoolean(PROPERTY_DISABLE_OPENGL_PRELOADING, false) || driverPackageName == null || driverPackageName.isEmpty()) { EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); } } 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() manipulates the list of JCA providers to insert // preferred providers. Note this is not done via security.properties as the JCA providers // are not on the classpath in the case of, for example, raw dalvikvm runtimes. 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); } /** * 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; } // Alter the target heap utilization. With explicit GCs this // is not likely to have any effect. float defaultUtilization = runtime.getTargetHeapUtilization(); runtime.setTargetHeapUtilization(0.8f); try { BufferedReader br = new BufferedReader(new InputStreamReader(is), 256); int count = 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 { if (false) { Log.v(TAG, "Preloading " + line + "..."); } // 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) { 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; } if (t instanceof RuntimeException) { throw (RuntimeException) t; } throw new RuntimeException(t); } Trace.traceEnd(Trace.TRACE_TAG_DALVIK); } Log.i(TAG, "...preloaded " + count + " classes in " + (SystemClock.uptimeMillis()-startTime) + "ms."); } catch (IOException e) { Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e); } finally { IoUtils.closeQuietly(is); // Restore default. runtime.setTargetHeapUtilization(defaultUtilization); // 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); // 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 commonly used resources, so they can be shared across * processes. * * These tend to be a few Kbytes, but are frequently in the 20-40K * range, and occasionally even larger. */ private static void preloadResources() { final VMRuntime runtime = VMRuntime.getRuntime(); try { mResources = Resources.getSystem(); mResources.startPreloading(); if (PRELOAD_RESOURCES) { Log.i(TAG, "Preloading resources..."); long startTime = SystemClock.uptimeMillis(); TypedArray ar = mResources.obtainTypedArray( com.android.internal.R.array.preloaded_drawables); int N = preloadDrawables(ar); ar.recycle(); Log.i(TAG, "...preloaded " + N + " resources in " + (SystemClock.uptimeMillis()-startTime) + "ms."); startTime = SystemClock.uptimeMillis(); ar = mResources.obtainTypedArray( com.android.internal.R.array.preloaded_color_state_lists); N = preloadColorStateLists(ar); ar.recycle(); Log.i(TAG, "...preloaded " + N + " resources in " + (SystemClock.uptimeMillis()-startTime) + "ms."); if (mResources.getBoolean( com.android.internal.R.bool.config_freeformWindowManagement)) { startTime = SystemClock.uptimeMillis(); ar = mResources.obtainTypedArray( com.android.internal.R.array.preloaded_freeform_multi_window_drawables); N = preloadDrawables(ar); ar.recycle(); Log.i(TAG, "...preloaded " + N + " resource in " + (SystemClock.uptimeMillis() - startTime) + "ms."); } } mResources.finishPreloading(); } catch (RuntimeException e) { Log.w(TAG, "Failure preloading resources", e); } } private static int preloadColorStateLists(TypedArray ar) { int N = ar.length(); for (int i=0; i<N; i++) { int id = ar.getResourceId(i, 0); if (false) { Log.v(TAG, "Preloading resource #" + Integer.toHexString(id)); } if (id != 0) { if (mResources.getColorStateList(id, null) == null) { throw new IllegalArgumentException( "Unable to find preloaded color resource #0x" + Integer.toHexString(id) + " (" + ar.getString(i) + ")"); } } } return N; } private static int preloadDrawables(TypedArray ar) { int N = ar.length(); for (int i=0; i<N; i++) { int id = ar.getResourceId(i, 0); if (false) { Log.v(TAG, "Preloading resource #" + Integer.toHexString(id)); } if (id != 0) { if (mResources.getDrawable(id, null) == null) { throw new IllegalArgumentException( "Unable to find preloaded drawable resource #0x" + Integer.toHexString(id) + " (" + ar.getString(i) + ")"); } } } return N; } /** * 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(). */ /*package*/ static void gcAndFinalize() { final VMRuntime runtime = VMRuntime.getRuntime(); /* runFinalizationSync() lets finalizers be called in Zygote, * which doesn't have a HeapWorker thread. */ System.gc(); runtime.runFinalizationSync(); System.gc(); } /** * Finish remaining work for the newly forked system server process. */ private static void handleSystemServerProcess( ZygoteConnection.Arguments parsedArgs) throws Zygote.MethodAndArgsCaller { // set umask to 0077 so new files and directories will default to owner-only permissions. Os.umask(S_IRWXG | S_IRWXO); if (parsedArgs.niceName != null) { Process.setArgV0(parsedArgs.niceName); } final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH"); if (systemServerClasspath != null) { performSystemServerDexOpt(systemServerClasspath); } if (parsedArgs.invokeWith != null) { String[] args = parsedArgs.remainingArgs; // 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.invokeWith, parsedArgs.niceName, parsedArgs.targetSdkVersion, VMRuntime.getCurrentInstructionSet(), null, args); } else { ClassLoader cl = null; if (systemServerClasspath != null) { cl = createPathClassLoader(systemServerClasspath, parsedArgs.targetSdkVersion); Thread.currentThread().setContextClassLoader(cl); } /* * Pass the remaining arguments to SystemServer. */ ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl); } /* should never reach here */ } /** * 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 use java.library.path as the native library path. */ static PathClassLoader createPathClassLoader(String classPath, int targetSdkVersion) { String libraryPath = System.getProperty("java.library.path"); return PathClassLoaderFactory.createClassLoader(classPath, libraryPath, libraryPath, ClassLoader.getSystemClassLoader(), targetSdkVersion, true /* isNamespaceShared */); } /** * Performs dex-opt on the elements of {@code classPath}, if needed. We * choose the instruction set of the current runtime. */ private static void performSystemServerDexOpt(String classPath) { final String[] classPathElements = classPath.split(":"); final IInstalld installd = IInstalld.Stub .asInterface(ServiceManager.getService("installd")); final String instructionSet = VMRuntime.getRuntime().vmInstructionSet(); String sharedLibraries = ""; for (String classPathElement : classPathElements) { // System server is fully AOTed and never profiled // for profile guided compilation. // TODO: Make this configurable between INTERPRET_ONLY, SPEED, SPACE and EVERYTHING? int dexoptNeeded; try { dexoptNeeded = DexFile.getDexOptNeeded( classPathElement, instructionSet, "speed", false /* newProfile */); } catch (FileNotFoundException ignored) { // Do not add to the classpath. Log.w(TAG, "Missing classpath element for system server: " + classPathElement); continue; } catch (IOException e) { // Not fully clear what to do here as we don't know the cause of the // IO exception. Add to the classpath to be conservative, but don't // attempt to compile it. Log.w(TAG, "Error checking classpath element for system server: " + classPathElement, e); dexoptNeeded = DexFile.NO_DEXOPT_NEEDED; } if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) { final String packageName = "*"; final String outputPath = null; final int dexFlags = 0; final String compilerFilter = "speed"; final String uuid = StorageManager.UUID_PRIVATE_INTERNAL; try { installd.dexopt(classPathElement, Process.SYSTEM_UID, packageName, instructionSet, dexoptNeeded, outputPath, dexFlags, compilerFilter, uuid, sharedLibraries); } catch (RemoteException | ServiceSpecificException e) { // Ignore (but log), we need this on the classpath for fallback mode. Log.w(TAG, "Failed compiling classpath element for system server: " + classPathElement, e); } } if (!sharedLibraries.isEmpty()) { sharedLibraries += ":"; } sharedLibraries += classPathElement; } } /** * Prepare the arguments and fork for the system server process. */ private static boolean startSystemServer(String abiList, String socketName, ZygoteServer zygoteServer) throws Zygote.MethodAndArgsCaller, RuntimeException { 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_RESOURCE, OsConstants.CAP_SYS_TIME, OsConstants.CAP_SYS_TTY_CONFIG, OsConstants.CAP_WAKE_ALARM ); /* Containers run without this capability, so avoid setting it in that case */ if (!SystemProperties.getBoolean(PROPERTY_RUNNING_IN_CONTAINER, false)) { capabilities |= posixCapabilitiesAsBits(OsConstants.CAP_BLOCK_SUSPEND); } /* 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,1032,3001,3002,3003,3006,3007,3009,3010", "--capabilities=" + capabilities + "," + capabilities, "--nice-name=system_server", "--runtime-args", "com.android.server.SystemServer", }; ZygoteConnection.Arguments parsedArgs = null; int pid; try { parsedArgs = new ZygoteConnection.Arguments(args); ZygoteConnection.applyDebuggerSystemProperty(parsedArgs); ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs); /* Request to fork the system server process */ pid = Zygote.forkSystemServer( parsedArgs.uid, parsedArgs.gid, parsedArgs.gids, parsedArgs.debugFlags, null, parsedArgs.permittedCapabilities, parsedArgs.effectiveCapabilities); } catch (IllegalArgumentException ex) { throw new RuntimeException(ex); } /* For child process */ if (pid == 0) { if (hasSecondZygote(abiList)) { waitForSecondaryZygote(socketName); } zygoteServer.closeServerSocket(); handleSystemServerProcess(parsedArgs); } return true; } /** * 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; } public static void main(String argv[]) { ZygoteServer zygoteServer = new ZygoteServer(); // 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); } try { Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "ZygoteInit"); RuntimeInit.enableDdms(); // Start profiling the zygote initialization. SamplingProfilerIntegration.start(); boolean startSystemServer = false; String socketName = "zygote"; String abiList = null; for (int i = 1; i < argv.length; i++) { if ("start-system-server".equals(argv[i])) { startSystemServer = 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)) { socketName = argv[i].substring(SOCKET_NAME_ARG.length()); } else { throw new RuntimeException("Unknown command line argument: " + argv[i]); } } if (abiList == null) { throw new RuntimeException("No ABI list supplied."); } zygoteServer.registerServerSocket(socketName); Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "ZygotePreload"); EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START, SystemClock.uptimeMillis()); preload(); EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END, SystemClock.uptimeMillis()); Trace.traceEnd(Trace.TRACE_TAG_DALVIK); // Finish profiling the zygote initialization. SamplingProfilerIntegration.writeZygoteSnapshot(); // Do an initial gc to clean up after startup Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PostZygoteInitGC"); gcAndFinalize(); Trace.traceEnd(Trace.TRACE_TAG_DALVIK); // Disable tracing so that forked processes do not inherit stale tracing tags from // Zygote. Trace.setTracingEnabled(false); // Zygote process unmounts root storage spaces. Zygote.nativeUnmountStorageOnInit(); // Set seccomp policy Seccomp.setPolicy(); ZygoteHooks.stopZygoteNoThreadCreation(); if (startSystemServer) { startSystemServer(abiList, socketName, zygoteServer); } Log.i(TAG, "Accepting command socket connections"); zygoteServer.runSelectLoop(abiList); zygoteServer.closeServerSocket(); } catch (Zygote.MethodAndArgsCaller caller) { caller.run(); } catch (Throwable ex) { Log.e(TAG, "System zygote died with exception", ex); zygoteServer.closeServerSocket(); throw ex; } } /** * 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 = Process.ZYGOTE_SOCKET.equals(socketName) ? Process.SECONDARY_ZYGOTE_SOCKET : Process.ZYGOTE_SOCKET; while (true) { try { final ZygoteProcess.ZygoteState zs = ZygoteProcess.ZygoteState.connect(otherZygoteName); zs.close(); break; } catch (IOException ioe) { Log.w(TAG, "Got error connecting to zygote, retrying. msg= " + ioe.getMessage()); } try { Thread.sleep(1000); } catch (InterruptedException ie) { } } } /** * 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 argv arg strings */ public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) throws Zygote.MethodAndArgsCaller { 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(); RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader); } private static final native void nativeZygoteInit(); }