blob: f1e8fc2d310bb8a18d393db4ce8fe4295fe61156 [file] [log] [blame]
Jonathan Dixond3101b12012-04-12 20:51:51 +01001/*
2 * Copyright (C) 2012 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
17package android.webkit;
18
Ignacio Solla451e3382014-11-10 10:35:54 +000019import android.annotation.SystemApi;
Primiano Tucci810c0522014-07-25 18:03:16 +010020import android.app.ActivityManagerInternal;
Gustav Senntoncd8f2732016-04-14 09:58:36 +010021import android.app.ActivityManagerNative;
Torne (Richard Coles)6c778ce2014-07-17 14:14:48 -070022import android.app.AppGlobals;
Jeff Sharkey85844912014-11-13 16:20:38 -080023import android.app.Application;
Torne (Richard Coles)6c778ce2014-07-17 14:14:48 -070024import android.content.Context;
Primiano Tucci1b7977b2014-07-25 19:19:32 +010025import android.content.pm.ApplicationInfo;
Torne (Richard Coles)0606cd52014-08-05 16:12:09 +010026import android.content.pm.PackageInfo;
Torne (Richard Coles)6c778ce2014-07-17 14:14:48 -070027import android.content.pm.PackageManager;
Gustav Senntoncd8f2732016-04-14 09:58:36 +010028import android.content.pm.Signature;
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +010029import android.os.Build;
30import android.os.Process;
31import android.os.RemoteException;
32import android.os.ServiceManager;
Ben Murdoche09e9762012-07-19 14:48:13 +010033import android.os.StrictMode;
Ben Murdoch5ced5022014-07-28 15:57:00 +010034import android.os.SystemProperties;
Torne (Richard Coles)38228822014-08-13 17:11:45 +010035import android.os.Trace;
Primiano Tucci1b7977b2014-07-25 19:19:32 +010036import android.text.TextUtils;
Torne (Richard Coles)03ce9b32013-06-12 16:02:03 +010037import android.util.AndroidRuntimeException;
Gustav Senntoncd8f2732016-04-14 09:58:36 +010038import android.util.ArraySet;
Jonathan Dixond3101b12012-04-12 20:51:51 +010039import android.util.Log;
Jeff Sharkey85844912014-11-13 16:20:38 -080040
Primiano Tucci810c0522014-07-25 18:03:16 +010041import com.android.server.LocalServices;
Jeff Sharkey85844912014-11-13 16:20:38 -080042
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +010043import dalvik.system.VMRuntime;
44
45import java.io.File;
Simon Baldwinb98082dc2015-05-15 12:56:50 +010046import java.io.IOException;
Primiano Tucci1b7977b2014-07-25 19:19:32 +010047import java.util.Arrays;
Simon Baldwinb98082dc2015-05-15 12:56:50 +010048import java.util.zip.ZipEntry;
49import java.util.zip.ZipFile;
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +010050
Jonathan Dixond3101b12012-04-12 20:51:51 +010051/**
52 * Top level factory, used creating all the main WebView implementation classes.
Jared Dukeb0e35842013-03-19 16:25:39 -070053 *
54 * @hide
Jonathan Dixond3101b12012-04-12 20:51:51 +010055 */
Ignacio Solla451e3382014-11-10 10:35:54 +000056@SystemApi
Jared Dukeb0e35842013-03-19 16:25:39 -070057public final class WebViewFactory {
Jonathan Dixona7eaa8e2013-07-25 19:52:47 -070058
Torne (Richard Coles)3b6ca992016-10-10 15:11:36 +010059 // visible for WebViewZygoteInit to look up the class by reflection and call preloadInZygote.
60 /** @hide */
61 public static final String CHROMIUM_WEBVIEW_FACTORY =
Torne (Richard Coles)a9bbd942012-10-24 11:59:22 +010062 "com.android.webview.chromium.WebViewChromiumFactoryProvider";
Jonathan Dixond3101b12012-04-12 20:51:51 +010063
Ben Murdoch0e04bcf2014-05-16 13:41:12 +010064 private static final String NULL_WEBVIEW_FACTORY =
65 "com.android.webview.nullwebview.NullWebViewFactoryProvider";
66
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +010067 private static final String CHROMIUM_WEBVIEW_NATIVE_RELRO_32 =
68 "/data/misc/shared_relro/libwebviewchromium32.relro";
69 private static final String CHROMIUM_WEBVIEW_NATIVE_RELRO_64 =
70 "/data/misc/shared_relro/libwebviewchromium64.relro";
71
Ben Murdoch5ced5022014-07-28 15:57:00 +010072 public static final String CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY =
73 "persist.sys.webview.vmsize";
74 private static final long CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES = 100 * 1024 * 1024;
Primiano Tucci1b7977b2014-07-25 19:19:32 +010075
Jonathan Dixond3101b12012-04-12 20:51:51 +010076 private static final String LOGTAG = "WebViewFactory";
77
78 private static final boolean DEBUG = false;
79
80 // Cache the factory both for efficiency, and ensure any one process gets all webviews from the
81 // same provider.
82 private static WebViewFactoryProvider sProviderInstance;
John Reck9f9d3452012-09-20 13:18:59 -070083 private static final Object sProviderLock = new Object();
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +010084 private static boolean sAddressSpaceReserved = false;
Torne (Richard Coles)84392d72014-08-14 16:43:18 +010085 private static PackageInfo sPackageInfo;
Jonathan Dixond3101b12012-04-12 20:51:51 +010086
Gustav Sennton85edb6c2015-04-15 11:54:20 +010087 // Error codes for loadWebViewNativeLibraryFromPackage
88 public static final int LIBLOAD_SUCCESS = 0;
89 public static final int LIBLOAD_WRONG_PACKAGE_NAME = 1;
90 public static final int LIBLOAD_ADDRESS_SPACE_NOT_RESERVED = 2;
Gustav Sennton6258dcd2015-10-30 19:25:37 +000091
92 // error codes for waiting for WebView preparation
Gustav Sennton85edb6c2015-04-15 11:54:20 +010093 public static final int LIBLOAD_FAILED_WAITING_FOR_RELRO = 3;
94 public static final int LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES = 4;
95
96 // native relro loading error codes
97 public static final int LIBLOAD_FAILED_TO_OPEN_RELRO_FILE = 5;
98 public static final int LIBLOAD_FAILED_TO_LOAD_LIBRARY = 6;
99 public static final int LIBLOAD_FAILED_JNI_CALL = 7;
100
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000101 // more error codes for waiting for WebView preparation
Gustav Sennton26c82ff2016-03-11 13:06:40 +0000102 public static final int LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN = 8;
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000103
Torne (Richard Coles)1a904122016-03-14 13:45:55 +0000104 // error for namespace lookup
105 public static final int LIBLOAD_FAILED_TO_FIND_NAMESPACE = 10;
106
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000107 private static String getWebViewPreparationErrorReason(int error) {
108 switch (error) {
109 case LIBLOAD_FAILED_WAITING_FOR_RELRO:
110 return "Time out waiting for Relro files being created";
111 case LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES:
112 return "No WebView installed";
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000113 case LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN:
114 return "Crashed for unknown reason";
115 }
116 return "Unknown";
117 }
118
119 /**
120 * @hide
121 */
122 public static class MissingWebViewPackageException extends AndroidRuntimeException {
Gustav Senntona8366e72015-04-17 11:24:07 +0100123 public MissingWebViewPackageException(String message) { super(message); }
124 public MissingWebViewPackageException(Exception e) { super(e); }
125 }
126
Gustav Sennton6ce92c92015-10-23 11:10:39 +0100127 /**
Gustav Sennton6ce92c92015-10-23 11:10:39 +0100128 * @hide
129 */
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000130 public static String getWebViewLibrary(ApplicationInfo ai) {
Gustav Sennton2ed6fee2015-03-03 15:12:34 +0000131 if (ai.metaData != null)
132 return ai.metaData.getString("com.android.webview.WebViewLibrary");
133 return null;
Ben Murdochdc00a842014-07-17 14:55:00 +0100134 }
135
Torne (Richard Coles)84392d72014-08-14 16:43:18 +0100136 public static PackageInfo getLoadedPackageInfo() {
137 return sPackageInfo;
138 }
139
Gustav Sennton85edb6c2015-04-15 11:54:20 +0100140 /**
141 * Load the native library for the given package name iff that package
Gustav Senntond97301822015-06-18 16:56:26 +0100142 * name is the same as the one providing the webview.
Gustav Sennton85edb6c2015-04-15 11:54:20 +0100143 */
Torne (Richard Coles)1a904122016-03-14 13:45:55 +0000144 public static int loadWebViewNativeLibraryFromPackage(String packageName,
145 ClassLoader clazzLoader) {
Gustav Senntonb088cb32016-06-17 14:02:52 +0100146 WebViewProviderResponse response = null;
147 try {
148 response = getUpdateService().waitForAndGetProvider();
149 } catch (RemoteException e) {
150 Log.e(LOGTAG, "error waiting for relro creation", e);
151 return LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN;
Gustav Sennton6ce92c92015-10-23 11:10:39 +0100152 }
Gustav Senntonb088cb32016-06-17 14:02:52 +0100153
154
155 if (response.status != LIBLOAD_SUCCESS
156 && response.status != LIBLOAD_FAILED_WAITING_FOR_RELRO) {
157 return response.status;
158 }
159 if (!response.packageInfo.packageName.equals(packageName)) {
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000160 return LIBLOAD_WRONG_PACKAGE_NAME;
Gustav Senntonb088cb32016-06-17 14:02:52 +0100161 }
162
163 PackageManager packageManager = AppGlobals.getInitialApplication().getPackageManager();
164 PackageInfo packageInfo;
165 try {
166 packageInfo = packageManager.getPackageInfo(packageName,
167 PackageManager.GET_META_DATA | PackageManager.MATCH_DEBUG_TRIAGED_MISSING);
168 } catch (PackageManager.NameNotFoundException e) {
169 Log.e(LOGTAG, "Couldn't find package " + packageName);
170 return LIBLOAD_WRONG_PACKAGE_NAME;
171 }
172 sPackageInfo = packageInfo;
Gustav Sennton6ce92c92015-10-23 11:10:39 +0100173
Gustav Senntonfd07efa2016-05-23 13:09:03 +0100174 int loadNativeRet = loadNativeLibrary(clazzLoader);
175 // If we failed waiting for relro we want to return that fact even if we successfully load
176 // the relro file.
Gustav Senntonb088cb32016-06-17 14:02:52 +0100177 if (loadNativeRet == LIBLOAD_SUCCESS) return response.status;
Gustav Senntonfd07efa2016-05-23 13:09:03 +0100178 return loadNativeRet;
Gustav Sennton85edb6c2015-04-15 11:54:20 +0100179 }
180
John Reck9f9d3452012-09-20 13:18:59 -0700181 static WebViewFactoryProvider getProvider() {
182 synchronized (sProviderLock) {
183 // For now the main purpose of this function (and the factory abstraction) is to keep
Torne (Richard Coles)d892afc2013-10-14 17:14:04 +0100184 // us honest and minimize usage of WebView internals when binding the proxy.
John Reck9f9d3452012-09-20 13:18:59 -0700185 if (sProviderInstance != null) return sProviderInstance;
Jonathan Dixond3101b12012-04-12 20:51:51 +0100186
Jeff Sharkey85844912014-11-13 16:20:38 -0800187 final int uid = android.os.Process.myUid();
188 if (uid == android.os.Process.ROOT_UID || uid == android.os.Process.SYSTEM_UID) {
189 throw new UnsupportedOperationException(
190 "For security reasons, WebView is not allowed in privileged processes");
191 }
192
Gustav Sennton5df5e222016-02-25 18:20:12 +0000193 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
Torne (Richard Coles)38228822014-08-13 17:11:45 +0100194 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getProvider()");
Torne (Richard Coles)03ce9b32013-06-12 16:02:03 +0100195 try {
Gustav Senntona8366e72015-04-17 11:24:07 +0100196 Class<WebViewFactoryProvider> providerClass = getProviderClass();
Torne (Richard Coles)38228822014-08-13 17:11:45 +0100197
Torne (Richard Coles)38228822014-08-13 17:11:45 +0100198 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "providerClass.newInstance()");
199 try {
Ignacio Solla1ea39b32014-11-10 14:08:37 +0000200 sProviderInstance = providerClass.getConstructor(WebViewDelegate.class)
201 .newInstance(new WebViewDelegate());
Torne (Richard Coles)38228822014-08-13 17:11:45 +0100202 if (DEBUG) Log.v(LOGTAG, "Loaded provider: " + sProviderInstance);
203 return sProviderInstance;
204 } catch (Exception e) {
205 Log.e(LOGTAG, "error instantiating provider", e);
206 throw new AndroidRuntimeException(e);
207 } finally {
208 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
Torne (Richard Coles)38228822014-08-13 17:11:45 +0100209 }
Torne (Richard Coles)03ce9b32013-06-12 16:02:03 +0100210 } finally {
Torne (Richard Coles)38228822014-08-13 17:11:45 +0100211 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
Gustav Sennton5df5e222016-02-25 18:20:12 +0000212 StrictMode.setThreadPolicy(oldPolicy);
Torne (Richard Coles)03ce9b32013-06-12 16:02:03 +0100213 }
Jonathan Dixond3101b12012-04-12 20:51:51 +0100214 }
Jonathan Dixond3101b12012-04-12 20:51:51 +0100215 }
216
Gustav Senntoncd8f2732016-04-14 09:58:36 +0100217 /**
218 * Returns true if the signatures match, false otherwise
219 */
220 private static boolean signaturesEquals(Signature[] s1, Signature[] s2) {
221 if (s1 == null) {
222 return s2 == null;
223 }
224 if (s2 == null) return false;
225
226 ArraySet<Signature> set1 = new ArraySet<>();
227 for(Signature signature : s1) {
228 set1.add(signature);
229 }
230 ArraySet<Signature> set2 = new ArraySet<>();
231 for(Signature signature : s2) {
232 set2.add(signature);
233 }
234 return set1.equals(set2);
235 }
236
237 // Throws MissingWebViewPackageException on failure
238 private static void verifyPackageInfo(PackageInfo chosen, PackageInfo toUse) {
239 if (!chosen.packageName.equals(toUse.packageName)) {
240 throw new MissingWebViewPackageException("Failed to verify WebView provider, "
241 + "packageName mismatch, expected: "
242 + chosen.packageName + " actual: " + toUse.packageName);
243 }
244 if (chosen.versionCode > toUse.versionCode) {
245 throw new MissingWebViewPackageException("Failed to verify WebView provider, "
Hui Shub5f554a2016-04-20 17:17:44 -0700246 + "version code is lower than expected: " + chosen.versionCode
Gustav Senntoncd8f2732016-04-14 09:58:36 +0100247 + " actual: " + toUse.versionCode);
248 }
249 if (getWebViewLibrary(toUse.applicationInfo) == null) {
250 throw new MissingWebViewPackageException("Tried to load an invalid WebView provider: "
251 + toUse.packageName);
252 }
253 if (!signaturesEquals(chosen.signatures, toUse.signatures)) {
254 throw new MissingWebViewPackageException("Failed to verify WebView provider, "
255 + "signature mismatch");
256 }
257 }
258
259 private static Context getWebViewContextAndSetProvider() {
260 Application initialApplication = AppGlobals.getInitialApplication();
261 try {
262 WebViewProviderResponse response = null;
263 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
264 "WebViewUpdateService.waitForAndGetProvider()");
265 try {
266 response = getUpdateService().waitForAndGetProvider();
267 } finally {
268 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
269 }
Gustav Senntonfd07efa2016-05-23 13:09:03 +0100270 if (response.status != LIBLOAD_SUCCESS
271 && response.status != LIBLOAD_FAILED_WAITING_FOR_RELRO) {
Gustav Senntoncd8f2732016-04-14 09:58:36 +0100272 throw new MissingWebViewPackageException("Failed to load WebView provider: "
273 + getWebViewPreparationErrorReason(response.status));
274 }
275 // Register to be killed before fetching package info - so that we will be
276 // killed if the package info goes out-of-date.
277 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "ActivityManager.addPackageDependency()");
278 try {
279 ActivityManagerNative.getDefault().addPackageDependency(
280 response.packageInfo.packageName);
281 } finally {
282 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
283 }
284 // Fetch package info and verify it against the chosen package
285 PackageInfo newPackageInfo = null;
286 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "PackageManager.getPackageInfo()");
287 try {
288 newPackageInfo = initialApplication.getPackageManager().getPackageInfo(
289 response.packageInfo.packageName,
290 PackageManager.GET_SHARED_LIBRARY_FILES
291 | PackageManager.MATCH_DEBUG_TRIAGED_MISSING
292 // Make sure that we fetch the current provider even if its not
293 // installed for the current user
294 | PackageManager.MATCH_UNINSTALLED_PACKAGES
295 // Fetch signatures for verification
296 | PackageManager.GET_SIGNATURES
297 // Get meta-data for meta data flag verification
298 | PackageManager.GET_META_DATA);
299 } finally {
300 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
301 }
302
303 // Validate the newly fetched package info, throws MissingWebViewPackageException on
304 // failure
305 verifyPackageInfo(response.packageInfo, newPackageInfo);
306
307 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
308 "initialApplication.createApplicationContext");
309 try {
310 // Construct an app context to load the Java code into the current app.
311 Context webViewContext = initialApplication.createApplicationContext(
312 newPackageInfo.applicationInfo,
313 Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
Gustav Senntonb088cb32016-06-17 14:02:52 +0100314 sPackageInfo = newPackageInfo;
Gustav Senntoncd8f2732016-04-14 09:58:36 +0100315 return webViewContext;
316 } finally {
317 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
318 }
319 } catch (RemoteException | PackageManager.NameNotFoundException e) {
320 throw new MissingWebViewPackageException("Failed to load WebView provider: " + e);
321 }
322 }
323
Gustav Senntona8366e72015-04-17 11:24:07 +0100324 private static Class<WebViewFactoryProvider> getProviderClass() {
Gustav Senntoncd8f2732016-04-14 09:58:36 +0100325 Context webViewContext = null;
326 Application initialApplication = AppGlobals.getInitialApplication();
327
Gustav Senntona8366e72015-04-17 11:24:07 +0100328 try {
Gustav Senntonfc424472016-01-06 17:11:09 +0000329 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
Gustav Senntoncd8f2732016-04-14 09:58:36 +0100330 "WebViewFactory.getWebViewContextAndSetProvider()");
Gustav Senntonfc424472016-01-06 17:11:09 +0000331 try {
Gustav Senntoncd8f2732016-04-14 09:58:36 +0100332 webViewContext = getWebViewContextAndSetProvider();
Gustav Senntonfc424472016-01-06 17:11:09 +0000333 } finally {
334 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000335 }
Gustav Senntona8366e72015-04-17 11:24:07 +0100336 Log.i(LOGTAG, "Loading " + sPackageInfo.packageName + " version " +
Gustav Senntoncd8f2732016-04-14 09:58:36 +0100337 sPackageInfo.versionName + " (code " + sPackageInfo.versionCode + ")");
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000338
Gustav Senntona8366e72015-04-17 11:24:07 +0100339 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getChromiumProviderClass()");
340 try {
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000341 initialApplication.getAssets().addAssetPathAsSharedLibrary(
342 webViewContext.getApplicationInfo().sourceDir);
343 ClassLoader clazzLoader = webViewContext.getClassLoader();
Torne (Richard Coles)1a904122016-03-14 13:45:55 +0000344
345 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.loadNativeLibrary()");
346 loadNativeLibrary(clazzLoader);
347 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
348
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000349 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "Class.forName()");
350 try {
351 return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY,
352 true, clazzLoader);
353 } finally {
354 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
355 }
Gustav Senntona8366e72015-04-17 11:24:07 +0100356 } catch (ClassNotFoundException e) {
357 Log.e(LOGTAG, "error loading provider", e);
358 throw new AndroidRuntimeException(e);
359 } finally {
360 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
361 }
362 } catch (MissingWebViewPackageException e) {
363 // If the package doesn't exist, then try loading the null WebView instead.
364 // If that succeeds, then this is a device without WebView support; if it fails then
365 // swallow the failure, complain that the real WebView is missing and rethrow the
366 // original exception.
367 try {
368 return (Class<WebViewFactoryProvider>) Class.forName(NULL_WEBVIEW_FACTORY);
369 } catch (ClassNotFoundException e2) {
370 // Ignore.
371 }
372 Log.e(LOGTAG, "Chromium WebView package does not exist", e);
373 throw new AndroidRuntimeException(e);
374 }
375 }
376
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +0100377 /**
378 * Perform any WebView loading preparations that must happen in the zygote.
379 * Currently, this means allocating address space to load the real JNI library later.
380 */
381 public static void prepareWebViewInZygote() {
382 try {
383 System.loadLibrary("webviewchromium_loader");
Ben Murdoch5ced5022014-07-28 15:57:00 +0100384 long addressSpaceToReserve =
385 SystemProperties.getLong(CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY,
386 CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES);
387 sAddressSpaceReserved = nativeReserveAddressSpace(addressSpaceToReserve);
388
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +0100389 if (sAddressSpaceReserved) {
Ben Murdoch5ced5022014-07-28 15:57:00 +0100390 if (DEBUG) {
391 Log.v(LOGTAG, "address space reserved: " + addressSpaceToReserve + " bytes");
392 }
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +0100393 } else {
Ben Murdoch5ced5022014-07-28 15:57:00 +0100394 Log.e(LOGTAG, "reserving " + addressSpaceToReserve +
395 " bytes of address space failed");
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +0100396 }
Primiano Tucci810c0522014-07-25 18:03:16 +0100397 } catch (Throwable t) {
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +0100398 // Log and discard errors at this stage as we must not crash the zygote.
Primiano Tucci810c0522014-07-25 18:03:16 +0100399 Log.e(LOGTAG, "error preparing native loader", t);
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +0100400 }
401 }
402
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000403 private static int prepareWebViewInSystemServer(String[] nativeLibraryPaths) {
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +0100404 if (DEBUG) Log.v(LOGTAG, "creating relro files");
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000405 int numRelros = 0;
Ben Murdoch5ced5022014-07-28 15:57:00 +0100406
407 // We must always trigger createRelRo regardless of the value of nativeLibraryPaths. Any
408 // unexpected values will be handled there to ensure that we trigger notifying any process
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000409 // waiting on relro creation.
Ben Murdoch5ced5022014-07-28 15:57:00 +0100410 if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
411 if (DEBUG) Log.v(LOGTAG, "Create 32 bit relro");
412 createRelroFile(false /* is64Bit */, nativeLibraryPaths);
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000413 numRelros++;
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +0100414 }
Ben Murdoch5ced5022014-07-28 15:57:00 +0100415
416 if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
417 if (DEBUG) Log.v(LOGTAG, "Create 64 bit relro");
418 createRelroFile(true /* is64Bit */, nativeLibraryPaths);
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000419 numRelros++;
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +0100420 }
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000421 return numRelros;
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +0100422 }
423
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000424 /**
425 * @hide
426 */
427 public static int onWebViewProviderChanged(PackageInfo packageInfo) {
Ben Murdoch5ced5022014-07-28 15:57:00 +0100428 String[] nativeLibs = null;
429 try {
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000430 nativeLibs = WebViewFactory.getWebViewNativeLibraryPaths(packageInfo);
Torne (Richard Coles)27cb0d22014-08-08 18:24:12 +0100431 if (nativeLibs != null) {
432 long newVmSize = 0L;
Ben Murdoch5ced5022014-07-28 15:57:00 +0100433
Torne (Richard Coles)27cb0d22014-08-08 18:24:12 +0100434 for (String path : nativeLibs) {
Simon Baldwinb98082dc2015-05-15 12:56:50 +0100435 if (path == null || TextUtils.isEmpty(path)) continue;
Torne (Richard Coles)27cb0d22014-08-08 18:24:12 +0100436 if (DEBUG) Log.d(LOGTAG, "Checking file size of " + path);
Torne (Richard Coles)27cb0d22014-08-08 18:24:12 +0100437 File f = new File(path);
438 if (f.exists()) {
Simon Baldwinb98082dc2015-05-15 12:56:50 +0100439 newVmSize = Math.max(newVmSize, f.length());
440 continue;
441 }
Simon Baldwin519919b2015-06-11 17:09:49 +0100442 if (path.contains("!/")) {
443 String[] split = TextUtils.split(path, "!/");
Simon Baldwinb98082dc2015-05-15 12:56:50 +0100444 if (split.length == 2) {
Torne (Richard Coles)c5283922015-08-10 16:40:50 +0100445 try (ZipFile z = new ZipFile(split[0])) {
Simon Baldwinb98082dc2015-05-15 12:56:50 +0100446 ZipEntry e = z.getEntry(split[1]);
447 if (e != null && e.getMethod() == ZipEntry.STORED) {
448 newVmSize = Math.max(newVmSize, e.getSize());
449 continue;
450 }
451 }
452 catch (IOException e) {
453 Log.e(LOGTAG, "error reading APK file " + split[0] + ", ", e);
454 }
Torne (Richard Coles)27cb0d22014-08-08 18:24:12 +0100455 }
Ben Murdoch5ced5022014-07-28 15:57:00 +0100456 }
Simon Baldwinb98082dc2015-05-15 12:56:50 +0100457 Log.e(LOGTAG, "error sizing load for " + path);
Ben Murdoch5ced5022014-07-28 15:57:00 +0100458 }
Ben Murdoch5ced5022014-07-28 15:57:00 +0100459
Torne (Richard Coles)27cb0d22014-08-08 18:24:12 +0100460 if (DEBUG) {
461 Log.v(LOGTAG, "Based on library size, need " + newVmSize +
462 " bytes of address space.");
463 }
464 // The required memory can be larger than the file on disk (due to .bss), and an
465 // upgraded version of the library will likely be larger, so always attempt to
466 // reserve twice as much as we think to allow for the library to grow during this
467 // boot cycle.
468 newVmSize = Math.max(2 * newVmSize, CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES);
469 Log.d(LOGTAG, "Setting new address space to " + newVmSize);
470 SystemProperties.set(CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY,
471 Long.toString(newVmSize));
Ben Murdoch5ced5022014-07-28 15:57:00 +0100472 }
Torne (Richard Coles)27cb0d22014-08-08 18:24:12 +0100473 } catch (Throwable t) {
474 // Log and discard errors at this stage as we must not crash the system server.
475 Log.e(LOGTAG, "error preparing webview native library", t);
Ben Murdoch5ced5022014-07-28 15:57:00 +0100476 }
Robert Sesekded20982016-08-15 13:59:13 -0400477
478 WebViewZygote.onWebViewProviderChanged(packageInfo);
479
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000480 return prepareWebViewInSystemServer(nativeLibs);
Ben Murdoch5ced5022014-07-28 15:57:00 +0100481 }
482
Gustav Senntona8366e72015-04-17 11:24:07 +0100483 // throws MissingWebViewPackageException
Simon Baldwinb98082dc2015-05-15 12:56:50 +0100484 private static String getLoadFromApkPath(String apkPath,
485 String[] abiList,
486 String nativeLibFileName) {
487 // Search the APK for a native library conforming to a listed ABI.
Torne (Richard Coles)c5283922015-08-10 16:40:50 +0100488 try (ZipFile z = new ZipFile(apkPath)) {
Simon Baldwinb98082dc2015-05-15 12:56:50 +0100489 for (String abi : abiList) {
490 final String entry = "lib/" + abi + "/" + nativeLibFileName;
491 ZipEntry e = z.getEntry(entry);
492 if (e != null && e.getMethod() == ZipEntry.STORED) {
493 // Return a path formatted for dlopen() load from APK.
Simon Baldwin519919b2015-06-11 17:09:49 +0100494 return apkPath + "!/" + entry;
Simon Baldwinb98082dc2015-05-15 12:56:50 +0100495 }
496 }
497 } catch (IOException e) {
498 throw new MissingWebViewPackageException(e);
499 }
500 return "";
501 }
502
503 // throws MissingWebViewPackageException
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000504 private static String[] getWebViewNativeLibraryPaths(PackageInfo packageInfo) {
505 ApplicationInfo ai = packageInfo.applicationInfo;
Gustav Sennton2ed6fee2015-03-03 15:12:34 +0000506 final String NATIVE_LIB_FILE_NAME = getWebViewLibrary(ai);
Primiano Tucci1b7977b2014-07-25 19:19:32 +0100507
508 String path32;
509 String path64;
510 boolean primaryArchIs64bit = VMRuntime.is64BitAbi(ai.primaryCpuAbi);
511 if (!TextUtils.isEmpty(ai.secondaryCpuAbi)) {
512 // Multi-arch case.
513 if (primaryArchIs64bit) {
514 // Primary arch: 64-bit, secondary: 32-bit.
515 path64 = ai.nativeLibraryDir;
516 path32 = ai.secondaryNativeLibraryDir;
517 } else {
518 // Primary arch: 32-bit, secondary: 64-bit.
519 path64 = ai.secondaryNativeLibraryDir;
520 path32 = ai.nativeLibraryDir;
521 }
522 } else if (primaryArchIs64bit) {
523 // Single-arch 64-bit.
524 path64 = ai.nativeLibraryDir;
525 path32 = "";
526 } else {
527 // Single-arch 32-bit.
528 path32 = ai.nativeLibraryDir;
529 path64 = "";
530 }
Simon Baldwinb98082dc2015-05-15 12:56:50 +0100531
532 // Form the full paths to the extracted native libraries.
533 // If libraries were not extracted, try load from APK paths instead.
534 if (!TextUtils.isEmpty(path32)) {
535 path32 += "/" + NATIVE_LIB_FILE_NAME;
536 File f = new File(path32);
537 if (!f.exists()) {
538 path32 = getLoadFromApkPath(ai.sourceDir,
539 Build.SUPPORTED_32_BIT_ABIS,
540 NATIVE_LIB_FILE_NAME);
541 }
542 }
543 if (!TextUtils.isEmpty(path64)) {
544 path64 += "/" + NATIVE_LIB_FILE_NAME;
545 File f = new File(path64);
546 if (!f.exists()) {
547 path64 = getLoadFromApkPath(ai.sourceDir,
548 Build.SUPPORTED_64_BIT_ABIS,
549 NATIVE_LIB_FILE_NAME);
550 }
551 }
552
553 if (DEBUG) Log.v(LOGTAG, "Native 32-bit lib: " + path32 + ", 64-bit lib: " + path64);
Primiano Tucci1b7977b2014-07-25 19:19:32 +0100554 return new String[] { path32, path64 };
555 }
556
Ben Murdoch5ced5022014-07-28 15:57:00 +0100557 private static void createRelroFile(final boolean is64Bit, String[] nativeLibraryPaths) {
Primiano Tucci1b7977b2014-07-25 19:19:32 +0100558 final String abi =
559 is64Bit ? Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0];
Primiano Tucci810c0522014-07-25 18:03:16 +0100560
561 // crashHandler is invoked by the ActivityManagerService when the isolated process crashes.
562 Runnable crashHandler = new Runnable() {
563 @Override
564 public void run() {
565 try {
Primiano Tucci1b7977b2014-07-25 19:19:32 +0100566 Log.e(LOGTAG, "relro file creator for " + abi + " crashed. Proceeding without");
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000567 getUpdateService().notifyRelroCreationCompleted();
Primiano Tucci810c0522014-07-25 18:03:16 +0100568 } catch (RemoteException e) {
569 Log.e(LOGTAG, "Cannot reach WebViewUpdateService. " + e.getMessage());
570 }
571 }
572 };
573
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +0100574 try {
Ben Murdoch5ced5022014-07-28 15:57:00 +0100575 if (nativeLibraryPaths == null
576 || nativeLibraryPaths[0] == null || nativeLibraryPaths[1] == null) {
577 throw new IllegalArgumentException(
578 "Native library paths to the WebView RelRo process must not be null!");
579 }
Primiano Tucci161536b2014-07-28 18:51:45 +0100580 int pid = LocalServices.getService(ActivityManagerInternal.class).startIsolatedProcess(
Ben Murdoch5ced5022014-07-28 15:57:00 +0100581 RelroFileCreator.class.getName(), nativeLibraryPaths, "WebViewLoader-" + abi, abi,
Primiano Tucci810c0522014-07-25 18:03:16 +0100582 Process.SHARED_RELRO_UID, crashHandler);
Primiano Tuccie76e81a2014-07-29 16:38:33 +0100583 if (pid <= 0) throw new Exception("Failed to start the relro file creator process");
Primiano Tucci810c0522014-07-25 18:03:16 +0100584 } catch (Throwable t) {
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +0100585 // Log and discard errors as we must not crash the system server.
Primiano Tucci810c0522014-07-25 18:03:16 +0100586 Log.e(LOGTAG, "error starting relro file creator for abi " + abi, t);
587 crashHandler.run();
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +0100588 }
589 }
590
591 private static class RelroFileCreator {
592 // Called in an unprivileged child process to create the relro file.
593 public static void main(String[] args) {
Primiano Tucci161536b2014-07-28 18:51:45 +0100594 boolean result = false;
595 boolean is64Bit = VMRuntime.getRuntime().is64Bit();
Primiano Tucci1b7977b2014-07-25 19:19:32 +0100596 try{
597 if (args.length != 2 || args[0] == null || args[1] == null) {
598 Log.e(LOGTAG, "Invalid RelroFileCreator args: " + Arrays.toString(args));
599 return;
600 }
Primiano Tucci1b7977b2014-07-25 19:19:32 +0100601 Log.v(LOGTAG, "RelroFileCreator (64bit = " + is64Bit + "), " +
602 " 32-bit lib: " + args[0] + ", 64-bit lib: " + args[1]);
603 if (!sAddressSpaceReserved) {
604 Log.e(LOGTAG, "can't create relro file; address space not reserved");
605 return;
606 }
Primiano Tucci161536b2014-07-28 18:51:45 +0100607 result = nativeCreateRelroFile(args[0] /* path32 */,
608 args[1] /* path64 */,
609 CHROMIUM_WEBVIEW_NATIVE_RELRO_32,
610 CHROMIUM_WEBVIEW_NATIVE_RELRO_64);
Primiano Tuccie76e81a2014-07-29 16:38:33 +0100611 if (result && DEBUG) Log.v(LOGTAG, "created relro file");
Primiano Tucci161536b2014-07-28 18:51:45 +0100612 } finally {
613 // We must do our best to always notify the update service, even if something fails.
Primiano Tucci1b7977b2014-07-25 19:19:32 +0100614 try {
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000615 getUpdateService().notifyRelroCreationCompleted();
Primiano Tucci1b7977b2014-07-25 19:19:32 +0100616 } catch (RemoteException e) {
617 Log.e(LOGTAG, "error notifying update service", e);
618 }
Primiano Tucci161536b2014-07-28 18:51:45 +0100619
620 if (!result) Log.e(LOGTAG, "failed to create relro file");
621
Primiano Tucci1b7977b2014-07-25 19:19:32 +0100622 // Must explicitly exit or else this process will just sit around after we return.
623 System.exit(0);
624 }
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +0100625 }
626 }
627
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000628 // Assumes that we have waited for relro creation and set sPackageInfo
Torne (Richard Coles)1a904122016-03-14 13:45:55 +0000629 private static int loadNativeLibrary(ClassLoader clazzLoader) {
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +0100630 if (!sAddressSpaceReserved) {
631 Log.e(LOGTAG, "can't load with relro file; address space not reserved");
Gustav Sennton85edb6c2015-04-15 11:54:20 +0100632 return LIBLOAD_ADDRESS_SPACE_NOT_RESERVED;
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +0100633 }
634
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000635 String[] args = getWebViewNativeLibraryPaths(sPackageInfo);
636 int result = nativeLoadWithRelroFile(args[0] /* path32 */,
Torne (Richard Coles)1a904122016-03-14 13:45:55 +0000637 args[1] /* path64 */,
638 CHROMIUM_WEBVIEW_NATIVE_RELRO_32,
639 CHROMIUM_WEBVIEW_NATIVE_RELRO_64,
640 clazzLoader);
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000641 if (result != LIBLOAD_SUCCESS) {
642 Log.w(LOGTAG, "failed to load with relro file, proceeding without");
643 } else if (DEBUG) {
644 Log.v(LOGTAG, "loaded with relro file");
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +0100645 }
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000646 return result;
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +0100647 }
648
Gustav Sennton1c177d82016-03-29 20:43:11 +0100649 private static String WEBVIEW_UPDATE_SERVICE_NAME = "webviewupdate";
650
651 /** @hide */
652 public static IWebViewUpdateService getUpdateService() {
653 return IWebViewUpdateService.Stub.asInterface(
654 ServiceManager.getService(WEBVIEW_UPDATE_SERVICE_NAME));
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +0100655 }
656
Ben Murdoch5ced5022014-07-28 15:57:00 +0100657 private static native boolean nativeReserveAddressSpace(long addressSpaceToReserve);
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +0100658 private static native boolean nativeCreateRelroFile(String lib32, String lib64,
659 String relro32, String relro64);
Gustav Sennton85edb6c2015-04-15 11:54:20 +0100660 private static native int nativeLoadWithRelroFile(String lib32, String lib64,
Torne (Richard Coles)1a904122016-03-14 13:45:55 +0000661 String relro32, String relro64,
662 ClassLoader clazzLoader);
Jonathan Dixond3101b12012-04-12 20:51:51 +0100663}