1 /*
2  * Copyright (C) 2007 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 static android.system.OsConstants.F_SETFD;
20 import static android.system.OsConstants.O_CLOEXEC;
21 import static android.system.OsConstants.POLLIN;
22 
23 import static com.android.internal.os.ZygoteConnectionConstants.CONNECTION_TIMEOUT_MILLIS;
24 import static com.android.internal.os.ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS;
25 
26 import android.compat.annotation.UnsupportedAppUsage;
27 import android.content.pm.ApplicationInfo;
28 import android.net.Credentials;
29 import android.net.LocalSocket;
30 import android.os.Parcel;
31 import android.os.Process;
32 import android.os.Trace;
33 import android.system.ErrnoException;
34 import android.system.Os;
35 import android.system.StructPollfd;
36 import android.util.Log;
37 
38 import dalvik.system.VMRuntime;
39 import dalvik.system.ZygoteHooks;
40 
41 import libcore.io.IoUtils;
42 
43 import java.io.ByteArrayInputStream;
44 import java.io.DataInputStream;
45 import java.io.DataOutputStream;
46 import java.io.FileDescriptor;
47 import java.io.IOException;
48 import java.nio.charset.StandardCharsets;
49 import java.util.Base64;
50 import java.util.concurrent.TimeUnit;
51 
52 /**
53  * A connection that can make spawn requests.
54  */
55 class ZygoteConnection {
56     private static final String TAG = "Zygote";
57 
58     /**
59      * The command socket.
60      *
61      * mSocket is retained in the child process in "peer wait" mode, so
62      * that it closes when the child process terminates. In other cases,
63      * it is closed in the peer.
64      */
65     @UnsupportedAppUsage
66     private final LocalSocket mSocket;
67     @UnsupportedAppUsage
68     private final DataOutputStream mSocketOutStream;
69     @UnsupportedAppUsage
70     private final Credentials peer;
71     private final String abiList;
72     private boolean isEof;
73 
74     /**
75      * Constructs instance from connected socket.
76      *
77      * @param socket non-null; connected socket
78      * @param abiList non-null; a list of ABIs this zygote supports.
79      * @throws IOException If obtaining the peer credentials fails
80      */
ZygoteConnection(LocalSocket socket, String abiList)81     ZygoteConnection(LocalSocket socket, String abiList) throws IOException {
82         mSocket = socket;
83         this.abiList = abiList;
84 
85         mSocketOutStream = new DataOutputStream(socket.getOutputStream());
86 
87         mSocket.setSoTimeout(CONNECTION_TIMEOUT_MILLIS);
88 
89         try {
90             peer = mSocket.getPeerCredentials();
91         } catch (IOException ex) {
92             Log.e(TAG, "Cannot read peer credentials", ex);
93             throw ex;
94         }
95 
96         isEof = false;
97     }
98 
99     /**
100      * Returns the file descriptor of the associated socket.
101      *
102      * @return null-ok; file descriptor
103      */
getFileDescriptor()104     FileDescriptor getFileDescriptor() {
105         return mSocket.getFileDescriptor();
106     }
107 
108     /**
109      * Reads a command from the command socket. If a child is successfully forked, a
110      * {@code Runnable} that calls the childs main method (or equivalent) is returned in the child
111      * process. {@code null} is always returned in the parent process (the zygote).
112      * If multipleOK is set, we may keep processing additional fork commands before returning.
113      *
114      * If the client closes the socket, an {@code EOF} condition is set, which callers can test
115      * for by calling {@code ZygoteConnection.isClosedByPeer}.
116      */
processCommand(ZygoteServer zygoteServer, boolean multipleOK)117     Runnable processCommand(ZygoteServer zygoteServer, boolean multipleOK) {
118         ZygoteArguments parsedArgs;
119 
120         try (ZygoteCommandBuffer argBuffer = new ZygoteCommandBuffer(mSocket)) {
121             while (true) {
122                 try {
123                     parsedArgs = ZygoteArguments.getInstance(argBuffer);
124                     // Keep argBuffer around, since we need it to fork.
125                 } catch (IOException ex) {
126                     throw new IllegalStateException("IOException on command socket", ex);
127                 }
128                 if (parsedArgs == null) {
129                     isEof = true;
130                     return null;
131                 }
132 
133                 int pid;
134                 FileDescriptor childPipeFd = null;
135                 FileDescriptor serverPipeFd = null;
136 
137                 if (parsedArgs.mBootCompleted) {
138                     handleBootCompleted();
139                     return null;
140                 }
141 
142                 if (parsedArgs.mAbiListQuery) {
143                     handleAbiListQuery();
144                     return null;
145                 }
146 
147                 if (parsedArgs.mPidQuery) {
148                     handlePidQuery();
149                     return null;
150                 }
151 
152                 if (parsedArgs.mUsapPoolStatusSpecified
153                         || parsedArgs.mApiDenylistExemptions != null
154                         || parsedArgs.mHiddenApiAccessLogSampleRate != -1
155                         || parsedArgs.mHiddenApiAccessStatslogSampleRate != -1) {
156                     // Handle these once we've released argBuffer, to avoid opening a second one.
157                     break;
158                 }
159 
160                 if (parsedArgs.mPreloadDefault) {
161                     handlePreload();
162                     return null;
163                 }
164 
165                 if (parsedArgs.mPreloadPackage != null) {
166                     handlePreloadPackage(parsedArgs.mPreloadPackage,
167                             parsedArgs.mPreloadPackageLibs,
168                             parsedArgs.mPreloadPackageLibFileName,
169                             parsedArgs.mPreloadPackageCacheKey);
170                     return null;
171                 }
172 
173                 if (canPreloadApp() && parsedArgs.mPreloadApp != null) {
174                     byte[] rawParcelData = Base64.getDecoder().decode(parsedArgs.mPreloadApp);
175                     Parcel appInfoParcel = Parcel.obtain();
176                     appInfoParcel.unmarshall(rawParcelData, 0, rawParcelData.length);
177                     appInfoParcel.setDataPosition(0);
178                     ApplicationInfo appInfo =
179                             ApplicationInfo.CREATOR.createFromParcel(appInfoParcel);
180                     appInfoParcel.recycle();
181                     if (appInfo != null) {
182                         handlePreloadApp(appInfo);
183                     } else {
184                         throw new IllegalArgumentException("Failed to deserialize --preload-app");
185                     }
186                     return null;
187                 }
188 
189                 if (parsedArgs.mPermittedCapabilities != 0
190                         || parsedArgs.mEffectiveCapabilities != 0) {
191                     throw new ZygoteSecurityException("Client may not specify capabilities: "
192                             + "permitted=0x" + Long.toHexString(parsedArgs.mPermittedCapabilities)
193                             + ", effective=0x"
194                             + Long.toHexString(parsedArgs.mEffectiveCapabilities));
195                 }
196 
197                 Zygote.applyUidSecurityPolicy(parsedArgs, peer);
198                 Zygote.applyInvokeWithSecurityPolicy(parsedArgs, peer);
199 
200                 Zygote.applyDebuggerSystemProperty(parsedArgs);
201                 Zygote.applyInvokeWithSystemProperty(parsedArgs);
202 
203                 int[][] rlimits = null;
204 
205                 if (parsedArgs.mRLimits != null) {
206                     rlimits = parsedArgs.mRLimits.toArray(Zygote.INT_ARRAY_2D);
207                 }
208 
209                 int[] fdsToIgnore = null;
210 
211                 if (parsedArgs.mInvokeWith != null) {
212                     try {
213                         FileDescriptor[] pipeFds = Os.pipe2(O_CLOEXEC);
214                         childPipeFd = pipeFds[1];
215                         serverPipeFd = pipeFds[0];
216                         Os.fcntlInt(childPipeFd, F_SETFD, 0);
217                         fdsToIgnore = new int[]{childPipeFd.getInt$(), serverPipeFd.getInt$()};
218                     } catch (ErrnoException errnoEx) {
219                         throw new IllegalStateException("Unable to set up pipe for invoke-with",
220                                 errnoEx);
221                     }
222                 }
223 
224                 /*
225                  * In order to avoid leaking descriptors to the Zygote child,
226                  * the native code must close the two Zygote socket descriptors
227                  * in the child process before it switches from Zygote-root to
228                  * the UID and privileges of the application being launched.
229                  *
230                  * In order to avoid "bad file descriptor" errors when the
231                  * two LocalSocket objects are closed, the Posix file
232                  * descriptors are released via a dup2() call which closes
233                  * the socket and substitutes an open descriptor to /dev/null.
234                  */
235 
236                 int [] fdsToClose = { -1, -1 };
237 
238                 FileDescriptor fd = mSocket.getFileDescriptor();
239 
240                 if (fd != null) {
241                     fdsToClose[0] = fd.getInt$();
242                 }
243 
244                 FileDescriptor zygoteFd = zygoteServer.getZygoteSocketFileDescriptor();
245 
246                 if (zygoteFd != null) {
247                     fdsToClose[1] = zygoteFd.getInt$();
248                 }
249 
250                 if (parsedArgs.mInvokeWith != null || parsedArgs.mStartChildZygote
251                         || !multipleOK || peer.getUid() != Process.SYSTEM_UID) {
252                     // Continue using old code for now. TODO: Handle these cases in the other path.
253                     pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid,
254                             parsedArgs.mGids, parsedArgs.mRuntimeFlags, rlimits,
255                             parsedArgs.mMountExternal, parsedArgs.mSeInfo, parsedArgs.mNiceName,
256                             fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
257                             parsedArgs.mInstructionSet, parsedArgs.mAppDataDir,
258                             parsedArgs.mIsTopApp, parsedArgs.mPkgDataInfoList,
259                             parsedArgs.mAllowlistedDataInfoList, parsedArgs.mBindMountAppDataDirs,
260                             parsedArgs.mBindMountAppStorageDirs);
261 
262                     try {
263                         if (pid == 0) {
264                             // in child
265                             zygoteServer.setForkChild();
266 
267                             zygoteServer.closeServerSocket();
268                             IoUtils.closeQuietly(serverPipeFd);
269                             serverPipeFd = null;
270 
271                             return handleChildProc(parsedArgs, childPipeFd,
272                                     parsedArgs.mStartChildZygote);
273                         } else {
274                             // In the parent. A pid < 0 indicates a failure and will be handled in
275                             // handleParentProc.
276                             IoUtils.closeQuietly(childPipeFd);
277                             childPipeFd = null;
278                             handleParentProc(pid, serverPipeFd);
279                             return null;
280                         }
281                     } finally {
282                         IoUtils.closeQuietly(childPipeFd);
283                         IoUtils.closeQuietly(serverPipeFd);
284                     }
285                 } else {
286                     ZygoteHooks.preFork();
287                     Runnable result = Zygote.forkSimpleApps(argBuffer,
288                             zygoteServer.getZygoteSocketFileDescriptor(),
289                             peer.getUid(), Zygote.minChildUid(peer), parsedArgs.mNiceName);
290                     if (result == null) {
291                         // parent; we finished some number of forks. Result is Boolean.
292                         // We already did the equivalent of handleParentProc().
293                         ZygoteHooks.postForkCommon();
294                         // argBuffer contains a command not understood by forksimpleApps.
295                         continue;
296                     } else {
297                         // child; result is a Runnable.
298                         zygoteServer.setForkChild();
299                         Zygote.setAppProcessName(parsedArgs, TAG);  // ??? Necessary?
300                         return result;
301                     }
302                 }
303             }
304         }
305         // Handle anything that may need a ZygoteCommandBuffer after we've released ours.
306         if (parsedArgs.mUsapPoolStatusSpecified) {
307             return handleUsapPoolStatusChange(zygoteServer, parsedArgs.mUsapPoolEnabled);
308         }
309         if (parsedArgs.mApiDenylistExemptions != null) {
310             return handleApiDenylistExemptions(zygoteServer,
311                     parsedArgs.mApiDenylistExemptions);
312         }
313         if (parsedArgs.mHiddenApiAccessLogSampleRate != -1
314                 || parsedArgs.mHiddenApiAccessStatslogSampleRate != -1) {
315             return handleHiddenApiAccessLogSampleRate(zygoteServer,
316                     parsedArgs.mHiddenApiAccessLogSampleRate,
317                     parsedArgs.mHiddenApiAccessStatslogSampleRate);
318         }
319         throw new AssertionError("Shouldn't get here");
320     }
321 
handleAbiListQuery()322     private void handleAbiListQuery() {
323         try {
324             final byte[] abiListBytes = abiList.getBytes(StandardCharsets.US_ASCII);
325             mSocketOutStream.writeInt(abiListBytes.length);
326             mSocketOutStream.write(abiListBytes);
327         } catch (IOException ioe) {
328             throw new IllegalStateException("Error writing to command socket", ioe);
329         }
330     }
331 
handlePidQuery()332     private void handlePidQuery() {
333         try {
334             String pidString = String.valueOf(Process.myPid());
335             final byte[] pidStringBytes = pidString.getBytes(StandardCharsets.US_ASCII);
336             mSocketOutStream.writeInt(pidStringBytes.length);
337             mSocketOutStream.write(pidStringBytes);
338         } catch (IOException ioe) {
339             throw new IllegalStateException("Error writing to command socket", ioe);
340         }
341     }
342 
handleBootCompleted()343     private void handleBootCompleted() {
344         try {
345             mSocketOutStream.writeInt(0);
346         } catch (IOException ioe) {
347             throw new IllegalStateException("Error writing to command socket", ioe);
348         }
349 
350         VMRuntime.bootCompleted();
351     }
352 
353     /**
354      * Preloads resources if the zygote is in lazily preload mode. Writes the result of the
355      * preload operation; {@code 0} when a preload was initiated due to this request and {@code 1}
356      * if no preload was initiated. The latter implies that the zygote is not configured to load
357      * resources lazy or that the zygote has already handled a previous request to handlePreload.
358      */
handlePreload()359     private void handlePreload() {
360         try {
361             if (isPreloadComplete()) {
362                 mSocketOutStream.writeInt(1);
363             } else {
364                 preload();
365                 mSocketOutStream.writeInt(0);
366             }
367         } catch (IOException ioe) {
368             throw new IllegalStateException("Error writing to command socket", ioe);
369         }
370     }
371 
stateChangeWithUsapPoolReset(ZygoteServer zygoteServer, Runnable stateChangeCode)372     private Runnable stateChangeWithUsapPoolReset(ZygoteServer zygoteServer,
373             Runnable stateChangeCode) {
374         try {
375             if (zygoteServer.isUsapPoolEnabled()) {
376                 Log.i(TAG, "Emptying USAP Pool due to state change.");
377                 Zygote.emptyUsapPool();
378             }
379 
380             stateChangeCode.run();
381 
382             if (zygoteServer.isUsapPoolEnabled()) {
383                 Runnable fpResult =
384                         zygoteServer.fillUsapPool(
385                                 new int[]{mSocket.getFileDescriptor().getInt$()}, false);
386 
387                 if (fpResult != null) {
388                     zygoteServer.setForkChild();
389                     return fpResult;
390                 } else {
391                     Log.i(TAG, "Finished refilling USAP Pool after state change.");
392                 }
393             }
394 
395             mSocketOutStream.writeInt(0);
396 
397             return null;
398         } catch (IOException ioe) {
399             throw new IllegalStateException("Error writing to command socket", ioe);
400         }
401     }
402 
403     /**
404      * Makes the necessary changes to implement a new API deny list exemption policy, and then
405      * responds to the system server, letting it know that the task has been completed.
406      *
407      * This necessitates a change to the internal state of the Zygote.  As such, if the USAP
408      * pool is enabled all existing USAPs have an incorrect API deny list exemption list.  To
409      * properly handle this request the pool must be emptied and refilled.  This process can return
410      * a Runnable object that must be returned to ZygoteServer.runSelectLoop to be invoked.
411      *
412      * @param zygoteServer  The server object that received the request
413      * @param exemptions  The new exemption list.
414      * @return A Runnable object representing a new app in any USAPs spawned from here; the
415      *         zygote process will always receive a null value from this function.
416      */
handleApiDenylistExemptions(ZygoteServer zygoteServer, String[] exemptions)417     private Runnable handleApiDenylistExemptions(ZygoteServer zygoteServer, String[] exemptions) {
418         return stateChangeWithUsapPoolReset(zygoteServer,
419                 () -> ZygoteInit.setApiDenylistExemptions(exemptions));
420     }
421 
handleUsapPoolStatusChange(ZygoteServer zygoteServer, boolean newStatus)422     private Runnable handleUsapPoolStatusChange(ZygoteServer zygoteServer, boolean newStatus) {
423         try {
424             Runnable fpResult = zygoteServer.setUsapPoolStatus(newStatus, mSocket);
425 
426             if (fpResult == null) {
427                 mSocketOutStream.writeInt(0);
428             } else {
429                 zygoteServer.setForkChild();
430             }
431 
432             return fpResult;
433         } catch (IOException ioe) {
434             throw new IllegalStateException("Error writing to command socket", ioe);
435         }
436     }
437 
438     /**
439      * Changes the API access log sample rate for the Zygote and processes spawned from it.
440      *
441      * This necessitates a change to the internal state of the Zygote.  As such, if the USAP
442      * pool is enabled all existing USAPs have an incorrect API access log sample rate.  To
443      * properly handle this request the pool must be emptied and refilled.  This process can return
444      * a Runnable object that must be returned to ZygoteServer.runSelectLoop to be invoked.
445      *
446      * @param zygoteServer  The server object that received the request
447      * @param samplingRate  The new sample rate for regular logging
448      * @param statsdSamplingRate  The new sample rate for statslog logging
449      * @return A Runnable object representing a new app in any blastulas spawned from here; the
450      *         zygote process will always receive a null value from this function.
451      */
handleHiddenApiAccessLogSampleRate(ZygoteServer zygoteServer, int samplingRate, int statsdSamplingRate)452     private Runnable handleHiddenApiAccessLogSampleRate(ZygoteServer zygoteServer,
453             int samplingRate, int statsdSamplingRate) {
454         return stateChangeWithUsapPoolReset(zygoteServer, () -> {
455             int maxSamplingRate = Math.max(samplingRate, statsdSamplingRate);
456             ZygoteInit.setHiddenApiAccessLogSampleRate(maxSamplingRate);
457             StatsdHiddenApiUsageLogger.setHiddenApiAccessLogSampleRates(
458                     samplingRate, statsdSamplingRate);
459             ZygoteInit.setHiddenApiUsageLogger(StatsdHiddenApiUsageLogger.getInstance());
460         });
461     }
462 
preload()463     protected void preload() {
464         ZygoteInit.lazyPreload();
465     }
466 
isPreloadComplete()467     protected boolean isPreloadComplete() {
468         return ZygoteInit.isPreloadComplete();
469     }
470 
getSocketOutputStream()471     protected DataOutputStream getSocketOutputStream() {
472         return mSocketOutStream;
473     }
474 
handlePreloadPackage(String packagePath, String libsPath, String libFileName, String cacheKey)475     protected void handlePreloadPackage(String packagePath, String libsPath, String libFileName,
476             String cacheKey) {
477         throw new RuntimeException("Zygote does not support package preloading");
478     }
479 
canPreloadApp()480     protected boolean canPreloadApp() {
481         return false;
482     }
483 
handlePreloadApp(ApplicationInfo aInfo)484     protected void handlePreloadApp(ApplicationInfo aInfo) {
485         throw new RuntimeException("Zygote does not support app preloading");
486     }
487 
488     /**
489      * Closes socket associated with this connection.
490      */
491     @UnsupportedAppUsage
closeSocket()492     void closeSocket() {
493         try {
494             mSocket.close();
495         } catch (IOException ex) {
496             Log.e(TAG, "Exception while closing command "
497                     + "socket in parent", ex);
498         }
499     }
500 
isClosedByPeer()501     boolean isClosedByPeer() {
502         return isEof;
503     }
504 
505     /**
506      * Handles post-fork setup of child proc, closing sockets as appropriate,
507      * reopen stdio as appropriate, and ultimately throwing MethodAndArgsCaller
508      * if successful or returning if failed.
509      *
510      * @param parsedArgs non-null; zygote args
511      * @param pipeFd null-ok; pipe for communication back to Zygote.
512      * @param isZygote whether this new child process is itself a new Zygote.
513      */
handleChildProc(ZygoteArguments parsedArgs, FileDescriptor pipeFd, boolean isZygote)514     private Runnable handleChildProc(ZygoteArguments parsedArgs,
515             FileDescriptor pipeFd, boolean isZygote) {
516         /*
517          * By the time we get here, the native code has closed the two actual Zygote
518          * socket connections, and substituted /dev/null in their place.  The LocalSocket
519          * objects still need to be closed properly.
520          */
521 
522         closeSocket();
523 
524         Zygote.setAppProcessName(parsedArgs, TAG);
525 
526         // End of the postFork event.
527         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
528         if (parsedArgs.mInvokeWith != null) {
529             WrapperInit.execApplication(parsedArgs.mInvokeWith,
530                     parsedArgs.mNiceName, parsedArgs.mTargetSdkVersion,
531                     VMRuntime.getCurrentInstructionSet(),
532                     pipeFd, parsedArgs.mRemainingArgs);
533 
534             // Should not get here.
535             throw new IllegalStateException("WrapperInit.execApplication unexpectedly returned");
536         } else {
537             if (!isZygote) {
538                 return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
539                         parsedArgs.mDisabledCompatChanges,
540                         parsedArgs.mRemainingArgs, null /* classLoader */);
541             } else {
542                 return ZygoteInit.childZygoteInit(
543                         parsedArgs.mRemainingArgs  /* classLoader */);
544             }
545         }
546     }
547 
548     /**
549      * Handles post-fork cleanup of parent proc
550      *
551      * @param pid != 0; pid of child if &gt; 0 or indication of failed fork
552      * if &lt; 0;
553      * @param pipeFd null-ok; pipe for communication with child.
554      */
handleParentProc(int pid, FileDescriptor pipeFd)555     private void handleParentProc(int pid, FileDescriptor pipeFd) {
556         if (pid > 0) {
557             setChildPgid(pid);
558         }
559 
560         boolean usingWrapper = false;
561         if (pipeFd != null && pid > 0) {
562             int innerPid = -1;
563             try {
564                 // Do a busy loop here. We can't guarantee that a failure (and thus an exception
565                 // bail) happens in a timely manner.
566                 final int BYTES_REQUIRED = 4;  // Bytes in an int.
567 
568                 StructPollfd[] fds = new StructPollfd[] {
569                         new StructPollfd()
570                 };
571 
572                 byte[] data = new byte[BYTES_REQUIRED];
573 
574                 int remainingSleepTime = WRAPPED_PID_TIMEOUT_MILLIS;
575                 int dataIndex = 0;
576                 long startTime = System.nanoTime();
577 
578                 while (dataIndex < data.length && remainingSleepTime > 0) {
579                     fds[0].fd = pipeFd;
580                     fds[0].events = (short) POLLIN;
581                     fds[0].revents = 0;
582                     fds[0].userData = null;
583 
584                     int res = android.system.Os.poll(fds, remainingSleepTime);
585                     long endTime = System.nanoTime();
586                     int elapsedTimeMs =
587                             (int) TimeUnit.MILLISECONDS.convert(
588                                     endTime - startTime,
589                                     TimeUnit.NANOSECONDS);
590                     remainingSleepTime = WRAPPED_PID_TIMEOUT_MILLIS - elapsedTimeMs;
591 
592                     if (res > 0) {
593                         if ((fds[0].revents & POLLIN) != 0) {
594                             // Only read one byte, so as not to block. Really needed?
595                             int readBytes = android.system.Os.read(pipeFd, data, dataIndex, 1);
596                             if (readBytes < 0) {
597                                 throw new RuntimeException("Some error");
598                             }
599                             dataIndex += readBytes;
600                         } else {
601                             // Error case. revents should contain one of the error bits.
602                             break;
603                         }
604                     } else if (res == 0) {
605                         Log.w(TAG, "Timed out waiting for child.");
606                     }
607                 }
608 
609                 if (dataIndex == data.length) {
610                     DataInputStream is = new DataInputStream(new ByteArrayInputStream(data));
611                     innerPid = is.readInt();
612                 }
613 
614                 if (innerPid == -1) {
615                     Log.w(TAG, "Error reading pid from wrapped process, child may have died");
616                 }
617             } catch (Exception ex) {
618                 Log.w(TAG, "Error reading pid from wrapped process, child may have died", ex);
619             }
620 
621             // Ensure that the pid reported by the wrapped process is either the
622             // child process that we forked, or a descendant of it.
623             if (innerPid > 0) {
624                 int parentPid = innerPid;
625                 while (parentPid > 0 && parentPid != pid) {
626                     parentPid = Process.getParentPid(parentPid);
627                 }
628                 if (parentPid > 0) {
629                     Log.i(TAG, "Wrapped process has pid " + innerPid);
630                     pid = innerPid;
631                     usingWrapper = true;
632                 } else {
633                     Log.w(TAG, "Wrapped process reported a pid that is not a child of "
634                             + "the process that we forked: childPid=" + pid
635                             + " innerPid=" + innerPid);
636                 }
637             }
638         }
639 
640         try {
641             mSocketOutStream.writeInt(pid);
642             mSocketOutStream.writeBoolean(usingWrapper);
643         } catch (IOException ex) {
644             throw new IllegalStateException("Error writing to command socket", ex);
645         }
646     }
647 
setChildPgid(int pid)648     private void setChildPgid(int pid) {
649         // Try to move the new child into the peer's process group.
650         try {
651             Os.setpgid(pid, Os.getpgid(peer.getPid()));
652         } catch (ErrnoException ex) {
653             // This exception is expected in the case where
654             // the peer is not in our session
655             // TODO get rid of this log message in the case where
656             // getsid(0) != getsid(peer.getPid())
657             Log.i(TAG, "Zygote: setpgid failed. This is "
658                 + "normal if peer is not in our session");
659         }
660     }
661 }
662