• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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