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 > 0 or indication of failed fork 555 * if < 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