dalvik_system_Zygote.cpp revision 21c93dc795dfbea3932e0273b1aa873051dde72c
1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18 * dalvik.system.Zygote
19 */
20#include "Dalvik.h"
21#include "native/InternalNativePriv.h"
22
23#ifdef HAVE_SELINUX
24#include <selinux/android.h>
25#endif
26
27#include <signal.h>
28#include <sys/types.h>
29#include <sys/wait.h>
30#include <grp.h>
31#include <errno.h>
32#include <paths.h>
33#include <sys/personality.h>
34#include <sys/stat.h>
35#include <sys/mount.h>
36#include <linux/fs.h>
37#include <cutils/fs.h>
38#include <cutils/sched_policy.h>
39#include <cutils/multiuser.h>
40#include <sched.h>
41
42#if defined(HAVE_PRCTL)
43# include <sys/prctl.h>
44#endif
45
46#define ZYGOTE_LOG_TAG "Zygote"
47
48/* must match values in dalvik.system.Zygote */
49enum {
50    DEBUG_ENABLE_DEBUGGER           = 1,
51    DEBUG_ENABLE_CHECKJNI           = 1 << 1,
52    DEBUG_ENABLE_ASSERT             = 1 << 2,
53    DEBUG_ENABLE_SAFEMODE           = 1 << 3,
54    DEBUG_ENABLE_JNI_LOGGING        = 1 << 4,
55};
56
57/* must match values in dalvik.system.Zygote */
58enum {
59    MOUNT_EXTERNAL_NONE = 0,
60    MOUNT_EXTERNAL_SINGLEUSER = 1,
61    MOUNT_EXTERNAL_MULTIUSER = 2,
62    MOUNT_EXTERNAL_MULTIUSER_ALL = 3,
63};
64
65/*
66 * This signal handler is for zygote mode, since the zygote
67 * must reap its children
68 */
69static void sigchldHandler(int s)
70{
71    pid_t pid;
72    int status;
73
74    while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
75        /* Log process-death status that we care about.  In general it is not
76           safe to call ALOG(...) from a signal handler because of possible
77           reentrancy.  However, we know a priori that the current implementation
78           of ALOG() is safe to call from a SIGCHLD handler in the zygote process.
79           If the ALOG() implementation changes its locking strategy or its use
80           of syscalls within the lazy-init critical section, its use here may
81           become unsafe. */
82        if (WIFEXITED(status)) {
83            if (WEXITSTATUS(status)) {
84                ALOG(LOG_DEBUG, ZYGOTE_LOG_TAG, "Process %d exited cleanly (%d)",
85                    (int) pid, WEXITSTATUS(status));
86            } else {
87                IF_ALOGV(/*should use ZYGOTE_LOG_TAG*/) {
88                    ALOG(LOG_VERBOSE, ZYGOTE_LOG_TAG,
89                        "Process %d exited cleanly (%d)",
90                        (int) pid, WEXITSTATUS(status));
91                }
92            }
93        } else if (WIFSIGNALED(status)) {
94            if (WTERMSIG(status) != SIGKILL) {
95                ALOG(LOG_DEBUG, ZYGOTE_LOG_TAG,
96                    "Process %d terminated by signal (%d)",
97                    (int) pid, WTERMSIG(status));
98            } else {
99                IF_ALOGV(/*should use ZYGOTE_LOG_TAG*/) {
100                    ALOG(LOG_VERBOSE, ZYGOTE_LOG_TAG,
101                        "Process %d terminated by signal (%d)",
102                        (int) pid, WTERMSIG(status));
103                }
104            }
105#ifdef WCOREDUMP
106            if (WCOREDUMP(status)) {
107                ALOG(LOG_INFO, ZYGOTE_LOG_TAG, "Process %d dumped core",
108                    (int) pid);
109            }
110#endif /* ifdef WCOREDUMP */
111        }
112
113        /*
114         * If the just-crashed process is the system_server, bring down zygote
115         * so that it is restarted by init and system server will be restarted
116         * from there.
117         */
118        if (pid == gDvm.systemServerPid) {
119            ALOG(LOG_INFO, ZYGOTE_LOG_TAG,
120                "Exit zygote because system server (%d) has terminated",
121                (int) pid);
122            kill(getpid(), SIGKILL);
123        }
124    }
125
126    if (pid < 0) {
127        ALOG(LOG_WARN, ZYGOTE_LOG_TAG,
128            "Zygote SIGCHLD error in waitpid: %s",strerror(errno));
129    }
130}
131
132/*
133 * configure sigchld handler for the zygote process
134 * This is configured very late, because earlier in the dalvik lifecycle
135 * we can fork() and exec() for the verifier/optimizer, and we
136 * want to waitpid() for those rather than have them be harvested immediately.
137 *
138 * This ends up being called repeatedly before each fork(), but there's
139 * no real harm in that.
140 */
141static void setSignalHandler()
142{
143    int err;
144    struct sigaction sa;
145
146    memset(&sa, 0, sizeof(sa));
147
148    sa.sa_handler = sigchldHandler;
149
150    err = sigaction (SIGCHLD, &sa, NULL);
151
152    if (err < 0) {
153        ALOGW("Error setting SIGCHLD handler: %s", strerror(errno));
154    }
155}
156
157/*
158 * Set the SIGCHLD handler back to default behavior in zygote children
159 */
160static void unsetSignalHandler()
161{
162    int err;
163    struct sigaction sa;
164
165    memset(&sa, 0, sizeof(sa));
166
167    sa.sa_handler = SIG_DFL;
168
169    err = sigaction (SIGCHLD, &sa, NULL);
170
171    if (err < 0) {
172        ALOGW("Error unsetting SIGCHLD handler: %s", strerror(errno));
173    }
174}
175
176/*
177 * Calls POSIX setgroups() using the int[] object as an argument.
178 * A NULL argument is tolerated.
179 */
180
181static int setgroupsIntarray(ArrayObject* gidArray)
182{
183    gid_t *gids;
184    u4 i;
185    s4 *contents;
186
187    if (gidArray == NULL) {
188        return 0;
189    }
190
191    /* just in case gid_t and u4 are different... */
192    gids = (gid_t *)alloca(sizeof(gid_t) * gidArray->length);
193    contents = (s4 *)(void *)gidArray->contents;
194
195    for (i = 0 ; i < gidArray->length ; i++) {
196        gids[i] = (gid_t) contents[i];
197    }
198
199    return setgroups((size_t) gidArray->length, gids);
200}
201
202/*
203 * Sets the resource limits via setrlimit(2) for the values in the
204 * two-dimensional array of integers that's passed in. The second dimension
205 * contains a tuple of length 3: (resource, rlim_cur, rlim_max). NULL is
206 * treated as an empty array.
207 *
208 * -1 is returned on error.
209 */
210static int setrlimitsFromArray(ArrayObject* rlimits)
211{
212    u4 i;
213    struct rlimit rlim;
214
215    if (rlimits == NULL) {
216        return 0;
217    }
218
219    memset (&rlim, 0, sizeof(rlim));
220
221    ArrayObject** tuples = (ArrayObject **)(void *)rlimits->contents;
222
223    for (i = 0; i < rlimits->length; i++) {
224        ArrayObject * rlimit_tuple = tuples[i];
225        s4* contents = (s4 *)(void *)rlimit_tuple->contents;
226        int err;
227
228        if (rlimit_tuple->length != 3) {
229            ALOGE("rlimits array must have a second dimension of size 3");
230            return -1;
231        }
232
233        rlim.rlim_cur = contents[1];
234        rlim.rlim_max = contents[2];
235
236        err = setrlimit(contents[0], &rlim);
237
238        if (err < 0) {
239            return -1;
240        }
241    }
242
243    return 0;
244}
245
246/*
247 * Create private mount space for this process and mount SD card
248 * into it, based on active user.  See storage config details at
249 * http://source.android.com/tech/storage/
250 */
251static int mountExternalStorage(uid_t uid, u4 mountExternal) {
252    userid_t userid = multiuser_get_user_id(uid);
253
254    // Create private mount namespace for our process
255    if (unshare(CLONE_NEWNS) == -1) {
256        SLOGE("Failed to unshare(): %s", strerror(errno));
257        return -1;
258    }
259
260    // Mark rootfs as being a slave in our process so that changes
261    // from parent namespace flow into our process.
262    if (mount("rootfs", "/", NULL, (MS_SLAVE | MS_REC), NULL) == -1) {
263        SLOGE("Failed to mount() rootfs as MS_SLAVE: %s", strerror(errno));
264        return -1;
265    }
266
267    // Create bind mounts to expose external storage
268    if (mountExternal == MOUNT_EXTERNAL_MULTIUSER
269            || mountExternal == MOUNT_EXTERNAL_MULTIUSER_ALL) {
270        const char* storage_base = getenv("ANDROID_STORAGE");
271        const char* target = getenv("EXTERNAL_STORAGE");
272        const char* source_base = getenv("MULTIUSER_EXTERNAL_STORAGE");
273        if (storage_base == NULL || target == NULL || source_base == NULL) {
274            SLOGE("Storage environment undefined; unable to provide external storage");
275            return -1;
276        }
277
278        if (mountExternal == MOUNT_EXTERNAL_MULTIUSER_ALL) {
279            // External storage for all users
280            if (mount(source_base, target, NULL, MS_BIND, NULL) == -1) {
281                SLOGE("Failed to mount %s to %s: %s", source_base, target, strerror(errno));
282                return -1;
283            }
284
285        } else {
286            // External storage for specific user
287            std::string source(StringPrintf("%s/%d", source_base, userid));
288            if (fs_prepare_dir(source.c_str(), 0000, 0, 0) == -1) {
289                return -1;
290            }
291            if (mount(source.c_str(), target, NULL, MS_BIND, NULL) == -1) {
292                SLOGE("Failed to mount %s to %s: %s", source.c_str(), target, strerror(errno));
293                return -1;
294            }
295
296            // Mount shared OBB storage into place
297            std::string obb_source(StringPrintf("%s/obb", source_base));
298            std::string android_target(StringPrintf("%s/Android", target));
299            std::string android_obb_target(StringPrintf("%s/Android/obb", target));
300            if (fs_prepare_dir(obb_source.c_str(), 0000, 0, 0) == -1
301                    || fs_prepare_dir(android_target.c_str(), 0000, 0, 0) == -1
302                    || fs_prepare_dir(android_obb_target.c_str(), 0000, 0, 0) == -1) {
303                return -1;
304            }
305            if (mount(obb_source.c_str(), android_obb_target.c_str(), NULL, MS_BIND, NULL) == -1) {
306                SLOGE("Failed to bind mount %s to %s: %s",
307                        obb_source.c_str(), android_obb_target.c_str(), strerror(errno));
308                return -1;
309            }
310        }
311
312    } else {
313        SLOGE("Mount mode %d unsupported", mountExternal);
314        return -1;
315    }
316
317    return 0;
318}
319
320/* native public static int fork(); */
321static void Dalvik_dalvik_system_Zygote_fork(const u4* args, JValue* pResult)
322{
323    pid_t pid;
324
325    if (!gDvm.zygote) {
326        dvmThrowIllegalStateException(
327            "VM instance not started with -Xzygote");
328
329        RETURN_VOID();
330    }
331
332    if (!dvmGcPreZygoteFork()) {
333        ALOGE("pre-fork heap failed");
334        dvmAbort();
335    }
336
337    setSignalHandler();
338
339    dvmDumpLoaderStats("zygote");
340    pid = fork();
341
342#ifdef HAVE_ANDROID_OS
343    if (pid == 0) {
344        /* child process */
345        extern int gMallocLeakZygoteChild;
346        gMallocLeakZygoteChild = 1;
347    }
348#endif
349
350    RETURN_INT(pid);
351}
352
353/*
354 * Enable/disable debug features requested by the caller.
355 *
356 * debugger
357 *   If set, enable debugging; if not set, disable debugging.  This is
358 *   easy to handle, because the JDWP thread isn't started until we call
359 *   dvmInitAfterZygote().
360 * checkjni
361 *   If set, make sure "check JNI" is enabled.
362 * assert
363 *   If set, make sure assertions are enabled.  This gets fairly weird,
364 *   because it affects the result of a method called by class initializers,
365 *   and hence can't affect pre-loaded/initialized classes.
366 * safemode
367 *   If set, operates the VM in the safe mode. The definition of "safe mode" is
368 *   implementation dependent and currently only the JIT compiler is disabled.
369 *   This is easy to handle because the compiler thread and associated resources
370 *   are not requested until we call dvmInitAfterZygote().
371 */
372static void enableDebugFeatures(u4 debugFlags)
373{
374    ALOGV("debugFlags is 0x%02x", debugFlags);
375
376    gDvm.jdwpAllowed = ((debugFlags & DEBUG_ENABLE_DEBUGGER) != 0);
377
378    if ((debugFlags & DEBUG_ENABLE_CHECKJNI) != 0) {
379        /* turn it on if it's not already enabled */
380        dvmLateEnableCheckedJni();
381    }
382
383    if ((debugFlags & DEBUG_ENABLE_JNI_LOGGING) != 0) {
384        gDvmJni.logThirdPartyJni = true;
385    }
386
387    if ((debugFlags & DEBUG_ENABLE_ASSERT) != 0) {
388        /* turn it on if it's not already enabled */
389        dvmLateEnableAssertions();
390    }
391
392    if ((debugFlags & DEBUG_ENABLE_SAFEMODE) != 0) {
393#if defined(WITH_JIT)
394        /* turn off the jit if it is explicitly requested by the app */
395        if (gDvm.executionMode == kExecutionModeJit)
396            gDvm.executionMode = kExecutionModeInterpFast;
397#endif
398    }
399
400#ifdef HAVE_ANDROID_OS
401    if ((debugFlags & DEBUG_ENABLE_DEBUGGER) != 0) {
402        /* To let a non-privileged gdbserver attach to this
403         * process, we must set its dumpable bit flag. However
404         * we are not interested in generating a coredump in
405         * case of a crash, so also set the coredump size to 0
406         * to disable that
407         */
408        if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) {
409            ALOGE("could not set dumpable bit flag for pid %d: %s",
410                 getpid(), strerror(errno));
411        } else {
412            struct rlimit rl;
413            rl.rlim_cur = 0;
414            rl.rlim_max = RLIM_INFINITY;
415            if (setrlimit(RLIMIT_CORE, &rl) < 0) {
416                ALOGE("could not disable core file generation for pid %d: %s",
417                    getpid(), strerror(errno));
418            }
419        }
420    }
421#endif
422}
423
424/*
425 * Set Linux capability flags.
426 *
427 * Returns 0 on success, errno on failure.
428 */
429static int setCapabilities(int64_t permitted, int64_t effective)
430{
431#ifdef HAVE_ANDROID_OS
432    struct __user_cap_header_struct capheader;
433    struct __user_cap_data_struct capdata;
434
435    memset(&capheader, 0, sizeof(capheader));
436    memset(&capdata, 0, sizeof(capdata));
437
438    capheader.version = _LINUX_CAPABILITY_VERSION;
439    capheader.pid = 0;
440
441    capdata.effective = effective;
442    capdata.permitted = permitted;
443
444    ALOGV("CAPSET perm=%llx eff=%llx", permitted, effective);
445    if (capset(&capheader, &capdata) != 0)
446        return errno;
447#endif /*HAVE_ANDROID_OS*/
448
449    return 0;
450}
451
452#ifdef HAVE_SELINUX
453/*
454 * Set SELinux security context.
455 *
456 * Returns 0 on success, -1 on failure.
457 */
458static int setSELinuxContext(uid_t uid, bool isSystemServer,
459                             const char *seInfo, const char *niceName)
460{
461#ifdef HAVE_ANDROID_OS
462    return selinux_android_setcontext(uid, isSystemServer, seInfo, niceName);
463#else
464    return 0;
465#endif
466}
467#endif
468
469/*
470 * Utility routine to fork zygote and specialize the child process.
471 */
472static pid_t forkAndSpecializeCommon(const u4* args, bool isSystemServer)
473{
474    pid_t pid;
475
476    uid_t uid = (uid_t) args[0];
477    gid_t gid = (gid_t) args[1];
478    ArrayObject* gids = (ArrayObject *)args[2];
479    u4 debugFlags = args[3];
480    ArrayObject *rlimits = (ArrayObject *)args[4];
481    u4 mountExternal = MOUNT_EXTERNAL_NONE;
482    int64_t permittedCapabilities, effectiveCapabilities;
483#ifdef HAVE_SELINUX
484    char *seInfo = NULL;
485    char *niceName = NULL;
486#endif
487
488    if (isSystemServer) {
489        /*
490         * Don't use GET_ARG_LONG here for now.  gcc is generating code
491         * that uses register d8 as a temporary, and that's coming out
492         * scrambled in the child process.  b/3138621
493         */
494        //permittedCapabilities = GET_ARG_LONG(args, 5);
495        //effectiveCapabilities = GET_ARG_LONG(args, 7);
496        permittedCapabilities = args[5] | (int64_t) args[6] << 32;
497        effectiveCapabilities = args[7] | (int64_t) args[8] << 32;
498    } else {
499        mountExternal = args[5];
500        permittedCapabilities = effectiveCapabilities = 0;
501#ifdef HAVE_SELINUX
502        StringObject* seInfoObj = (StringObject*)args[6];
503        if (seInfoObj) {
504            seInfo = dvmCreateCstrFromString(seInfoObj);
505            if (!seInfo) {
506                ALOGE("seInfo dvmCreateCstrFromString failed");
507                dvmAbort();
508            }
509        }
510        StringObject* niceNameObj = (StringObject*)args[7];
511        if (niceNameObj) {
512            niceName = dvmCreateCstrFromString(niceNameObj);
513            if (!niceName) {
514                ALOGE("niceName dvmCreateCstrFromString failed");
515                dvmAbort();
516            }
517        }
518#endif
519    }
520
521    if (!gDvm.zygote) {
522        dvmThrowIllegalStateException(
523            "VM instance not started with -Xzygote");
524
525        return -1;
526    }
527
528    if (!dvmGcPreZygoteFork()) {
529        ALOGE("pre-fork heap failed");
530        dvmAbort();
531    }
532
533    setSignalHandler();
534
535    dvmDumpLoaderStats("zygote");
536    pid = fork();
537
538    if (pid == 0) {
539        int err;
540        /* The child process */
541
542#ifdef HAVE_ANDROID_OS
543        extern int gMallocLeakZygoteChild;
544        gMallocLeakZygoteChild = 1;
545
546        /* keep caps across UID change, unless we're staying root */
547        if (uid != 0) {
548            err = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
549
550            if (err < 0) {
551                ALOGE("cannot PR_SET_KEEPCAPS: %s", strerror(errno));
552                dvmAbort();
553            }
554        }
555
556#endif /* HAVE_ANDROID_OS */
557
558        if (mountExternal != MOUNT_EXTERNAL_NONE) {
559            err = mountExternalStorage(uid, mountExternal);
560            if (err < 0) {
561                ALOGE("cannot mountExternalStorage(): %s", strerror(errno));
562
563                if (errno == ENOTCONN || errno == EROFS) {
564                    // When device is actively encrypting, we get ENOTCONN here
565                    // since FUSE was mounted before the framework restarted.
566                    // When encrypted device is booting, we get EROFS since
567                    // FUSE hasn't been created yet by init.
568                    // In either case, continue without external storage.
569                } else {
570                    dvmAbort();
571                }
572            }
573        }
574
575        err = setgroupsIntarray(gids);
576        if (err < 0) {
577            ALOGE("cannot setgroups(): %s", strerror(errno));
578            dvmAbort();
579        }
580
581        err = setrlimitsFromArray(rlimits);
582        if (err < 0) {
583            ALOGE("cannot setrlimit(): %s", strerror(errno));
584            dvmAbort();
585        }
586
587        err = setgid(gid);
588        if (err < 0) {
589            ALOGE("cannot setgid(%d): %s", gid, strerror(errno));
590            dvmAbort();
591        }
592
593        err = setuid(uid);
594        if (err < 0) {
595            ALOGE("cannot setuid(%d): %s", uid, strerror(errno));
596            dvmAbort();
597        }
598
599        int current = personality(0xffffFFFF);
600        int success = personality((ADDR_NO_RANDOMIZE | current));
601        if (success == -1) {
602          ALOGW("Personality switch failed. current=%d error=%d\n", current, errno);
603        }
604
605        err = setCapabilities(permittedCapabilities, effectiveCapabilities);
606        if (err != 0) {
607            ALOGE("cannot set capabilities (%llx,%llx): %s",
608                permittedCapabilities, effectiveCapabilities, strerror(err));
609            dvmAbort();
610        }
611
612        err = set_sched_policy(0, SP_DEFAULT);
613        if (err < 0) {
614            ALOGE("cannot set_sched_policy(0, SP_DEFAULT): %s", strerror(-err));
615            dvmAbort();
616        }
617
618#ifdef HAVE_SELINUX
619        err = setSELinuxContext(uid, isSystemServer, seInfo, niceName);
620        if (err < 0) {
621            ALOGE("cannot set SELinux context: %s\n", strerror(errno));
622            dvmAbort();
623        }
624        // These free(3) calls are safe because we know we're only ever forking
625        // a single-threaded process, so we know no other thread held the heap
626        // lock when we forked.
627        free(seInfo);
628        free(niceName);
629#endif
630
631        /*
632         * Our system thread ID has changed.  Get the new one.
633         */
634        Thread* thread = dvmThreadSelf();
635        thread->systemTid = dvmGetSysThreadId();
636
637        /* configure additional debug options */
638        enableDebugFeatures(debugFlags);
639
640        unsetSignalHandler();
641        gDvm.zygote = false;
642        if (!dvmInitAfterZygote()) {
643            ALOGE("error in post-zygote initialization");
644            dvmAbort();
645        }
646    } else if (pid > 0) {
647        /* the parent process */
648#ifdef HAVE_SELINUX
649        free(seInfo);
650        free(niceName);
651#endif
652    }
653
654    return pid;
655}
656
657/*
658 * native public static int nativeForkAndSpecialize(int uid, int gid,
659 *     int[] gids, int debugFlags, int[][] rlimits, int mountExternal,
660 *     String seInfo, String niceName);
661 */
662static void Dalvik_dalvik_system_Zygote_forkAndSpecialize(const u4* args,
663    JValue* pResult)
664{
665    pid_t pid;
666
667    pid = forkAndSpecializeCommon(args, false);
668
669    RETURN_INT(pid);
670}
671
672/*
673 * native public static int nativeForkSystemServer(int uid, int gid,
674 *     int[] gids, int debugFlags, int[][] rlimits,
675 *     long permittedCapabilities, long effectiveCapabilities);
676 */
677static void Dalvik_dalvik_system_Zygote_forkSystemServer(
678        const u4* args, JValue* pResult)
679{
680    pid_t pid;
681    pid = forkAndSpecializeCommon(args, true);
682
683    /* The zygote process checks whether the child process has died or not. */
684    if (pid > 0) {
685        int status;
686
687        ALOGI("System server process %d has been created", pid);
688        gDvm.systemServerPid = pid;
689        /* There is a slight window that the system server process has crashed
690         * but it went unnoticed because we haven't published its pid yet. So
691         * we recheck here just to make sure that all is well.
692         */
693        if (waitpid(pid, &status, WNOHANG) == pid) {
694            ALOGE("System server process %d has died. Restarting Zygote!", pid);
695            kill(getpid(), SIGKILL);
696        }
697    }
698    RETURN_INT(pid);
699}
700
701/* native private static void nativeExecShell(String command);
702 */
703static void Dalvik_dalvik_system_Zygote_execShell(
704        const u4* args, JValue* pResult)
705{
706    StringObject* command = (StringObject*)args[0];
707
708    const char *argp[] = {_PATH_BSHELL, "-c", NULL, NULL};
709    argp[2] = dvmCreateCstrFromString(command);
710
711    ALOGI("Exec: %s %s %s", argp[0], argp[1], argp[2]);
712
713    execv(_PATH_BSHELL, (char**)argp);
714    exit(127);
715}
716
717const DalvikNativeMethod dvm_dalvik_system_Zygote[] = {
718    { "nativeFork", "()I",
719      Dalvik_dalvik_system_Zygote_fork },
720    { "nativeForkAndSpecialize", "(II[II[[IILjava/lang/String;Ljava/lang/String;)I",
721      Dalvik_dalvik_system_Zygote_forkAndSpecialize },
722    { "nativeForkSystemServer", "(II[II[[IJJ)I",
723      Dalvik_dalvik_system_Zygote_forkSystemServer },
724    { "nativeExecShell", "(Ljava/lang/String;)V",
725      Dalvik_dalvik_system_Zygote_execShell },
726    { NULL, NULL, NULL },
727};
728