1 /* 2 * Copyright (C) 2018 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 android.os; 18 19 import static android.os.Process.ZYGOTE_POLICY_FLAG_EMPTY; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.content.pm.ApplicationInfo; 24 import android.content.pm.ProcessInfo; 25 import android.util.Log; 26 import android.util.Pair; 27 28 import com.android.internal.annotations.GuardedBy; 29 import com.android.internal.os.Zygote; 30 31 import dalvik.system.VMRuntime; 32 33 import java.util.Map; 34 35 /** 36 * AppZygote is responsible for interfacing with an application-specific zygote. 37 * 38 * Application zygotes can pre-load app-specific code and data, and this interface can 39 * be used to spawn isolated services from such an application zygote. 40 * 41 * Note that we'll have only one instance of this per application / uid combination. 42 * 43 * @hide 44 */ 45 public class AppZygote { 46 private static final String LOG_TAG = "AppZygote"; 47 48 // UID of the Zygote itself 49 private final int mZygoteUid; 50 51 // First UID/GID of the range the AppZygote can setuid()/setgid() to 52 private final int mZygoteUidGidMin; 53 54 // Last UID/GID of the range the AppZygote can setuid()/setgid() to 55 private final int mZygoteUidGidMax; 56 57 private final Object mLock = new Object(); 58 59 /** 60 * Instance that maintains the socket connection to the zygote. This is {@code null} if the 61 * zygote is not running or is not connected. 62 */ 63 @GuardedBy("mLock") 64 private ChildZygoteProcess mZygote; 65 66 private final ApplicationInfo mAppInfo; 67 private final ProcessInfo mProcessInfo; 68 AppZygote(ApplicationInfo appInfo, ProcessInfo processInfo, int zygoteUid, int uidGidMin, int uidGidMax)69 public AppZygote(ApplicationInfo appInfo, ProcessInfo processInfo, int zygoteUid, int uidGidMin, 70 int uidGidMax) { 71 mAppInfo = appInfo; 72 mProcessInfo = processInfo; 73 mZygoteUid = zygoteUid; 74 mZygoteUidGidMin = uidGidMin; 75 mZygoteUidGidMax = uidGidMax; 76 } 77 78 /** 79 * Returns the zygote process associated with this app zygote. 80 * Creates the process if it's not already running. 81 */ getProcess()82 public ChildZygoteProcess getProcess() { 83 synchronized (mLock) { 84 if (mZygote != null) return mZygote; 85 86 connectToZygoteIfNeededLocked(); 87 return mZygote; 88 } 89 } 90 91 /** 92 * Stops the Zygote and kills the zygote process. 93 */ stopZygote()94 public void stopZygote() { 95 synchronized (mLock) { 96 stopZygoteLocked(); 97 } 98 } 99 getAppInfo()100 public ApplicationInfo getAppInfo() { 101 return mAppInfo; 102 } 103 104 /** 105 * Start a new process. 106 * 107 * <p>Wrap ZygoteProcess.start with retry logic. 108 * 109 * @param processClass The class to use as the process's main entry 110 * point. 111 * @param niceName A more readable name to use for the process. 112 * @param uid The user-id under which the process will run. 113 * @param gids Additional group-ids associated with the process. 114 * @param runtimeFlags Additional flags. 115 * @param targetSdkVersion The target SDK version for the app. 116 * @param seInfo null-ok SELinux information for the new process. 117 * @param abi non-null the ABI this app should be started with. 118 * @param instructionSet null-ok the instruction set to use. 119 * @param appDataDir null-ok the data directory of the app. 120 * @param packageName null-ok the name of the package this process belongs to. 121 * @param isTopApp Whether the process starts for high priority application. 122 * @param disabledCompatChanges null-ok list of disabled compat changes for the process being 123 * started. 124 * @param pkgDataInfoMap Map from related package names to private data directory 125 * volume UUID and inode number. 126 * @param allowlistedDataInfoList Map from allowlisted package names to private data directory 127 * volume UUID and inode number. 128 * @param zygoteArgs Additional arguments to supply to the Zygote process. 129 * @return An object that describes the result of the attempt to start the process. 130 * @throws RuntimeException on fatal start failure 131 */ startProcess(@onNull final String processClass, final String niceName, int uid, @Nullable int[] gids, int runtimeFlags, int mountExternal, int targetSdkVersion, @Nullable String seInfo, @NonNull String abi, @Nullable String instructionSet, @Nullable String appDataDir, @Nullable String packageName, boolean isTopApp, @Nullable long[] disabledCompatChanges, @Nullable Map<String, Pair<String, Long>> pkgDataInfoMap, @Nullable Map<String, Pair<String, Long>> allowlistedDataInfoList, @Nullable String[] zygoteArgs)132 public final Process.ProcessStartResult startProcess(@NonNull final String processClass, 133 final String niceName, 134 int uid, @Nullable int[] gids, 135 int runtimeFlags, int mountExternal, 136 int targetSdkVersion, 137 @Nullable String seInfo, 138 @NonNull String abi, 139 @Nullable String instructionSet, 140 @Nullable String appDataDir, 141 @Nullable String packageName, 142 boolean isTopApp, 143 @Nullable long[] disabledCompatChanges, 144 @Nullable Map<String, Pair<String, Long>> 145 pkgDataInfoMap, 146 @Nullable Map<String, Pair<String, Long>> 147 allowlistedDataInfoList, 148 @Nullable String[] zygoteArgs) { 149 try { 150 return getProcess().start(processClass, 151 niceName, uid, uid, gids, runtimeFlags, mountExternal, 152 targetSdkVersion, seInfo, abi, instructionSet, 153 appDataDir, null, packageName, 154 /*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp, 155 disabledCompatChanges, pkgDataInfoMap, allowlistedDataInfoList, 156 false, false, false, 157 zygoteArgs); 158 } catch (RuntimeException e) { 159 if (!Flags.appZygoteRetryStart()) { 160 throw e; 161 } 162 final boolean zygote_dead = getProcess().isDead(); 163 if (!zygote_dead) { 164 throw e; // Zygote process is alive. Do nothing. 165 } 166 } 167 // Retry here if the previous start fails. 168 Log.w(LOG_TAG, "retry starting process " + niceName); 169 stopZygote(); 170 return getProcess().start(processClass, 171 niceName, uid, uid, gids, runtimeFlags, mountExternal, 172 targetSdkVersion, seInfo, abi, instructionSet, 173 appDataDir, null, packageName, 174 /*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp, 175 disabledCompatChanges, pkgDataInfoMap, allowlistedDataInfoList, 176 false, false, false, 177 zygoteArgs); 178 } 179 180 @GuardedBy("mLock") stopZygoteLocked()181 private void stopZygoteLocked() { 182 if (mZygote != null) { 183 mZygote.close(); 184 // use killProcessGroup() here, so we kill all untracked children as well. 185 if (!mZygote.isDead()) { 186 Process.killProcessGroup(mZygoteUid, mZygote.getPid()); 187 } 188 mZygote = null; 189 } 190 } 191 192 @GuardedBy("mLock") connectToZygoteIfNeededLocked()193 private void connectToZygoteIfNeededLocked() { 194 String abi = mAppInfo.primaryCpuAbi != null ? mAppInfo.primaryCpuAbi : 195 Build.SUPPORTED_ABIS[0]; 196 try { 197 int runtimeFlags = Zygote.getMemorySafetyRuntimeFlagsForSecondaryZygote( 198 mAppInfo, mProcessInfo); 199 200 final int[] sharedAppGid = { 201 UserHandle.getSharedAppGid(UserHandle.getAppId(mAppInfo.uid)) }; 202 mZygote = Process.ZYGOTE_PROCESS.startChildZygote( 203 "com.android.internal.os.AppZygoteInit", 204 mAppInfo.processName + "_zygote", 205 mZygoteUid, 206 mZygoteUid, 207 sharedAppGid, // Zygote gets access to shared app GID for profiles 208 runtimeFlags, 209 "app_zygote", // seInfo 210 abi, // abi 211 abi, // acceptedAbiList 212 VMRuntime.getInstructionSet(abi), // instructionSet 213 mZygoteUidGidMin, 214 mZygoteUidGidMax); 215 216 ZygoteProcess.waitForConnectionToZygote(mZygote.getPrimarySocketAddress()); 217 // preload application code in the zygote 218 Log.i(LOG_TAG, "Starting application preload."); 219 mZygote.preloadApp(mAppInfo, abi); 220 Log.i(LOG_TAG, "Application preload done."); 221 } catch (Exception e) { 222 Log.e(LOG_TAG, "Error connecting to app zygote", e); 223 stopZygoteLocked(); 224 } 225 } 226 } 227