188 lines
6.9 KiB
Java
188 lines
6.9 KiB
Java
/*
|
|
* 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);
|
|
|
|
}
|