/
*
*
*
Specialize the current process into one described by argBuffer
or
the command read
from
*
usapPoolSocket. Exactly one of those must be null. If we are given an argBuffer, we close
*
it. Used both
for
a specializing a USAP process,
and
for
process creation without USAPs.
*
In both cases, we specialize the process after first returning to Java code.
*
*
@param writePipe The write end of the reporting pipe used to communicate with the poll loop
*
of the ZygoteServer.
*
@
return
A runnable oject representing the new application.
*
/
private static Runnable childMain(@Nullable ZygoteCommandBuffer argBuffer,
@Nullable
LocalServerSocket usapPoolSocket,
FileDescriptor writePipe) {
final
int
pid
=
Process.myPid();
DataOutputStream usapOutputStream
=
null;
ZygoteArguments args
=
null;
LocalSocket sessionSocket
=
null;
if
(argBuffer
=
=
null) {
/
/
Read arguments
from
usapPoolSocket instead.
Process.setArgV0(Process.is64Bit() ?
"usap64"
:
"usap32"
);
/
/
Change the priority to
max
before calling accept so we can respond to new
/
/
specialization requests as quickly as possible. This will be reverted to the
/
/
default priority
in
the native specialization code.
boostUsapPriority();
while
(true) {
ZygoteCommandBuffer tmpArgBuffer
=
null;
try
{
sessionSocket
=
usapPoolSocket.accept();
/
/
Block SIGTERM so we won't be killed
if
the Zygote flushes the USAP pool.
/
/
This
is
safe
from
a race condition because the pool
is
only flushed after
/
/
the SystemServer changes its internal state to stop using the USAP pool.
blockSigTerm();
usapOutputStream
=
new DataOutputStream(sessionSocket.getOutputStream());
Credentials peerCredentials
=
sessionSocket.getPeerCredentials();
tmpArgBuffer
=
new ZygoteCommandBuffer(sessionSocket);
args
=
ZygoteArguments.getInstance(tmpArgBuffer);
applyUidSecurityPolicy(args, peerCredentials);
/
/
TODO (chriswailes): Should this only be run
for
debug builds?
validateUsapCommand(args);
break
;
} catch (Exception ex) {
Log.e(
"USAP"
, ex.getMessage());
}
/
/
Re
-
enable SIGTERM so the USAP can be flushed
from
the pool
if
necessary.
unblockSigTerm();
IoUtils.closeQuietly(sessionSocket);
IoUtils.closeQuietly(tmpArgBuffer);
}
}
else
{
/
/
Block SIGTERM so we won't be killed
if
the Zygote flushes the USAP pool.
blockSigTerm();
try
{
args
=
ZygoteArguments.getInstance(argBuffer);
} catch (Exception ex) {
Log.e(
"AppStartup"
, ex.getMessage());
throw new AssertionError(
"Failed to parse application start command"
, ex);
}
/
/
peerCredentials were checked
in
parent.
}
if
(args
=
=
null) {
throw new AssertionError(
"Empty command line"
);
}
try
{
/
/
SIGTERM
is
blocked here. This prevents a USAP that
is
specializing
from
being
/
/
killed during a pool flush.
applyDebuggerSystemProperty(args);
int
[][] rlimits
=
null;
if
(args.mRLimits !
=
null) {
rlimits
=
args.mRLimits.toArray(INT_ARRAY_2D);
}
if
(argBuffer
=
=
null) {
/
/
This must happen before the SELinux policy
for
this process
is
/
/
changed when specializing.
try
{
/
/
Used by ZygoteProcess.zygoteSendArgsAndGetResult to fill
in
a
/
/
Process.ProcessStartResult
object
.
usapOutputStream.writeInt(pid);
} catch (IOException ioEx) {
Log.e(
"USAP"
,
"Failed to write response to session socket: "
+
ioEx.getMessage());
throw new RuntimeException(ioEx);
}
finally
{
try
{
/
/
Since the raw FD
is
created by init
and
then loaded
from
an environment
/
/
variable (as opposed to being created by the LocalSocketImpl itself),
/
/
the LocalSocket
/
LocalSocketImpl does
not
own the Os
-
level socket. See
/
/
the spec
for
LocalSocket.createConnectedLocalSocket(FileDescriptor fd).
/
/
Thus closing the LocalSocket does
not
suffice. See b
/
130309968
for
more
/
/
discussion.
FileDescriptor fd
=
usapPoolSocket.getFileDescriptor();
usapPoolSocket.close();
Os.close(fd);
} catch (ErrnoException | IOException ex) {
Log.e(
"USAP"
,
"Failed to close USAP pool socket"
);
throw new RuntimeException(ex);
}
}
}
if
(writePipe !
=
null) {
try
{
ByteArrayOutputStream
buffer
=
new ByteArrayOutputStream(Zygote.USAP_MANAGEMENT_MESSAGE_BYTES);
DataOutputStream outputStream
=
new DataOutputStream(
buffer
);
/
/
This
is
written as a
long
so that the USAP reporting pipe
and
USAP pool
/
/
event FD handlers
in
ZygoteServer.runSelectLoop can be unified. These two
/
/
cases should both send
/
receive
8
bytes.
/
/
TODO: Needs tweaking to handle the non
-
Usap invoke
-
with case, which expects
/
/
a different
format
.
outputStream.writeLong(pid);
outputStream.flush();
Os.write(writePipe,
buffer
.toByteArray(),
0
,
buffer
.size());
} catch (Exception ex) {
Log.e(
"USAP"
,
String.
format
(
"Failed to write PID (%d) to pipe (%d): %s"
,
pid, writePipe.getInt$(), ex.getMessage()));
throw new RuntimeException(ex);
}
finally
{
IoUtils.closeQuietly(writePipe);
}
}
specializeAppProcess(args.mUid, args.mGid, args.mGids,
args.mRuntimeFlags, rlimits, args.mMountExternal,
args.mSeInfo, args.mNiceName, args.mStartChildZygote,
args.mInstructionSet, args.mAppDataDir, args.mIsTopApp,
args.mPkgDataInfoList, args.mAllowlistedDataInfoList,
args.mBindMountAppDataDirs, args.mBindMountAppStorageDirs);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
return
ZygoteInit.zygoteInit(args.mTargetSdkVersion,
args.mDisabledCompatChanges,
args.mRemainingArgs,
null
/
*
classLoader
*
/
);
}
finally
{
/
/
Unblock SIGTERM to restore the process to default behavior.
unblockSigTerm();
}
}