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 package com.android.internal.os; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.net.LocalSocket; 22 23 import java.io.FileDescriptor; 24 import java.lang.ref.Reference; // For reachabilityFence. 25 26 /** 27 * A native-accessible buffer for Zygote commands. Designed to support repeated forking 28 * of applications without intervening memory allocation, thus keeping zygote memory 29 * as stable as possible. 30 * A ZygoteCommandBuffer may have an associated socket from which it can be refilled. 31 * Otherwise the contents are explicitly set by getInstance(). 32 * 33 * NOT THREAD-SAFE. No methods may be called concurrently from multiple threads. 34 * 35 * Only one ZygoteCommandBuffer can exist at a time. 36 * Must be explicitly closed before being dropped. 37 * @hide 38 */ 39 class ZygoteCommandBuffer implements AutoCloseable { 40 private long mNativeBuffer; // Not final so that we can clear it in close(). 41 42 /** 43 * The command socket. 44 * 45 * mSocket is retained in the child process in "peer wait" mode, so 46 * that it closes when the child process terminates. In other cases, 47 * it is closed in the peer. 48 */ 49 private final LocalSocket mSocket; 50 private final int mNativeSocket; 51 52 /** 53 * Constructs instance from file descriptor from which the command will be read. 54 * Only a single instance may be live in a given process. The native code checks. 55 * 56 * @param fd file descriptor to read from. The setCommand() method may be used if and only if 57 * fd is null. 58 */ ZygoteCommandBuffer(@ullable LocalSocket socket)59 ZygoteCommandBuffer(@Nullable LocalSocket socket) { 60 mSocket = socket; 61 if (socket == null) { 62 mNativeSocket = -1; 63 } else { 64 mNativeSocket = mSocket.getFileDescriptor().getInt$(); 65 } 66 mNativeBuffer = getNativeBuffer(mNativeSocket); 67 } 68 69 /** 70 * Constructs an instance with explicitly supplied arguments and an invalid 71 * file descriptor. Can only be used for a single command. 72 */ ZygoteCommandBuffer(@onNull String[] args)73 ZygoteCommandBuffer(@NonNull String[] args) { 74 this((LocalSocket) null); 75 setCommand(args); 76 } 77 78 getNativeBuffer(int fd)79 private static native long getNativeBuffer(int fd); 80 81 /** 82 * Deallocate native resources associated with the one and only command buffer, and prevent 83 * reuse. Subsequent calls to getInstance() will yield a new buffer. 84 * We do not close the associated socket, if any. 85 */ 86 @Override close()87 public void close() { 88 freeNativeBuffer(mNativeBuffer); 89 mNativeBuffer = 0; 90 } 91 freeNativeBuffer(long nbuffer)92 private static native void freeNativeBuffer(long /* NativeCommandBuffer* */ nbuffer); 93 94 /** 95 * Read at least the first line of the next command into the buffer, return the argument count 96 * from that line. Assumes we are initially positioned at the beginning of the first line of 97 * the command. Leave the buffer positioned at the beginning of the second command line, i.e. 98 * the first argument. If the buffer has no associated file descriptor, we just reposition to 99 * the beginning of the buffer, and reread existing contents. Returns zero if we started out 100 * at EOF. 101 */ getCount()102 int getCount() { 103 try { 104 return nativeGetCount(mNativeBuffer); 105 } finally { 106 // Make sure the mNativeSocket doesn't get closed due to early finalization. 107 Reference.reachabilityFence(mSocket); 108 } 109 } 110 nativeGetCount(long nbuffer)111 private static native int nativeGetCount(long /* NativeCommandBuffer* */ nbuffer); 112 113 114 /* 115 * Set the buffer to contain the supplied sequence of arguments. 116 */ setCommand(String[] command)117 private void setCommand(String[] command) { 118 int nArgs = command.length; 119 insert(mNativeBuffer, Integer.toString(nArgs)); 120 for (String s: command) { 121 insert(mNativeBuffer, s); 122 } 123 // Native code checks there is no socket; hence no reachabilityFence. 124 } 125 insert(long nbuffer, String s)126 private static native void insert(long /* NativeCommandBuffer* */ nbuffer, String s); 127 128 /** 129 * Retrieve the next argument/line from the buffer, filling the buffer as necessary. 130 */ nextArg()131 String nextArg() { 132 try { 133 return nativeNextArg(mNativeBuffer); 134 } finally { 135 Reference.reachabilityFence(mSocket); 136 } 137 } 138 nativeNextArg(long nbuffer)139 private static native String nativeNextArg(long /* NativeCommandBuffer* */ nbuffer); 140 readFullyAndReset()141 void readFullyAndReset() { 142 try { 143 nativeReadFullyAndReset(mNativeBuffer); 144 } finally { 145 Reference.reachabilityFence(mSocket); 146 } 147 } 148 nativeReadFullyAndReset(long nbuffer)149 private static native void nativeReadFullyAndReset(long /* NativeCommandBuffer* */ nbuffer); 150 151 /** 152 * Fork a child as specified by the current command in the buffer, and repeat this process 153 * after refilling the buffer, so long as the buffer clearly contains another fork command. 154 * 155 * @param zygoteSocket socket from which to obtain new connections when current one is 156 * disconnected 157 * @param expectedUid Peer UID for current connection. We refuse to deal with requests from 158 * a different UID. 159 * @param minUid the smallest uid that may be request for the child process. 160 * @param firstNiceName The name for the initial process to be forked. Used only for error 161 * reporting. 162 * 163 * @return true in the child, false in the parent. In the parent case, the buffer is positioned 164 * at the beginning of a command that still needs to be processed. 165 */ forkRepeatedly(FileDescriptor zygoteSocket, int expectedUid, int minUid, String firstNiceName)166 boolean forkRepeatedly(FileDescriptor zygoteSocket, int expectedUid, int minUid, 167 String firstNiceName) { 168 try { 169 return nativeForkRepeatedly(mNativeBuffer, zygoteSocket.getInt$(), 170 expectedUid, minUid, firstNiceName); 171 } finally { 172 Reference.reachabilityFence(mSocket); 173 Reference.reachabilityFence(zygoteSocket); 174 } 175 } 176 177 /* 178 * Repeatedly fork children as above. It commonly does not return in the parent, but it may. 179 * @return true in the child, false in the parent if we encounter a command we couldn't handle. 180 */ nativeForkRepeatedly(long nbuffer, int zygoteSocketRawFd, int expectedUid, int minUid, String firstNiceName)181 private static native boolean nativeForkRepeatedly(long /* NativeCommandBuffer* */ nbuffer, 182 int zygoteSocketRawFd, 183 int expectedUid, 184 int minUid, 185 String firstNiceName); 186 187 } 188