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