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