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.NonNull; 20 import android.annotation.SystemApi; 21 import android.annotation.UptimeMillisLong; 22 import android.app.ActivityManager; 23 import android.app.AppGlobals; 24 import android.app.Application; 25 import android.compat.annotation.UnsupportedAppUsage; 26 import android.content.Context; 27 import android.content.pm.ApplicationInfo; 28 import android.content.pm.PackageInfo; 29 import android.content.pm.PackageManager; 30 import android.content.pm.Signature; 31 import android.os.Build; 32 import android.os.RemoteException; 33 import android.os.ServiceManager; 34 import android.os.SystemClock; 35 import android.os.Trace; 36 import android.util.AndroidRuntimeException; 37 import android.util.ArraySet; 38 import android.util.Log; 39 40 import java.io.File; 41 import java.lang.reflect.Method; 42 43 /** 44 * Top level factory, used creating all the main WebView implementation classes. 45 * 46 * @hide 47 */ 48 @SystemApi 49 public final class WebViewFactory { 50 51 // visible for WebViewZygoteInit to look up the class by reflection and call preloadInZygote. 52 /** @hide */ 53 private static final String CHROMIUM_WEBVIEW_FACTORY = 54 "com.android.webview.chromium.WebViewChromiumFactoryProviderForT"; 55 56 private static final String CHROMIUM_WEBVIEW_FACTORY_METHOD = "create"; 57 58 private static final String LOGTAG = "WebViewFactory"; 59 60 private static final boolean DEBUG = false; 61 62 // Cache the factory both for efficiency, and ensure any one process gets all webviews from the 63 // same provider. 64 @UnsupportedAppUsage 65 private static WebViewFactoryProvider sProviderInstance; 66 private static final Object sProviderLock = new Object(); 67 @UnsupportedAppUsage 68 private static PackageInfo sPackageInfo; 69 private static Boolean sWebViewSupported; 70 private static boolean sWebViewDisabled; 71 private static String sDataDirectorySuffix; // stored here so it can be set without loading WV 72 73 // Error codes for loadWebViewNativeLibraryFromPackage 74 public static final int LIBLOAD_SUCCESS = 0; 75 public static final int LIBLOAD_WRONG_PACKAGE_NAME = 1; 76 public static final int LIBLOAD_ADDRESS_SPACE_NOT_RESERVED = 2; 77 78 // error codes for waiting for WebView preparation 79 public static final int LIBLOAD_FAILED_WAITING_FOR_RELRO = 3; 80 public static final int LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES = 4; 81 82 // native relro loading error codes 83 public static final int LIBLOAD_FAILED_TO_OPEN_RELRO_FILE = 5; 84 public static final int LIBLOAD_FAILED_TO_LOAD_LIBRARY = 6; 85 public static final int LIBLOAD_FAILED_JNI_CALL = 7; 86 87 // more error codes for waiting for WebView preparation 88 public static final int LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN = 8; 89 90 // error for namespace lookup 91 public static final int LIBLOAD_FAILED_TO_FIND_NAMESPACE = 10; 92 93 /** 94 * Stores the timestamps at which various WebView startup events occurred in this process. 95 */ 96 public static class StartupTimestamps { 97 long mWebViewLoadStart; 98 long mCreateContextStart; 99 long mCreateContextEnd; 100 long mAddAssetsStart; 101 long mAddAssetsEnd; 102 long mGetClassLoaderStart; 103 long mGetClassLoaderEnd; 104 long mNativeLoadStart; 105 long mNativeLoadEnd; 106 long mProviderClassForNameStart; 107 long mProviderClassForNameEnd; 108 StartupTimestamps()109 StartupTimestamps() {} 110 111 /** When the overall WebView provider load began. */ 112 @UptimeMillisLong getWebViewLoadStart()113 public long getWebViewLoadStart() { 114 return mWebViewLoadStart; 115 } 116 117 /** Before creating the WebView APK Context. */ 118 @UptimeMillisLong getCreateContextStart()119 public long getCreateContextStart() { 120 return mCreateContextStart; 121 } 122 123 /** After creating the WebView APK Context. */ 124 @UptimeMillisLong getCreateContextEnd()125 public long getCreateContextEnd() { 126 return mCreateContextEnd; 127 } 128 129 /** Before adding WebView assets to AssetManager. */ 130 @UptimeMillisLong getAddAssetsStart()131 public long getAddAssetsStart() { 132 return mAddAssetsStart; 133 } 134 135 /** After adding WebView assets to AssetManager. */ 136 @UptimeMillisLong getAddAssetsEnd()137 public long getAddAssetsEnd() { 138 return mAddAssetsEnd; 139 } 140 141 /** Before creating the WebView ClassLoader. */ 142 @UptimeMillisLong getGetClassLoaderStart()143 public long getGetClassLoaderStart() { 144 return mGetClassLoaderStart; 145 } 146 147 /** After creating the WebView ClassLoader. */ 148 @UptimeMillisLong getGetClassLoaderEnd()149 public long getGetClassLoaderEnd() { 150 return mGetClassLoaderEnd; 151 } 152 153 /** Before preloading the WebView native library. */ 154 @UptimeMillisLong getNativeLoadStart()155 public long getNativeLoadStart() { 156 return mNativeLoadStart; 157 } 158 159 /** After preloading the WebView native library. */ 160 @UptimeMillisLong getNativeLoadEnd()161 public long getNativeLoadEnd() { 162 return mNativeLoadEnd; 163 } 164 165 /** Before looking up the WebView provider class. */ 166 @UptimeMillisLong getProviderClassForNameStart()167 public long getProviderClassForNameStart() { 168 return mProviderClassForNameStart; 169 } 170 171 /** After looking up the WebView provider class. */ 172 @UptimeMillisLong getProviderClassForNameEnd()173 public long getProviderClassForNameEnd() { 174 return mProviderClassForNameEnd; 175 } 176 } 177 178 static final StartupTimestamps sTimestamps = new StartupTimestamps(); 179 180 @NonNull getStartupTimestamps()181 static StartupTimestamps getStartupTimestamps() { 182 return sTimestamps; 183 } 184 getWebViewPreparationErrorReason(int error)185 private static String getWebViewPreparationErrorReason(int error) { 186 switch (error) { 187 case LIBLOAD_FAILED_WAITING_FOR_RELRO: 188 return "Time out waiting for Relro files being created"; 189 case LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES: 190 return "No WebView installed"; 191 case LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN: 192 return "Crashed for unknown reason"; 193 } 194 return "Unknown"; 195 } 196 197 static class MissingWebViewPackageException extends Exception { MissingWebViewPackageException(String message)198 public MissingWebViewPackageException(String message) { super(message); } MissingWebViewPackageException(Exception e)199 public MissingWebViewPackageException(Exception e) { super(e); } 200 } 201 isWebViewSupported()202 private static boolean isWebViewSupported() { 203 // No lock; this is a benign race as Boolean's state is final and the PackageManager call 204 // will always return the same value. 205 if (sWebViewSupported == null) { 206 sWebViewSupported = AppGlobals.getInitialApplication().getPackageManager() 207 .hasSystemFeature(PackageManager.FEATURE_WEBVIEW); 208 } 209 return sWebViewSupported; 210 } 211 212 /** 213 * @hide 214 */ disableWebView()215 static void disableWebView() { 216 synchronized (sProviderLock) { 217 if (sProviderInstance != null) { 218 throw new IllegalStateException( 219 "Can't disable WebView: WebView already initialized"); 220 } 221 sWebViewDisabled = true; 222 } 223 } 224 225 /** 226 * @hide 227 */ setDataDirectorySuffix(String suffix)228 static void setDataDirectorySuffix(String suffix) { 229 synchronized (sProviderLock) { 230 if (sProviderInstance != null) { 231 throw new IllegalStateException( 232 "Can't set data directory suffix: WebView already initialized"); 233 } 234 if (suffix.indexOf(File.separatorChar) >= 0) { 235 throw new IllegalArgumentException("Suffix " + suffix 236 + " contains a path separator"); 237 } 238 sDataDirectorySuffix = suffix; 239 } 240 } 241 242 /** 243 * @hide 244 */ getDataDirectorySuffix()245 static String getDataDirectorySuffix() { 246 synchronized (sProviderLock) { 247 return sDataDirectorySuffix; 248 } 249 } 250 251 /** 252 * @hide 253 */ getWebViewLibrary(ApplicationInfo ai)254 public static String getWebViewLibrary(ApplicationInfo ai) { 255 if (ai.metaData != null) 256 return ai.metaData.getString("com.android.webview.WebViewLibrary"); 257 return null; 258 } 259 getLoadedPackageInfo()260 public static PackageInfo getLoadedPackageInfo() { 261 synchronized (sProviderLock) { 262 return sPackageInfo; 263 } 264 } 265 266 /** 267 * @hide 268 */ getWebViewProviderClass(ClassLoader clazzLoader)269 public static Class<WebViewFactoryProvider> getWebViewProviderClass(ClassLoader clazzLoader) 270 throws ClassNotFoundException { 271 return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY, 272 true, clazzLoader); 273 } 274 275 /** 276 * Load the native library for the given package name if that package 277 * name is the same as the one providing the webview. 278 */ loadWebViewNativeLibraryFromPackage(String packageName, ClassLoader clazzLoader)279 public static int loadWebViewNativeLibraryFromPackage(String packageName, 280 ClassLoader clazzLoader) { 281 if (!isWebViewSupported()) { 282 return LIBLOAD_WRONG_PACKAGE_NAME; 283 } 284 285 WebViewProviderResponse response = null; 286 try { 287 response = getUpdateService().waitForAndGetProvider(); 288 } catch (RemoteException e) { 289 Log.e(LOGTAG, "error waiting for relro creation", e); 290 return LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN; 291 } 292 293 294 if (response.status != LIBLOAD_SUCCESS 295 && response.status != LIBLOAD_FAILED_WAITING_FOR_RELRO) { 296 return response.status; 297 } 298 if (!response.packageInfo.packageName.equals(packageName)) { 299 return LIBLOAD_WRONG_PACKAGE_NAME; 300 } 301 302 PackageManager packageManager = AppGlobals.getInitialApplication().getPackageManager(); 303 String libraryFileName; 304 try { 305 PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 306 PackageManager.GET_META_DATA | PackageManager.MATCH_DEBUG_TRIAGED_MISSING); 307 libraryFileName = getWebViewLibrary(packageInfo.applicationInfo); 308 } catch (PackageManager.NameNotFoundException e) { 309 Log.e(LOGTAG, "Couldn't find package " + packageName); 310 return LIBLOAD_WRONG_PACKAGE_NAME; 311 } 312 313 int loadNativeRet = WebViewLibraryLoader.loadNativeLibrary(clazzLoader, libraryFileName); 314 // If we failed waiting for relro we want to return that fact even if we successfully 315 // load the relro file. 316 if (loadNativeRet == LIBLOAD_SUCCESS) return response.status; 317 return loadNativeRet; 318 } 319 320 @UnsupportedAppUsage getProvider()321 static WebViewFactoryProvider getProvider() { 322 synchronized (sProviderLock) { 323 // For now the main purpose of this function (and the factory abstraction) is to keep 324 // us honest and minimize usage of WebView internals when binding the proxy. 325 if (sProviderInstance != null) return sProviderInstance; 326 327 sTimestamps.mWebViewLoadStart = SystemClock.uptimeMillis(); 328 final int uid = android.os.Process.myUid(); 329 if (uid == android.os.Process.ROOT_UID || uid == android.os.Process.SYSTEM_UID 330 || uid == android.os.Process.PHONE_UID || uid == android.os.Process.NFC_UID 331 || uid == android.os.Process.BLUETOOTH_UID) { 332 throw new UnsupportedOperationException( 333 "For security reasons, WebView is not allowed in privileged processes"); 334 } 335 336 if (!isWebViewSupported()) { 337 // Device doesn't support WebView; don't try to load it, just throw. 338 throw new UnsupportedOperationException(); 339 } 340 341 if (sWebViewDisabled) { 342 throw new IllegalStateException( 343 "WebView.disableWebView() was called: WebView is disabled"); 344 } 345 346 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getProvider()"); 347 try { 348 Class<WebViewFactoryProvider> providerClass = getProviderClass(); 349 Method staticFactory = providerClass.getMethod( 350 CHROMIUM_WEBVIEW_FACTORY_METHOD, WebViewDelegate.class); 351 352 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactoryProvider invocation"); 353 try { 354 sProviderInstance = (WebViewFactoryProvider) 355 staticFactory.invoke(null, new WebViewDelegate()); 356 if (DEBUG) Log.v(LOGTAG, "Loaded provider: " + sProviderInstance); 357 return sProviderInstance; 358 } finally { 359 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW); 360 } 361 } catch (Exception e) { 362 Log.e(LOGTAG, "error instantiating provider", e); 363 throw new AndroidRuntimeException(e); 364 } finally { 365 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW); 366 } 367 } 368 } 369 370 /** 371 * Returns {@code true} if the signatures match, {@code false} otherwise 372 */ signaturesEquals(Signature[] s1, Signature[] s2)373 private static boolean signaturesEquals(Signature[] s1, Signature[] s2) { 374 if (s1 == null) { 375 return s2 == null; 376 } 377 if (s2 == null) return false; 378 379 ArraySet<Signature> set1 = new ArraySet<>(); 380 for(Signature signature : s1) { 381 set1.add(signature); 382 } 383 ArraySet<Signature> set2 = new ArraySet<>(); 384 for(Signature signature : s2) { 385 set2.add(signature); 386 } 387 return set1.equals(set2); 388 } 389 390 // Throws MissingWebViewPackageException on failure verifyPackageInfo(PackageInfo chosen, PackageInfo toUse)391 private static void verifyPackageInfo(PackageInfo chosen, PackageInfo toUse) 392 throws MissingWebViewPackageException { 393 if (!chosen.packageName.equals(toUse.packageName)) { 394 throw new MissingWebViewPackageException("Failed to verify WebView provider, " 395 + "packageName mismatch, expected: " 396 + chosen.packageName + " actual: " + toUse.packageName); 397 } 398 if (chosen.getLongVersionCode() > toUse.getLongVersionCode()) { 399 throw new MissingWebViewPackageException("Failed to verify WebView provider, " 400 + "version code is lower than expected: " + chosen.getLongVersionCode() 401 + " actual: " + toUse.getLongVersionCode()); 402 } 403 if (getWebViewLibrary(toUse.applicationInfo) == null) { 404 throw new MissingWebViewPackageException("Tried to load an invalid WebView provider: " 405 + toUse.packageName); 406 } 407 if (!signaturesEquals(chosen.signatures, toUse.signatures)) { 408 throw new MissingWebViewPackageException("Failed to verify WebView provider, " 409 + "signature mismatch"); 410 } 411 } 412 413 @UnsupportedAppUsage getWebViewContextAndSetProvider()414 private static Context getWebViewContextAndSetProvider() throws MissingWebViewPackageException { 415 Application initialApplication = AppGlobals.getInitialApplication(); 416 try { 417 WebViewProviderResponse response = null; 418 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, 419 "WebViewUpdateService.waitForAndGetProvider()"); 420 try { 421 response = getUpdateService().waitForAndGetProvider(); 422 } finally { 423 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW); 424 } 425 if (response.status != LIBLOAD_SUCCESS 426 && response.status != LIBLOAD_FAILED_WAITING_FOR_RELRO) { 427 throw new MissingWebViewPackageException("Failed to load WebView provider: " 428 + getWebViewPreparationErrorReason(response.status)); 429 } 430 // Register to be killed before fetching package info - so that we will be 431 // killed if the package info goes out-of-date. 432 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "ActivityManager.addPackageDependency()"); 433 try { 434 ActivityManager.getService().addPackageDependency( 435 response.packageInfo.packageName); 436 } finally { 437 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW); 438 } 439 // Fetch package info and verify it against the chosen package 440 PackageInfo newPackageInfo = null; 441 PackageManager pm = initialApplication.getPackageManager(); 442 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "PackageManager.getPackageInfo()"); 443 try { 444 newPackageInfo = pm.getPackageInfo( 445 response.packageInfo.packageName, 446 PackageManager.GET_SHARED_LIBRARY_FILES 447 | PackageManager.MATCH_DEBUG_TRIAGED_MISSING 448 // Make sure that we fetch the current provider even if its not 449 // installed for the current user 450 | PackageManager.MATCH_UNINSTALLED_PACKAGES 451 // Fetch signatures for verification 452 | PackageManager.GET_SIGNATURES 453 // Get meta-data for meta data flag verification 454 | PackageManager.GET_META_DATA); 455 } finally { 456 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW); 457 } 458 459 // Validate the newly fetched package info, throws MissingWebViewPackageException on 460 // failure 461 verifyPackageInfo(response.packageInfo, newPackageInfo); 462 463 ApplicationInfo ai = newPackageInfo.applicationInfo; 464 465 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, 466 "initialApplication.createApplicationContext"); 467 sTimestamps.mCreateContextStart = SystemClock.uptimeMillis(); 468 try { 469 // Construct an app context to load the Java code into the current app. 470 Context webViewContext = initialApplication.createApplicationContext( 471 ai, 472 Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); 473 sPackageInfo = newPackageInfo; 474 return webViewContext; 475 } finally { 476 sTimestamps.mCreateContextEnd = SystemClock.uptimeMillis(); 477 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW); 478 } 479 } catch (RemoteException | PackageManager.NameNotFoundException e) { 480 throw new MissingWebViewPackageException("Failed to load WebView provider: " + e); 481 } 482 } 483 484 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) getProviderClass()485 private static Class<WebViewFactoryProvider> getProviderClass() { 486 Context webViewContext = null; 487 Application initialApplication = AppGlobals.getInitialApplication(); 488 489 try { 490 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, 491 "WebViewFactory.getWebViewContextAndSetProvider()"); 492 try { 493 webViewContext = getWebViewContextAndSetProvider(); 494 } finally { 495 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW); 496 } 497 Log.i(LOGTAG, "Loading " + sPackageInfo.packageName + " version " + 498 sPackageInfo.versionName + " (code " + sPackageInfo.getLongVersionCode() + ")"); 499 500 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getChromiumProviderClass()"); 501 try { 502 sTimestamps.mAddAssetsStart = SystemClock.uptimeMillis(); 503 for (String newAssetPath : webViewContext.getApplicationInfo().getAllApkPaths()) { 504 initialApplication.getAssets().addAssetPathAsSharedLibrary(newAssetPath); 505 } 506 sTimestamps.mAddAssetsEnd = sTimestamps.mGetClassLoaderStart = 507 SystemClock.uptimeMillis(); 508 ClassLoader clazzLoader = webViewContext.getClassLoader(); 509 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.loadNativeLibrary()"); 510 sTimestamps.mGetClassLoaderEnd = sTimestamps.mNativeLoadStart = 511 SystemClock.uptimeMillis(); 512 WebViewLibraryLoader.loadNativeLibrary(clazzLoader, 513 getWebViewLibrary(sPackageInfo.applicationInfo)); 514 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW); 515 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "Class.forName()"); 516 sTimestamps.mNativeLoadEnd = sTimestamps.mProviderClassForNameStart = 517 SystemClock.uptimeMillis(); 518 try { 519 return getWebViewProviderClass(clazzLoader); 520 } finally { 521 sTimestamps.mProviderClassForNameEnd = SystemClock.uptimeMillis(); 522 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW); 523 } 524 } catch (ClassNotFoundException e) { 525 Log.e(LOGTAG, "error loading provider", e); 526 throw new AndroidRuntimeException(e); 527 } finally { 528 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW); 529 } 530 } catch (MissingWebViewPackageException e) { 531 Log.e(LOGTAG, "Chromium WebView package does not exist", e); 532 throw new AndroidRuntimeException(e); 533 } 534 } 535 536 /** 537 * Perform any WebView loading preparations that must happen in the zygote. 538 * Currently, this means allocating address space to load the real JNI library later. 539 */ prepareWebViewInZygote()540 public static void prepareWebViewInZygote() { 541 try { 542 WebViewLibraryLoader.reserveAddressSpaceInZygote(); 543 } catch (Throwable t) { 544 // Log and discard errors at this stage as we must not crash the zygote. 545 Log.e(LOGTAG, "error preparing native loader", t); 546 } 547 } 548 549 /** 550 * @hide 551 */ onWebViewProviderChanged(PackageInfo packageInfo)552 public static int onWebViewProviderChanged(PackageInfo packageInfo) { 553 int startedRelroProcesses = 0; 554 try { 555 startedRelroProcesses = WebViewLibraryLoader.prepareNativeLibraries(packageInfo); 556 } catch (Throwable t) { 557 // Log and discard errors at this stage as we must not crash the system server. 558 Log.e(LOGTAG, "error preparing webview native library", t); 559 } 560 561 WebViewZygote.onWebViewProviderChanged(packageInfo); 562 563 return startedRelroProcesses; 564 } 565 566 private static String WEBVIEW_UPDATE_SERVICE_NAME = "webviewupdate"; 567 568 /** @hide */ 569 @UnsupportedAppUsage getUpdateService()570 public static IWebViewUpdateService getUpdateService() { 571 if (isWebViewSupported()) { 572 return getUpdateServiceUnchecked(); 573 } else { 574 return null; 575 } 576 } 577 578 /** @hide */ getUpdateServiceUnchecked()579 static IWebViewUpdateService getUpdateServiceUnchecked() { 580 return IWebViewUpdateService.Stub.asInterface( 581 ServiceManager.getService(WEBVIEW_UPDATE_SERVICE_NAME)); 582 } 583 } 584