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