• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.webkit;
18 
19 import android.content.pm.PackageInfo;
20 import android.os.Build;
21 import android.os.ChildZygoteProcess;
22 import android.os.Process;
23 import android.os.ZygoteProcess;
24 import android.text.TextUtils;
25 import android.util.Log;
26 
27 import com.android.internal.annotations.GuardedBy;
28 import com.android.internal.os.Zygote;
29 
30 /** @hide */
31 public class WebViewZygote {
32     private static final String LOGTAG = "WebViewZygote";
33 
34     /**
35      * Lock object that protects all other static members.
36      */
37     private static final Object sLock = new Object();
38 
39     /**
40      * Instance that maintains the socket connection to the zygote. This is {@code null} if the
41      * zygote is not running or is not connected.
42      */
43     @GuardedBy("sLock")
44     private static ChildZygoteProcess sZygote;
45 
46     /**
47      * Information about the selected WebView package. This is set from #onWebViewProviderChanged().
48      */
49     @GuardedBy("sLock")
50     private static PackageInfo sPackage;
51 
52     /**
53      * Flag for whether multi-process WebView is enabled. If this is {@code false}, the zygote
54      * will not be started.
55      */
56     @GuardedBy("sLock")
57     private static boolean sMultiprocessEnabled = false;
58 
getProcess()59     public static ZygoteProcess getProcess() {
60         synchronized (sLock) {
61             if (sZygote != null) return sZygote;
62 
63             connectToZygoteIfNeededLocked();
64             return sZygote;
65         }
66     }
67 
getPackageName()68     public static String getPackageName() {
69         synchronized (sLock) {
70             return sPackage.packageName;
71         }
72     }
73 
isMultiprocessEnabled()74     public static boolean isMultiprocessEnabled() {
75         synchronized (sLock) {
76             return sMultiprocessEnabled && sPackage != null;
77         }
78     }
79 
setMultiprocessEnabled(boolean enabled)80     public static void setMultiprocessEnabled(boolean enabled) {
81         synchronized (sLock) {
82             sMultiprocessEnabled = enabled;
83 
84             // When multi-process is disabled, kill the zygote. When it is enabled,
85             // the zygote will be started when it is first needed in getProcess().
86             if (!enabled) {
87                 stopZygoteLocked();
88             }
89         }
90     }
91 
onWebViewProviderChanged(PackageInfo packageInfo)92     static void onWebViewProviderChanged(PackageInfo packageInfo) {
93         synchronized (sLock) {
94             sPackage = packageInfo;
95 
96             // If multi-process is not enabled, then do not start the zygote service.
97             if (!sMultiprocessEnabled) {
98                 return;
99             }
100 
101             stopZygoteLocked();
102         }
103     }
104 
105     @GuardedBy("sLock")
stopZygoteLocked()106     private static void stopZygoteLocked() {
107         if (sZygote != null) {
108             // Close the connection and kill the zygote process. This will not cause
109             // child processes to be killed by itself. But if this is called in response to
110             // setMultiprocessEnabled() or onWebViewProviderChanged(), the WebViewUpdater
111             // will kill all processes that depend on the WebView package.
112             sZygote.close();
113             Process.killProcess(sZygote.getPid());
114             sZygote = null;
115         }
116     }
117 
118     @GuardedBy("sLock")
connectToZygoteIfNeededLocked()119     private static void connectToZygoteIfNeededLocked() {
120         if (sZygote != null) {
121             return;
122         }
123 
124         if (sPackage == null) {
125             Log.e(LOGTAG, "Cannot connect to zygote, no package specified");
126             return;
127         }
128 
129         try {
130             String abi = sPackage.applicationInfo.primaryCpuAbi;
131             int runtimeFlags = Zygote.getMemorySafetyRuntimeFlagsForSecondaryZygote(
132                     sPackage.applicationInfo, null);
133             sZygote = Process.ZYGOTE_PROCESS.startChildZygote(
134                     "com.android.internal.os.WebViewZygoteInit",
135                     "webview_zygote",
136                     Process.WEBVIEW_ZYGOTE_UID,
137                     Process.WEBVIEW_ZYGOTE_UID,
138                     null,  // gids
139                     runtimeFlags,
140                     "webview_zygote",  // seInfo
141                     abi,  // abi
142                     TextUtils.join(",", Build.SUPPORTED_ABIS),
143                     null, // instructionSet
144                     Process.FIRST_ISOLATED_UID,
145                     Integer.MAX_VALUE); // TODO(b/123615476) deal with user-id ranges properly
146             ZygoteProcess.waitForConnectionToZygote(sZygote.getPrimarySocketAddress());
147             sZygote.preloadApp(sPackage.applicationInfo, abi);
148         } catch (Exception e) {
149             Log.e(LOGTAG, "Error connecting to webview zygote", e);
150             stopZygoteLocked();
151         }
152     }
153 }
154