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