1 /* 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 17 package android.webkit; 18 19 import android.annotation.SystemApi; 20 import android.app.ActivityManagerInternal; 21 import android.app.ActivityManagerNative; 22 import android.app.AppGlobals; 23 import android.app.Application; 24 import android.content.Context; 25 import android.content.pm.ApplicationInfo; 26 import android.content.pm.PackageInfo; 27 import android.content.pm.PackageManager; 28 import android.content.pm.Signature; 29 import android.os.Build; 30 import android.os.Process; 31 import android.os.RemoteException; 32 import android.os.ServiceManager; 33 import android.os.StrictMode; 34 import android.os.SystemProperties; 35 import android.os.Trace; 36 import android.text.TextUtils; 37 import android.util.AndroidRuntimeException; 38 import android.util.ArraySet; 39 import android.util.Log; 40 41 import com.android.server.LocalServices; 42 43 import dalvik.system.VMRuntime; 44 45 import java.io.File; 46 import java.io.IOException; 47 import java.util.Arrays; 48 import java.util.zip.ZipEntry; 49 import java.util.zip.ZipFile; 50 51 /** 52 * Top level factory, used creating all the main WebView implementation classes. 53 * 54 * @hide 55 */ 56 @SystemApi 57 public final class WebViewFactory { 58 59 private static final String CHROMIUM_WEBVIEW_FACTORY = 60 "com.android.webview.chromium.WebViewChromiumFactoryProvider"; 61 62 private static final String NULL_WEBVIEW_FACTORY = 63 "com.android.webview.nullwebview.NullWebViewFactoryProvider"; 64 65 private static final String CHROMIUM_WEBVIEW_NATIVE_RELRO_32 = 66 "/data/misc/shared_relro/libwebviewchromium32.relro"; 67 private static final String CHROMIUM_WEBVIEW_NATIVE_RELRO_64 = 68 "/data/misc/shared_relro/libwebviewchromium64.relro"; 69 70 public static final String CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY = 71 "persist.sys.webview.vmsize"; 72 private static final long CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES = 100 * 1024 * 1024; 73 74 private static final String LOGTAG = "WebViewFactory"; 75 76 private static final boolean DEBUG = false; 77 78 // Cache the factory both for efficiency, and ensure any one process gets all webviews from the 79 // same provider. 80 private static WebViewFactoryProvider sProviderInstance; 81 private static final Object sProviderLock = new Object(); 82 private static boolean sAddressSpaceReserved = false; 83 private static PackageInfo sPackageInfo; 84 85 // Error codes for loadWebViewNativeLibraryFromPackage 86 public static final int LIBLOAD_SUCCESS = 0; 87 public static final int LIBLOAD_WRONG_PACKAGE_NAME = 1; 88 public static final int LIBLOAD_ADDRESS_SPACE_NOT_RESERVED = 2; 89 90 // error codes for waiting for WebView preparation 91 public static final int LIBLOAD_FAILED_WAITING_FOR_RELRO = 3; 92 public static final int LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES = 4; 93 94 // native relro loading error codes 95 public static final int LIBLOAD_FAILED_TO_OPEN_RELRO_FILE = 5; 96 public static final int LIBLOAD_FAILED_TO_LOAD_LIBRARY = 6; 97 public static final int LIBLOAD_FAILED_JNI_CALL = 7; 98 99 // more error codes for waiting for WebView preparation 100 public static final int LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN = 8; 101 102 // error for namespace lookup 103 public static final int LIBLOAD_FAILED_TO_FIND_NAMESPACE = 10; 104 getWebViewPreparationErrorReason(int error)105 private static String getWebViewPreparationErrorReason(int error) { 106 switch (error) { 107 case LIBLOAD_FAILED_WAITING_FOR_RELRO: 108 return "Time out waiting for Relro files being created"; 109 case LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES: 110 return "No WebView installed"; 111 case LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN: 112 return "Crashed for unknown reason"; 113 } 114 return "Unknown"; 115 } 116 117 /** 118 * @hide 119 */ 120 public static class MissingWebViewPackageException extends AndroidRuntimeException { MissingWebViewPackageException(String message)121 public MissingWebViewPackageException(String message) { super(message); } MissingWebViewPackageException(Exception e)122 public MissingWebViewPackageException(Exception e) { super(e); } 123 } 124 125 /** 126 * @hide 127 */ getWebViewLibrary(ApplicationInfo ai)128 public static String getWebViewLibrary(ApplicationInfo ai) { 129 if (ai.metaData != null) 130 return ai.metaData.getString("com.android.webview.WebViewLibrary"); 131 return null; 132 } 133 getLoadedPackageInfo()134 public static PackageInfo getLoadedPackageInfo() { 135 return sPackageInfo; 136 } 137 138 /** 139 * Load the native library for the given package name iff that package 140 * name is the same as the one providing the webview. 141 */ loadWebViewNativeLibraryFromPackage(String packageName, ClassLoader clazzLoader)142 public static int loadWebViewNativeLibraryFromPackage(String packageName, 143 ClassLoader clazzLoader) { 144 WebViewProviderResponse response = null; 145 try { 146 response = getUpdateService().waitForAndGetProvider(); 147 } catch (RemoteException e) { 148 Log.e(LOGTAG, "error waiting for relro creation", e); 149 return LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN; 150 } 151 152 153 if (response.status != LIBLOAD_SUCCESS 154 && response.status != LIBLOAD_FAILED_WAITING_FOR_RELRO) { 155 return response.status; 156 } 157 if (!response.packageInfo.packageName.equals(packageName)) { 158 return LIBLOAD_WRONG_PACKAGE_NAME; 159 } 160 161 PackageManager packageManager = AppGlobals.getInitialApplication().getPackageManager(); 162 PackageInfo packageInfo; 163 try { 164 packageInfo = packageManager.getPackageInfo(packageName, 165 PackageManager.GET_META_DATA | PackageManager.MATCH_DEBUG_TRIAGED_MISSING); 166 } catch (PackageManager.NameNotFoundException e) { 167 Log.e(LOGTAG, "Couldn't find package " + packageName); 168 return LIBLOAD_WRONG_PACKAGE_NAME; 169 } 170 sPackageInfo = packageInfo; 171 172 int loadNativeRet = loadNativeLibrary(clazzLoader); 173 // If we failed waiting for relro we want to return that fact even if we successfully load 174 // the relro file. 175 if (loadNativeRet == LIBLOAD_SUCCESS) return response.status; 176 return loadNativeRet; 177 } 178 getProvider()179 static WebViewFactoryProvider getProvider() { 180 synchronized (sProviderLock) { 181 // For now the main purpose of this function (and the factory abstraction) is to keep 182 // us honest and minimize usage of WebView internals when binding the proxy. 183 if (sProviderInstance != null) return sProviderInstance; 184 185 final int uid = android.os.Process.myUid(); 186 if (uid == android.os.Process.ROOT_UID || uid == android.os.Process.SYSTEM_UID) { 187 throw new UnsupportedOperationException( 188 "For security reasons, WebView is not allowed in privileged processes"); 189 } 190 191 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); 192 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getProvider()"); 193 try { 194 Class<WebViewFactoryProvider> providerClass = getProviderClass(); 195 196 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "providerClass.newInstance()"); 197 try { 198 sProviderInstance = providerClass.getConstructor(WebViewDelegate.class) 199 .newInstance(new WebViewDelegate()); 200 if (DEBUG) Log.v(LOGTAG, "Loaded provider: " + sProviderInstance); 201 return sProviderInstance; 202 } catch (Exception e) { 203 Log.e(LOGTAG, "error instantiating provider", e); 204 throw new AndroidRuntimeException(e); 205 } finally { 206 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW); 207 } 208 } finally { 209 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW); 210 StrictMode.setThreadPolicy(oldPolicy); 211 } 212 } 213 } 214 215 /** 216 * Returns true if the signatures match, false otherwise 217 */ signaturesEquals(Signature[] s1, Signature[] s2)218 private static boolean signaturesEquals(Signature[] s1, Signature[] s2) { 219 if (s1 == null) { 220 return s2 == null; 221 } 222 if (s2 == null) return false; 223 224 ArraySet<Signature> set1 = new ArraySet<>(); 225 for(Signature signature : s1) { 226 set1.add(signature); 227 } 228 ArraySet<Signature> set2 = new ArraySet<>(); 229 for(Signature signature : s2) { 230 set2.add(signature); 231 } 232 return set1.equals(set2); 233 } 234 235 // Throws MissingWebViewPackageException on failure verifyPackageInfo(PackageInfo chosen, PackageInfo toUse)236 private static void verifyPackageInfo(PackageInfo chosen, PackageInfo toUse) { 237 if (!chosen.packageName.equals(toUse.packageName)) { 238 throw new MissingWebViewPackageException("Failed to verify WebView provider, " 239 + "packageName mismatch, expected: " 240 + chosen.packageName + " actual: " + toUse.packageName); 241 } 242 if (chosen.versionCode > toUse.versionCode) { 243 throw new MissingWebViewPackageException("Failed to verify WebView provider, " 244 + "version code is lower than expected: " + chosen.versionCode 245 + " actual: " + toUse.versionCode); 246 } 247 if (getWebViewLibrary(toUse.applicationInfo) == null) { 248 throw new MissingWebViewPackageException("Tried to load an invalid WebView provider: " 249 + toUse.packageName); 250 } 251 if (!signaturesEquals(chosen.signatures, toUse.signatures)) { 252 throw new MissingWebViewPackageException("Failed to verify WebView provider, " 253 + "signature mismatch"); 254 } 255 } 256 getWebViewContextAndSetProvider()257 private static Context getWebViewContextAndSetProvider() { 258 Application initialApplication = AppGlobals.getInitialApplication(); 259 try { 260 WebViewProviderResponse response = null; 261 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, 262 "WebViewUpdateService.waitForAndGetProvider()"); 263 try { 264 response = getUpdateService().waitForAndGetProvider(); 265 } finally { 266 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW); 267 } 268 if (response.status != LIBLOAD_SUCCESS 269 && response.status != LIBLOAD_FAILED_WAITING_FOR_RELRO) { 270 throw new MissingWebViewPackageException("Failed to load WebView provider: " 271 + getWebViewPreparationErrorReason(response.status)); 272 } 273 // Register to be killed before fetching package info - so that we will be 274 // killed if the package info goes out-of-date. 275 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "ActivityManager.addPackageDependency()"); 276 try { 277 ActivityManagerNative.getDefault().addPackageDependency( 278 response.packageInfo.packageName); 279 } finally { 280 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW); 281 } 282 // Fetch package info and verify it against the chosen package 283 PackageInfo newPackageInfo = null; 284 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "PackageManager.getPackageInfo()"); 285 try { 286 newPackageInfo = initialApplication.getPackageManager().getPackageInfo( 287 response.packageInfo.packageName, 288 PackageManager.GET_SHARED_LIBRARY_FILES 289 | PackageManager.MATCH_DEBUG_TRIAGED_MISSING 290 // Make sure that we fetch the current provider even if its not 291 // installed for the current user 292 | PackageManager.MATCH_UNINSTALLED_PACKAGES 293 // Fetch signatures for verification 294 | PackageManager.GET_SIGNATURES 295 // Get meta-data for meta data flag verification 296 | PackageManager.GET_META_DATA); 297 } finally { 298 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW); 299 } 300 301 // Validate the newly fetched package info, throws MissingWebViewPackageException on 302 // failure 303 verifyPackageInfo(response.packageInfo, newPackageInfo); 304 305 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, 306 "initialApplication.createApplicationContext"); 307 try { 308 // Construct an app context to load the Java code into the current app. 309 Context webViewContext = initialApplication.createApplicationContext( 310 newPackageInfo.applicationInfo, 311 Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); 312 sPackageInfo = newPackageInfo; 313 return webViewContext; 314 } finally { 315 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW); 316 } 317 } catch (RemoteException | PackageManager.NameNotFoundException e) { 318 throw new MissingWebViewPackageException("Failed to load WebView provider: " + e); 319 } 320 } 321 getProviderClass()322 private static Class<WebViewFactoryProvider> getProviderClass() { 323 Context webViewContext = null; 324 Application initialApplication = AppGlobals.getInitialApplication(); 325 326 try { 327 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, 328 "WebViewFactory.getWebViewContextAndSetProvider()"); 329 try { 330 webViewContext = getWebViewContextAndSetProvider(); 331 } finally { 332 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW); 333 } 334 Log.i(LOGTAG, "Loading " + sPackageInfo.packageName + " version " + 335 sPackageInfo.versionName + " (code " + sPackageInfo.versionCode + ")"); 336 337 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getChromiumProviderClass()"); 338 try { 339 initialApplication.getAssets().addAssetPathAsSharedLibrary( 340 webViewContext.getApplicationInfo().sourceDir); 341 ClassLoader clazzLoader = webViewContext.getClassLoader(); 342 343 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.loadNativeLibrary()"); 344 loadNativeLibrary(clazzLoader); 345 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW); 346 347 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "Class.forName()"); 348 try { 349 return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY, 350 true, clazzLoader); 351 } finally { 352 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW); 353 } 354 } catch (ClassNotFoundException e) { 355 Log.e(LOGTAG, "error loading provider", e); 356 throw new AndroidRuntimeException(e); 357 } finally { 358 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW); 359 } 360 } catch (MissingWebViewPackageException e) { 361 // If the package doesn't exist, then try loading the null WebView instead. 362 // If that succeeds, then this is a device without WebView support; if it fails then 363 // swallow the failure, complain that the real WebView is missing and rethrow the 364 // original exception. 365 try { 366 return (Class<WebViewFactoryProvider>) Class.forName(NULL_WEBVIEW_FACTORY); 367 } catch (ClassNotFoundException e2) { 368 // Ignore. 369 } 370 Log.e(LOGTAG, "Chromium WebView package does not exist", e); 371 throw new AndroidRuntimeException(e); 372 } 373 } 374 375 /** 376 * Perform any WebView loading preparations that must happen in the zygote. 377 * Currently, this means allocating address space to load the real JNI library later. 378 */ prepareWebViewInZygote()379 public static void prepareWebViewInZygote() { 380 try { 381 System.loadLibrary("webviewchromium_loader"); 382 long addressSpaceToReserve = 383 SystemProperties.getLong(CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY, 384 CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES); 385 sAddressSpaceReserved = nativeReserveAddressSpace(addressSpaceToReserve); 386 387 if (sAddressSpaceReserved) { 388 if (DEBUG) { 389 Log.v(LOGTAG, "address space reserved: " + addressSpaceToReserve + " bytes"); 390 } 391 } else { 392 Log.e(LOGTAG, "reserving " + addressSpaceToReserve + 393 " bytes of address space failed"); 394 } 395 } catch (Throwable t) { 396 // Log and discard errors at this stage as we must not crash the zygote. 397 Log.e(LOGTAG, "error preparing native loader", t); 398 } 399 } 400 prepareWebViewInSystemServer(String[] nativeLibraryPaths)401 private static int prepareWebViewInSystemServer(String[] nativeLibraryPaths) { 402 if (DEBUG) Log.v(LOGTAG, "creating relro files"); 403 int numRelros = 0; 404 405 // We must always trigger createRelRo regardless of the value of nativeLibraryPaths. Any 406 // unexpected values will be handled there to ensure that we trigger notifying any process 407 // waiting on relro creation. 408 if (Build.SUPPORTED_32_BIT_ABIS.length > 0) { 409 if (DEBUG) Log.v(LOGTAG, "Create 32 bit relro"); 410 createRelroFile(false /* is64Bit */, nativeLibraryPaths); 411 numRelros++; 412 } 413 414 if (Build.SUPPORTED_64_BIT_ABIS.length > 0) { 415 if (DEBUG) Log.v(LOGTAG, "Create 64 bit relro"); 416 createRelroFile(true /* is64Bit */, nativeLibraryPaths); 417 numRelros++; 418 } 419 return numRelros; 420 } 421 422 /** 423 * @hide 424 */ onWebViewProviderChanged(PackageInfo packageInfo)425 public static int onWebViewProviderChanged(PackageInfo packageInfo) { 426 String[] nativeLibs = null; 427 try { 428 nativeLibs = WebViewFactory.getWebViewNativeLibraryPaths(packageInfo); 429 if (nativeLibs != null) { 430 long newVmSize = 0L; 431 432 for (String path : nativeLibs) { 433 if (path == null || TextUtils.isEmpty(path)) continue; 434 if (DEBUG) Log.d(LOGTAG, "Checking file size of " + path); 435 File f = new File(path); 436 if (f.exists()) { 437 newVmSize = Math.max(newVmSize, f.length()); 438 continue; 439 } 440 if (path.contains("!/")) { 441 String[] split = TextUtils.split(path, "!/"); 442 if (split.length == 2) { 443 try (ZipFile z = new ZipFile(split[0])) { 444 ZipEntry e = z.getEntry(split[1]); 445 if (e != null && e.getMethod() == ZipEntry.STORED) { 446 newVmSize = Math.max(newVmSize, e.getSize()); 447 continue; 448 } 449 } 450 catch (IOException e) { 451 Log.e(LOGTAG, "error reading APK file " + split[0] + ", ", e); 452 } 453 } 454 } 455 Log.e(LOGTAG, "error sizing load for " + path); 456 } 457 458 if (DEBUG) { 459 Log.v(LOGTAG, "Based on library size, need " + newVmSize + 460 " bytes of address space."); 461 } 462 // The required memory can be larger than the file on disk (due to .bss), and an 463 // upgraded version of the library will likely be larger, so always attempt to 464 // reserve twice as much as we think to allow for the library to grow during this 465 // boot cycle. 466 newVmSize = Math.max(2 * newVmSize, CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES); 467 Log.d(LOGTAG, "Setting new address space to " + newVmSize); 468 SystemProperties.set(CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY, 469 Long.toString(newVmSize)); 470 } 471 } catch (Throwable t) { 472 // Log and discard errors at this stage as we must not crash the system server. 473 Log.e(LOGTAG, "error preparing webview native library", t); 474 } 475 return prepareWebViewInSystemServer(nativeLibs); 476 } 477 478 // throws MissingWebViewPackageException getLoadFromApkPath(String apkPath, String[] abiList, String nativeLibFileName)479 private static String getLoadFromApkPath(String apkPath, 480 String[] abiList, 481 String nativeLibFileName) { 482 // Search the APK for a native library conforming to a listed ABI. 483 try (ZipFile z = new ZipFile(apkPath)) { 484 for (String abi : abiList) { 485 final String entry = "lib/" + abi + "/" + nativeLibFileName; 486 ZipEntry e = z.getEntry(entry); 487 if (e != null && e.getMethod() == ZipEntry.STORED) { 488 // Return a path formatted for dlopen() load from APK. 489 return apkPath + "!/" + entry; 490 } 491 } 492 } catch (IOException e) { 493 throw new MissingWebViewPackageException(e); 494 } 495 return ""; 496 } 497 498 // throws MissingWebViewPackageException getWebViewNativeLibraryPaths(PackageInfo packageInfo)499 private static String[] getWebViewNativeLibraryPaths(PackageInfo packageInfo) { 500 ApplicationInfo ai = packageInfo.applicationInfo; 501 final String NATIVE_LIB_FILE_NAME = getWebViewLibrary(ai); 502 503 String path32; 504 String path64; 505 boolean primaryArchIs64bit = VMRuntime.is64BitAbi(ai.primaryCpuAbi); 506 if (!TextUtils.isEmpty(ai.secondaryCpuAbi)) { 507 // Multi-arch case. 508 if (primaryArchIs64bit) { 509 // Primary arch: 64-bit, secondary: 32-bit. 510 path64 = ai.nativeLibraryDir; 511 path32 = ai.secondaryNativeLibraryDir; 512 } else { 513 // Primary arch: 32-bit, secondary: 64-bit. 514 path64 = ai.secondaryNativeLibraryDir; 515 path32 = ai.nativeLibraryDir; 516 } 517 } else if (primaryArchIs64bit) { 518 // Single-arch 64-bit. 519 path64 = ai.nativeLibraryDir; 520 path32 = ""; 521 } else { 522 // Single-arch 32-bit. 523 path32 = ai.nativeLibraryDir; 524 path64 = ""; 525 } 526 527 // Form the full paths to the extracted native libraries. 528 // If libraries were not extracted, try load from APK paths instead. 529 if (!TextUtils.isEmpty(path32)) { 530 path32 += "/" + NATIVE_LIB_FILE_NAME; 531 File f = new File(path32); 532 if (!f.exists()) { 533 path32 = getLoadFromApkPath(ai.sourceDir, 534 Build.SUPPORTED_32_BIT_ABIS, 535 NATIVE_LIB_FILE_NAME); 536 } 537 } 538 if (!TextUtils.isEmpty(path64)) { 539 path64 += "/" + NATIVE_LIB_FILE_NAME; 540 File f = new File(path64); 541 if (!f.exists()) { 542 path64 = getLoadFromApkPath(ai.sourceDir, 543 Build.SUPPORTED_64_BIT_ABIS, 544 NATIVE_LIB_FILE_NAME); 545 } 546 } 547 548 if (DEBUG) Log.v(LOGTAG, "Native 32-bit lib: " + path32 + ", 64-bit lib: " + path64); 549 return new String[] { path32, path64 }; 550 } 551 createRelroFile(final boolean is64Bit, String[] nativeLibraryPaths)552 private static void createRelroFile(final boolean is64Bit, String[] nativeLibraryPaths) { 553 final String abi = 554 is64Bit ? Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0]; 555 556 // crashHandler is invoked by the ActivityManagerService when the isolated process crashes. 557 Runnable crashHandler = new Runnable() { 558 @Override 559 public void run() { 560 try { 561 Log.e(LOGTAG, "relro file creator for " + abi + " crashed. Proceeding without"); 562 getUpdateService().notifyRelroCreationCompleted(); 563 } catch (RemoteException e) { 564 Log.e(LOGTAG, "Cannot reach WebViewUpdateService. " + e.getMessage()); 565 } 566 } 567 }; 568 569 try { 570 if (nativeLibraryPaths == null 571 || nativeLibraryPaths[0] == null || nativeLibraryPaths[1] == null) { 572 throw new IllegalArgumentException( 573 "Native library paths to the WebView RelRo process must not be null!"); 574 } 575 int pid = LocalServices.getService(ActivityManagerInternal.class).startIsolatedProcess( 576 RelroFileCreator.class.getName(), nativeLibraryPaths, "WebViewLoader-" + abi, abi, 577 Process.SHARED_RELRO_UID, crashHandler); 578 if (pid <= 0) throw new Exception("Failed to start the relro file creator process"); 579 } catch (Throwable t) { 580 // Log and discard errors as we must not crash the system server. 581 Log.e(LOGTAG, "error starting relro file creator for abi " + abi, t); 582 crashHandler.run(); 583 } 584 } 585 586 private static class RelroFileCreator { 587 // Called in an unprivileged child process to create the relro file. main(String[] args)588 public static void main(String[] args) { 589 boolean result = false; 590 boolean is64Bit = VMRuntime.getRuntime().is64Bit(); 591 try{ 592 if (args.length != 2 || args[0] == null || args[1] == null) { 593 Log.e(LOGTAG, "Invalid RelroFileCreator args: " + Arrays.toString(args)); 594 return; 595 } 596 Log.v(LOGTAG, "RelroFileCreator (64bit = " + is64Bit + "), " + 597 " 32-bit lib: " + args[0] + ", 64-bit lib: " + args[1]); 598 if (!sAddressSpaceReserved) { 599 Log.e(LOGTAG, "can't create relro file; address space not reserved"); 600 return; 601 } 602 result = nativeCreateRelroFile(args[0] /* path32 */, 603 args[1] /* path64 */, 604 CHROMIUM_WEBVIEW_NATIVE_RELRO_32, 605 CHROMIUM_WEBVIEW_NATIVE_RELRO_64); 606 if (result && DEBUG) Log.v(LOGTAG, "created relro file"); 607 } finally { 608 // We must do our best to always notify the update service, even if something fails. 609 try { 610 getUpdateService().notifyRelroCreationCompleted(); 611 } catch (RemoteException e) { 612 Log.e(LOGTAG, "error notifying update service", e); 613 } 614 615 if (!result) Log.e(LOGTAG, "failed to create relro file"); 616 617 // Must explicitly exit or else this process will just sit around after we return. 618 System.exit(0); 619 } 620 } 621 } 622 623 // Assumes that we have waited for relro creation and set sPackageInfo loadNativeLibrary(ClassLoader clazzLoader)624 private static int loadNativeLibrary(ClassLoader clazzLoader) { 625 if (!sAddressSpaceReserved) { 626 Log.e(LOGTAG, "can't load with relro file; address space not reserved"); 627 return LIBLOAD_ADDRESS_SPACE_NOT_RESERVED; 628 } 629 630 String[] args = getWebViewNativeLibraryPaths(sPackageInfo); 631 int result = nativeLoadWithRelroFile(args[0] /* path32 */, 632 args[1] /* path64 */, 633 CHROMIUM_WEBVIEW_NATIVE_RELRO_32, 634 CHROMIUM_WEBVIEW_NATIVE_RELRO_64, 635 clazzLoader); 636 if (result != LIBLOAD_SUCCESS) { 637 Log.w(LOGTAG, "failed to load with relro file, proceeding without"); 638 } else if (DEBUG) { 639 Log.v(LOGTAG, "loaded with relro file"); 640 } 641 return result; 642 } 643 644 private static String WEBVIEW_UPDATE_SERVICE_NAME = "webviewupdate"; 645 646 /** @hide */ getUpdateService()647 public static IWebViewUpdateService getUpdateService() { 648 return IWebViewUpdateService.Stub.asInterface( 649 ServiceManager.getService(WEBVIEW_UPDATE_SERVICE_NAME)); 650 } 651 nativeReserveAddressSpace(long addressSpaceToReserve)652 private static native boolean nativeReserveAddressSpace(long addressSpaceToReserve); nativeCreateRelroFile(String lib32, String lib64, String relro32, String relro64)653 private static native boolean nativeCreateRelroFile(String lib32, String lib64, 654 String relro32, String relro64); nativeLoadWithRelroFile(String lib32, String lib64, String relro32, String relro64, ClassLoader clazzLoader)655 private static native int nativeLoadWithRelroFile(String lib32, String lib64, 656 String relro32, String relro64, 657 ClassLoader clazzLoader); 658 } 659