| /* |
| * Copyright (C) 2020 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 android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.net.LocalSocket; |
| |
| import java.io.FileDescriptor; |
| import java.lang.ref.Reference; // For reachabilityFence. |
| |
| /** |
| * A native-accessible buffer for Zygote commands. Designed to support repeated forking |
| * of applications without intervening memory allocation, thus keeping zygote memory |
| * as stable as possible. |
| * A ZygoteCommandBuffer may have an associated socket from which it can be refilled. |
| * Otherwise the contents are explicitly set by getInstance(). |
| * |
| * NOT THREAD-SAFE. No methods may be called concurrently from multiple threads. |
| * |
| * Only one ZygoteCommandBuffer can exist at a time. |
| * Must be explicitly closed before being dropped. |
| * @hide |
| */ |
| class ZygoteCommandBuffer implements AutoCloseable { |
| private long mNativeBuffer; // Not final so that we can clear it in close(). |
| |
| /** |
| * The command socket. |
| * |
| * mSocket is retained in the child process in "peer wait" mode, so |
| * that it closes when the child process terminates. In other cases, |
| * it is closed in the peer. |
| */ |
| private final LocalSocket mSocket; |
| private final int mNativeSocket; |
| |
| /** |
| * Constructs instance from file descriptor from which the command will be read. |
| * Only a single instance may be live in a given process. The native code checks. |
| * |
| * @param fd file descriptor to read from. The setCommand() method may be used if and only if |
| * fd is null. |
| */ |
| ZygoteCommandBuffer(@Nullable LocalSocket socket) { |
| mSocket = socket; |
| if (socket == null) { |
| mNativeSocket = -1; |
| } else { |
| mNativeSocket = mSocket.getFileDescriptor().getInt$(); |
| } |
| mNativeBuffer = getNativeBuffer(mNativeSocket); |
| } |
| |
| /** |
| * Constructs an instance with explicitly supplied arguments and an invalid |
| * file descriptor. Can only be used for a single command. |
| */ |
| ZygoteCommandBuffer(@NonNull String[] args) { |
| this((LocalSocket) null); |
| setCommand(args); |
| } |
| |
| |
| private static native long getNativeBuffer(int fd); |
| |
| /** |
| * Deallocate native resources associated with the one and only command buffer, and prevent |
| * reuse. Subsequent calls to getInstance() will yield a new buffer. |
| * We do not close the associated socket, if any. |
| */ |
| @Override |
| public void close() { |
| freeNativeBuffer(mNativeBuffer); |
| mNativeBuffer = 0; |
| } |
| |
| private static native void freeNativeBuffer(long /* NativeCommandBuffer* */ nbuffer); |
| |
| /** |
| * Read at least the first line of the next command into the buffer, return the argument count |
| * from that line. Assumes we are initially positioned at the beginning of the first line of |
| * the command. Leave the buffer positioned at the beginning of the second command line, i.e. |
| * the first argument. If the buffer has no associated file descriptor, we just reposition to |
| * the beginning of the buffer, and reread existing contents. Returns zero if we started out |
| * at EOF. |
| */ |
| int getCount() { |
| try { |
| return nativeGetCount(mNativeBuffer); |
| } finally { |
| // Make sure the mNativeSocket doesn't get closed due to early finalization. |
| Reference.reachabilityFence(mSocket); |
| } |
| } |
| |
| private static native int nativeGetCount(long /* NativeCommandBuffer* */ nbuffer); |
| |
| |
| /* |
| * Set the buffer to contain the supplied sequence of arguments. |
| */ |
| private void setCommand(String[] command) { |
| int nArgs = command.length; |
| insert(mNativeBuffer, Integer.toString(nArgs)); |
| for (String s: command) { |
| insert(mNativeBuffer, s); |
| } |
| // Native code checks there is no socket; hence no reachabilityFence. |
| } |
| |
| private static native void insert(long /* NativeCommandBuffer* */ nbuffer, String s); |
| |
| /** |
| * Retrieve the next argument/line from the buffer, filling the buffer as necessary. |
| */ |
| String nextArg() { |
| try { |
| return nativeNextArg(mNativeBuffer); |
| } finally { |
| Reference.reachabilityFence(mSocket); |
| } |
| } |
| |
| private static native String nativeNextArg(long /* NativeCommandBuffer* */ nbuffer); |
| |
| void readFullyAndReset() { |
| try { |
| nativeReadFullyAndReset(mNativeBuffer); |
| } finally { |
| Reference.reachabilityFence(mSocket); |
| } |
| } |
| |
| private static native void nativeReadFullyAndReset(long /* NativeCommandBuffer* */ nbuffer); |
| |
| /** |
| * Fork a child as specified by the current command in the buffer, and repeat this process |
| * after refilling the buffer, so long as the buffer clearly contains another fork command. |
| * |
| * @param zygoteSocket socket from which to obtain new connections when current one is |
| * disconnected |
| * @param expectedUid Peer UID for current connection. We refuse to deal with requests from |
| * a different UID. |
| * @param minUid the smallest uid that may be request for the child process. |
| * @param firstNiceName The name for the initial process to be forked. Used only for error |
| * reporting. |
| * |
| * @return true in the child, false in the parent. In the parent case, the buffer is positioned |
| * at the beginning of a command that still needs to be processed. |
| */ |
| boolean forkRepeatedly(FileDescriptor zygoteSocket, int expectedUid, int minUid, |
| String firstNiceName) { |
| try { |
| return nativeForkRepeatedly(mNativeBuffer, zygoteSocket.getInt$(), |
| expectedUid, minUid, firstNiceName); |
| } finally { |
| Reference.reachabilityFence(mSocket); |
| Reference.reachabilityFence(zygoteSocket); |
| } |
| } |
| |
| /* |
| * Repeatedly fork children as above. It commonly does not return in the parent, but it may. |
| * @return true in the child, false in the parent if we encounter a command we couldn't handle. |
| */ |
| private static native boolean nativeForkRepeatedly(long /* NativeCommandBuffer* */ nbuffer, |
| int zygoteSocketRawFd, |
| int expectedUid, |
| int minUid, |
| String firstNiceName); |
| |
| } |