1 /*
2 * Copyright (C) 2020 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 #include "com_android_internal_os_Zygote.h"
18
19 #include <algorithm>
20 #include <android-base/logging.h>
21 #include <async_safe/log.h>
22 #include <cctype>
23 #include <chrono>
24 #include <core_jni_helpers.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <jni.h>
28 #include <nativehelper/JNIHelp.h>
29 #include <optional>
30 #include <poll.h>
31 #include <unistd.h>
32 #include <utility>
33 #include <utils/misc.h>
34 #include <sys/mman.h>
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <sys/system_properties.h>
38 #include <vector>
39
40 namespace android {
41
42 using namespace std::placeholders;
43 using android::base::StringPrintf;
44 using android::zygote::ZygoteFailure;
45
46 // WARNING: Knows a little about the wire protocol used to communicate with Zygote.
47
48 // Commands and nice names have large arbitrary size limits to avoid dynamic memory allocation.
49 constexpr size_t MAX_COMMAND_BYTES = 32768;
50 constexpr size_t NICE_NAME_BYTES = 128;
51
52 // A buffer optionally bundled with a file descriptor from which we can fill it.
53 // Does not own the file descriptor; destroying a NativeCommandBuffer does not
54 // close the descriptor.
55 class NativeCommandBuffer {
56 public:
NativeCommandBuffer(int sourceFd)57 NativeCommandBuffer(int sourceFd): mEnd(0), mNext(0), mLinesLeft(0), mFd(sourceFd) {}
58
59 // Read mNext line from mFd, filling mBuffer from file descriptor, as needed.
60 // Return a pair of pointers pointing to the first character, and one past the
61 // mEnd of the line, i.e. at the newline. Returns nothing on failure.
62 template<class FailFn>
readLine(FailFn fail_fn)63 std::optional<std::pair<char*, char*>> readLine(FailFn fail_fn) {
64 char* result = mBuffer + mNext;
65 while (true) {
66 if (mNext == mEnd) {
67 if (mEnd == MAX_COMMAND_BYTES) {
68 return {};
69 }
70 if (mFd == -1) {
71 fail_fn("ZygoteCommandBuffer.readLine attempted to read from mFd -1");
72 }
73 ssize_t nread = TEMP_FAILURE_RETRY(read(mFd, mBuffer + mEnd, MAX_COMMAND_BYTES - mEnd));
74 if (nread <= 0) {
75 if (nread == 0) {
76 return {};
77 }
78 fail_fn(CREATE_ERROR("session socket read failed: %s", strerror(errno)));
79 } else if (nread == MAX_COMMAND_BYTES - mEnd) {
80 // This is pessimistic by one character, but close enough.
81 fail_fn("ZygoteCommandBuffer overflowed: command too long");
82 }
83 mEnd += nread;
84 }
85 // UTF-8 does not allow newline to occur as part of a multibyte character.
86 char* nl = static_cast<char *>(memchr(mBuffer + mNext, '\n', mEnd - mNext));
87 if (nl == nullptr) {
88 mNext = mEnd;
89 } else {
90 mNext = nl - mBuffer + 1;
91 if (--mLinesLeft < 0) {
92 fail_fn("ZygoteCommandBuffer.readLine attempted to read past mEnd of command");
93 }
94 return std::make_pair(result, nl);
95 }
96 }
97 }
98
reset()99 void reset() {
100 mNext = 0;
101 }
102
103 // Make sure the current command is fully buffered, without reading past the current command.
104 template<class FailFn>
readAllLines(FailFn fail_fn)105 void readAllLines(FailFn fail_fn) {
106 while (mLinesLeft > 0) {
107 readLine(fail_fn);
108 }
109 }
110
clear()111 void clear() {
112 // Don't bother to actually clear the buffer; it'll be unmapped in the child anyway.
113 reset();
114 mNiceName[0] = '\0';
115 mEnd = 0;
116 }
117
118 // Insert line into the mBuffer. Checks that the mBuffer is not associated with an mFd.
119 // Implicitly adds newline separators. Allows mBuffer contents to be explicitly set.
insert(const char * line,size_t lineLen)120 void insert(const char* line, size_t lineLen) {
121 DCHECK(mFd == -1);
122 CHECK(mEnd + lineLen < MAX_COMMAND_BYTES);
123 strncpy(mBuffer + mEnd, line, lineLen);
124 mBuffer[mEnd + lineLen] = '\n';
125 mEnd += lineLen + 1;
126 }
127
128 // Clear mBuffer, start reading new command, return the number of arguments, leaving mBuffer
129 // positioned at the beginning of first argument. Return 0 on EOF.
130 template<class FailFn>
getCount(FailFn fail_fn)131 int getCount(FailFn fail_fn) {
132 mLinesLeft = 1;
133 auto line = readLine(fail_fn);
134 if (!line.has_value()) {
135 return 0;
136 }
137 char* countString = line.value().first; // Newline terminated.
138 long nArgs = atol(countString);
139 if (nArgs <= 0 || nArgs >= MAX_COMMAND_BYTES / 2) {
140 fail_fn(CREATE_ERROR("Unreasonable argument count %ld", nArgs));
141 }
142 mLinesLeft = nArgs;
143 return static_cast<int>(nArgs);
144 }
145
146 // Is the mBuffer a simple fork command?
147 // We disallow request to wrap the child process, child zygotes, anything that
148 // mentions capabilities or requests uid < minUid.
149 // We insist that --setuid and --setgid arguments are explicitly included and that the
150 // command starts with --runtime-args.
151 // Assumes we are positioned at the beginning of the command after the argument count,
152 // and leaves the position at some indeterminate position in the buffer.
153 // As a side effect, this sets mNiceName to a non-empty string, if possible.
154 template<class FailFn>
isSimpleForkCommand(int minUid,FailFn fail_fn)155 bool isSimpleForkCommand(int minUid, FailFn fail_fn) {
156 if (mLinesLeft <= 0 || mLinesLeft >= MAX_COMMAND_BYTES / 2) {
157 return false;
158 }
159 static const char* RUNTIME_ARGS = "--runtime-args";
160 static const char* INVOKE_WITH = "--invoke-with";
161 static const char* CHILD_ZYGOTE = "--start-child-zygote";
162 static const char* SETUID = "--setuid=";
163 static const char* SETGID = "--setgid=";
164 static const char* CAPABILITIES = "--capabilities";
165 static const char* NICE_NAME = "--nice-name=";
166 static const size_t RA_LENGTH = strlen(RUNTIME_ARGS);
167 static const size_t IW_LENGTH = strlen(INVOKE_WITH);
168 static const size_t CZ_LENGTH = strlen(CHILD_ZYGOTE);
169 static const size_t SU_LENGTH = strlen(SETUID);
170 static const size_t SG_LENGTH = strlen(SETGID);
171 static const size_t CA_LENGTH = strlen(CAPABILITIES);
172 static const size_t NN_LENGTH = strlen(NICE_NAME);
173
174 bool saw_setuid = false, saw_setgid = false;
175 bool saw_runtime_args = false;
176
177 while (mLinesLeft > 0) {
178 auto read_result = readLine(fail_fn);
179 if (!read_result.has_value()) {
180 return false;
181 }
182 auto [arg_start, arg_end] = read_result.value();
183 if (arg_end - arg_start == RA_LENGTH
184 && strncmp(arg_start, RUNTIME_ARGS, RA_LENGTH) == 0) {
185 saw_runtime_args = true;
186 continue;
187 }
188 if (arg_end - arg_start >= NN_LENGTH
189 && strncmp(arg_start, NICE_NAME, NN_LENGTH) == 0) {
190 size_t name_len = arg_end - (arg_start + NN_LENGTH);
191 size_t copy_len = std::min(name_len, NICE_NAME_BYTES - 1);
192 memcpy(mNiceName, arg_start + NN_LENGTH, copy_len);
193 mNiceName[copy_len] = '\0';
194 if (haveWrapProperty()) {
195 return false;
196 }
197 continue;
198 }
199 if (arg_end - arg_start == IW_LENGTH
200 && strncmp(arg_start, INVOKE_WITH, IW_LENGTH) == 0) {
201 // This also removes the need for invoke-with security checks here.
202 return false;
203 }
204 if (arg_end - arg_start == CZ_LENGTH
205 && strncmp(arg_start, CHILD_ZYGOTE, CZ_LENGTH) == 0) {
206 return false;
207 }
208 if (arg_end - arg_start >= CA_LENGTH
209 && strncmp(arg_start, CAPABILITIES, CA_LENGTH) == 0) {
210 return false;
211 }
212 if (arg_end - arg_start >= SU_LENGTH
213 && strncmp(arg_start, SETUID, SU_LENGTH) == 0) {
214 int uid = digitsVal(arg_start + SU_LENGTH, arg_end);
215 if (uid < minUid) {
216 return false;
217 }
218 saw_setuid = true;
219 continue;
220 }
221 if (arg_end - arg_start >= SG_LENGTH
222 && strncmp(arg_start, SETGID, SG_LENGTH) == 0) {
223 int gid = digitsVal(arg_start + SG_LENGTH, arg_end);
224 if (gid == -1) {
225 return false;
226 }
227 saw_setgid = true;
228 }
229 // ro.debuggable can be handled entirely in the child unless --invoke-with is also specified.
230 // Thus we do not need to check it here.
231 }
232 return saw_runtime_args && saw_setuid && saw_setgid;
233 }
234
setFd(int new_fd)235 void setFd(int new_fd) {
236 mFd = new_fd;
237 }
238
getFd() const239 int getFd() const {
240 return mFd;
241 }
242
niceNameAddr() const243 const char* niceNameAddr() const {
244 return mNiceName;
245 }
246
247 // Debug only:
logState() const248 void logState() const {
249 ALOGD("mbuffer starts with %c%c, nice name is %s, "
250 "mEnd = %u, mNext = %u, mLinesLeft = %d, mFd = %d",
251 mBuffer[0], (mBuffer[1] == '\n' ? ' ' : mBuffer[1]),
252 niceNameAddr(),
253 static_cast<unsigned>(mEnd), static_cast<unsigned>(mNext),
254 static_cast<int>(mLinesLeft), mFd);
255 }
256
257 private:
haveWrapProperty()258 bool haveWrapProperty() {
259 static const char* WRAP = "wrap.";
260 static const size_t WRAP_LENGTH = strlen(WRAP);
261 char propNameBuf[WRAP_LENGTH + NICE_NAME_BYTES];
262 strcpy(propNameBuf, WRAP);
263 strlcpy(propNameBuf + WRAP_LENGTH, mNiceName, NICE_NAME_BYTES);
264 return __system_property_find(propNameBuf) != nullptr;
265 }
266 // Picky version of atoi(). No sign or unexpected characters allowed. Return -1 on failure.
digitsVal(char * start,char * end)267 static int digitsVal(char* start, char* end) {
268 int result = 0;
269 if (end - start > 6) {
270 return -1;
271 }
272 for (char* dp = start; dp < end; ++dp) {
273 if (*dp < '0' || *dp > '9') {
274 ALOGW("Argument failed integer format check");
275 return -1;
276 }
277 result = 10 * result + (*dp - '0');
278 }
279 return result;
280 }
281
282 uint32_t mEnd; // Index of first empty byte in the mBuffer.
283 uint32_t mNext; // Index of first character past last line returned by readLine.
284 int32_t mLinesLeft; // Lines in current command that haven't yet been read.
285 int mFd; // Open file descriptor from which we can read more. -1 if none.
286 char mNiceName[NICE_NAME_BYTES]; // Always null terminated.
287 char mBuffer[MAX_COMMAND_BYTES];
288 };
289
290 static int buffersAllocd(0);
291
292 // Get a new NativeCommandBuffer. Can only be called once between freeNativeBuffer calls,
293 // so that only one buffer exists at a time.
com_android_internal_os_ZygoteCommandBuffer_getNativeBuffer(JNIEnv * env,jclass,jint fd)294 jlong com_android_internal_os_ZygoteCommandBuffer_getNativeBuffer(JNIEnv* env, jclass, jint fd) {
295 CHECK(buffersAllocd == 0);
296 ++buffersAllocd;
297 // MMap explicitly to get it page aligned.
298 void *bufferMem = mmap(NULL, sizeof(NativeCommandBuffer), PROT_READ | PROT_WRITE,
299 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
300 // Currently we mmap and unmap one for every request handled by the Java code.
301 // That could be improved, but unclear it matters.
302 if (bufferMem == MAP_FAILED) {
303 ZygoteFailure(env, nullptr, nullptr, "Failed to map argument buffer");
304 }
305 return (jlong) new(bufferMem) NativeCommandBuffer(fd);
306 }
307
308 // Delete native command buffer.
com_android_internal_os_ZygoteCommandBuffer_freeNativeBuffer(JNIEnv * env,jclass,jlong j_buffer)309 void com_android_internal_os_ZygoteCommandBuffer_freeNativeBuffer(JNIEnv* env, jclass,
310 jlong j_buffer) {
311 CHECK(buffersAllocd == 1);
312 NativeCommandBuffer* n_buffer = reinterpret_cast<NativeCommandBuffer*>(j_buffer);
313 n_buffer->~NativeCommandBuffer();
314 if (munmap(n_buffer, sizeof(NativeCommandBuffer)) != 0) {
315 ZygoteFailure(env, nullptr, nullptr, "Failed to unmap argument buffer");
316 }
317 --buffersAllocd;
318 }
319
320 // Clear the buffer, read the line containing the count, and return the count.
com_android_internal_os_ZygoteCommandBuffer_nativeGetCount(JNIEnv * env,jclass,jlong j_buffer)321 jint com_android_internal_os_ZygoteCommandBuffer_nativeGetCount(JNIEnv* env, jclass,
322 jlong j_buffer) {
323 NativeCommandBuffer* n_buffer = reinterpret_cast<NativeCommandBuffer*>(j_buffer);
324 auto fail_fn = std::bind(ZygoteFailure, env, nullptr, nullptr, _1);
325 return n_buffer->getCount(fail_fn);
326 }
327
328 // Explicitly insert a string as the last line (argument) of the buffer.
com_android_internal_os_ZygoteCommandBuffer_insert(JNIEnv * env,jclass,jlong j_buffer,jstring line)329 void com_android_internal_os_ZygoteCommandBuffer_insert(JNIEnv* env, jclass, jlong j_buffer,
330 jstring line) {
331 NativeCommandBuffer* n_buffer = reinterpret_cast<NativeCommandBuffer*>(j_buffer);
332 size_t lineLen = static_cast<size_t>(env->GetStringUTFLength(line));
333 const char* cstring = env->GetStringUTFChars(line, NULL);
334 n_buffer->insert(cstring, lineLen);
335 env->ReleaseStringUTFChars(line, cstring);
336 }
337
338 // Read a line from the buffer, refilling as necessary.
com_android_internal_os_ZygoteCommandBuffer_nativeNextArg(JNIEnv * env,jclass,jlong j_buffer)339 jstring com_android_internal_os_ZygoteCommandBuffer_nativeNextArg(JNIEnv* env, jclass,
340 jlong j_buffer) {
341 NativeCommandBuffer* n_buffer = reinterpret_cast<NativeCommandBuffer*>(j_buffer);
342 auto fail_fn = std::bind(ZygoteFailure, env, n_buffer->niceNameAddr(), nullptr, _1);
343 auto line = n_buffer->readLine(fail_fn);
344 if (!line.has_value()) {
345 fail_fn("Incomplete zygote command");
346 }
347 auto [cresult, endp] = line.value();
348 // OK to temporarily clobber the buffer, since this is not thread safe, and we're modifying
349 // the buffer anyway.
350 *endp = '\0';
351 jstring result = env->NewStringUTF(cresult);
352 *endp = '\n';
353 return result;
354 }
355
356 // Read all lines from the current command into the buffer, and then reset the buffer, so
357 // we will start reading again at the beginning of the command, starting with the argument
358 // count. And we don't need access to the fd to do so.
com_android_internal_os_ZygoteCommandBuffer_nativeReadFullyAndReset(JNIEnv * env,jclass,jlong j_buffer)359 void com_android_internal_os_ZygoteCommandBuffer_nativeReadFullyAndReset(JNIEnv* env, jclass,
360 jlong j_buffer) {
361 NativeCommandBuffer* n_buffer = reinterpret_cast<NativeCommandBuffer*>(j_buffer);
362 auto fail_fn = std::bind(ZygoteFailure, env, n_buffer->niceNameAddr(), nullptr, _1);
363 n_buffer->readAllLines(fail_fn);
364 n_buffer->reset();
365 }
366
367 // Fork a child as specified by the current command buffer, and refill the command
368 // buffer from the given socket. So long as the result is another simple fork command,
369 // repeat this process.
370 // It must contain a fork command, which is currently restricted not to fork another
371 // zygote or involve a wrapper process.
372 // The initial buffer should be partially or entirely read; we read it fully and reset it.
373 // When we return, the buffer contains the command we couldn't handle, and has been reset().
374 // We return false in the parent when we see a command we didn't understand, and thus the
375 // command in the buffer still needs to be executed.
376 // We return true in each child.
377 // We only process fork commands if the peer uid matches expected_uid.
378 // For every fork command after the first, we check that the requested uid is at
379 // least minUid.
380 NO_STACK_PROTECTOR
com_android_internal_os_ZygoteCommandBuffer_nativeForkRepeatedly(JNIEnv * env,jclass,jlong j_buffer,jint zygote_socket_fd,jint expected_uid,jint minUid,jstring managed_nice_name)381 jboolean com_android_internal_os_ZygoteCommandBuffer_nativeForkRepeatedly(
382 JNIEnv* env,
383 jclass,
384 jlong j_buffer,
385 jint zygote_socket_fd,
386 jint expected_uid,
387 jint minUid,
388 jstring managed_nice_name) {
389
390 ALOGI("Entering forkRepeatedly native zygote loop");
391 NativeCommandBuffer* n_buffer = reinterpret_cast<NativeCommandBuffer*>(j_buffer);
392 int session_socket = n_buffer->getFd();
393 std::vector<int> session_socket_fds {session_socket};
394 auto fail_fn_1 = std::bind(ZygoteFailure, env, static_cast<const char*>(nullptr),
395 static_cast<jstring>(managed_nice_name), _1);
396 // This binds to the nice name address; the actual names are updated by isSimpleForkCommand:
397 auto fail_fn_n = std::bind(ZygoteFailure, env, n_buffer->niceNameAddr(),
398 static_cast<jstring>(nullptr), _1);
399 auto fail_fn_z = std::bind(ZygoteFailure, env, "zygote", nullptr, _1);
400
401 struct pollfd fd_structs[2];
402 static const int ZYGOTE_IDX = 0;
403 static const int SESSION_IDX = 1;
404 fd_structs[ZYGOTE_IDX].fd = zygote_socket_fd;
405 fd_structs[ZYGOTE_IDX].events = POLLIN;
406 fd_structs[SESSION_IDX].fd = session_socket;
407 fd_structs[SESSION_IDX].events = POLLIN;
408
409 struct timeval timeout;
410 socklen_t timeout_size = sizeof timeout;
411 if (getsockopt(session_socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, &timeout_size) != 0) {
412 fail_fn_z("Failed to retrieve session socket timeout");
413 }
414
415 struct ucred credentials;
416 socklen_t cred_size = sizeof credentials;
417 if (getsockopt(n_buffer->getFd(), SOL_SOCKET, SO_PEERCRED, &credentials, &cred_size) == -1
418 || cred_size != sizeof credentials) {
419 fail_fn_1(CREATE_ERROR("ForkRepeatedly failed to get initial credentials, %s",
420 strerror(errno)));
421 }
422
423 bool first_time = true;
424 do {
425 if (credentials.uid != expected_uid) {
426 return JNI_FALSE;
427 }
428 n_buffer->readAllLines(first_time ? fail_fn_1 : fail_fn_n);
429 n_buffer->reset();
430 int pid = zygote::forkApp(env, /* no pipe FDs */ -1, -1, session_socket_fds,
431 /*args_known=*/ true, /*is_priority_fork=*/ true,
432 /*purge=*/ first_time);
433 if (pid == 0) {
434 return JNI_TRUE;
435 }
436 // We're in the parent. Write big-endian pid, followed by a boolean.
437 char pid_buf[5];
438 int tmp_pid = pid;
439 for (int i = 3; i >= 0; --i) {
440 pid_buf[i] = tmp_pid & 0xff;
441 tmp_pid >>= 8;
442 }
443 pid_buf[4] = 0; // Process is not wrapped.
444 int res = TEMP_FAILURE_RETRY(write(session_socket, pid_buf, 5));
445 if (res != 5) {
446 if (res == -1) {
447 (first_time ? fail_fn_1 : fail_fn_n)
448 (CREATE_ERROR("Pid write error %d: %s", errno, strerror(errno)));
449 } else {
450 (first_time ? fail_fn_1 : fail_fn_n)
451 (CREATE_ERROR("Write unexpectedly returned short: %d < 5", res));
452 }
453 }
454 // Clear buffer and get count from next command.
455 n_buffer->clear();
456 for (;;) {
457 // Poll isn't strictly necessary for now. But without it, disconnect is hard to detect.
458 int poll_res = TEMP_FAILURE_RETRY(poll(fd_structs, 2, -1 /* infinite timeout */));
459 if ((fd_structs[SESSION_IDX].revents & POLLIN) != 0) {
460 if (n_buffer->getCount(fail_fn_z) != 0) {
461 break;
462 } // else disconnected;
463 } else if (poll_res == 0 || (fd_structs[ZYGOTE_IDX].revents & POLLIN) == 0) {
464 fail_fn_z(
465 CREATE_ERROR("Poll returned with no descriptors ready! Poll returned %d", poll_res));
466 }
467 // We've now seen either a disconnect or connect request.
468 close(session_socket);
469 int new_fd = TEMP_FAILURE_RETRY(accept(zygote_socket_fd, nullptr, nullptr));
470 if (new_fd == -1) {
471 fail_fn_z(CREATE_ERROR("Accept(%d) failed: %s", zygote_socket_fd, strerror(errno)));
472 }
473 if (new_fd != session_socket) {
474 // Move new_fd back to the old value, so that we don't have to change Java-level data
475 // structures to reflect a change. This implicitly closes the old one.
476 if (TEMP_FAILURE_RETRY(dup2(new_fd, session_socket)) != session_socket) {
477 fail_fn_z(CREATE_ERROR("Failed to move fd %d to %d: %s",
478 new_fd, session_socket, strerror(errno)));
479 }
480 close(new_fd); // On Linux, fd is closed even if EINTR is returned.
481 }
482 // If we ever return, we effectively reuse the old Java ZygoteConnection.
483 // None of its state needs to change.
484 if (setsockopt(session_socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, timeout_size) != 0) {
485 fail_fn_z(CREATE_ERROR("Failed to set receive timeout for socket %d: %s",
486 session_socket, strerror(errno)));
487 }
488 if (setsockopt(session_socket, SOL_SOCKET, SO_SNDTIMEO, &timeout, timeout_size) != 0) {
489 fail_fn_z(CREATE_ERROR("Failed to set send timeout for socket %d: %s",
490 session_socket, strerror(errno)));
491 }
492 if (getsockopt(session_socket, SOL_SOCKET, SO_PEERCRED, &credentials, &cred_size) == -1) {
493 fail_fn_z(CREATE_ERROR("ForkMany failed to get credentials: %s", strerror(errno)));
494 }
495 if (cred_size != sizeof credentials) {
496 fail_fn_z(CREATE_ERROR("ForkMany credential size = %d, should be %d",
497 cred_size, static_cast<int>(sizeof credentials)));
498 }
499 }
500 first_time = false;
501 } while (n_buffer->isSimpleForkCommand(minUid, fail_fn_n));
502 ALOGW("forkRepeatedly terminated due to non-simple command");
503 n_buffer->logState();
504 n_buffer->reset();
505 return JNI_FALSE;
506 }
507
508 #define METHOD_NAME(m) com_android_internal_os_ZygoteCommandBuffer_ ## m
509
510 static const JNINativeMethod gMethods[] = {
511 {"getNativeBuffer", "(I)J", (void *) METHOD_NAME(getNativeBuffer)},
512 {"freeNativeBuffer", "(J)V", (void *) METHOD_NAME(freeNativeBuffer)},
513 {"insert", "(JLjava/lang/String;)V", (void *) METHOD_NAME(insert)},
514 {"nativeNextArg", "(J)Ljava/lang/String;", (void *) METHOD_NAME(nativeNextArg)},
515 {"nativeReadFullyAndReset", "(J)V", (void *) METHOD_NAME(nativeReadFullyAndReset)},
516 {"nativeGetCount", "(J)I", (void *) METHOD_NAME(nativeGetCount)},
517 {"nativeForkRepeatedly", "(JIIILjava/lang/String;)Z",
518 (void *) METHOD_NAME(nativeForkRepeatedly)},
519 };
520
register_com_android_internal_os_ZygoteCommandBuffer(JNIEnv * env)521 int register_com_android_internal_os_ZygoteCommandBuffer(JNIEnv* env) {
522 return RegisterMethodsOrDie(env, "com/android/internal/os/ZygoteCommandBuffer", gMethods,
523 NELEM(gMethods));
524 }
525
526 } // namespace android
527