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