// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package com.android.webview.chromium;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.SystemClock;
import android.os.UserManager;
import android.webkit.CookieManager;
import android.webkit.GeolocationPermissions;
import android.webkit.PacProcessor;
import android.webkit.ServiceWorkerController;
import android.webkit.TokenBindingService;
import android.webkit.TracingController;
import android.webkit.ValueCallback;
import android.webkit.WebStorage;
import android.webkit.WebView;
import android.webkit.WebViewDatabase;
import android.webkit.WebViewDelegate;
import android.webkit.WebViewFactory;
import android.webkit.WebViewFactoryProvider;
import android.webkit.WebViewProvider;
import androidx.annotation.RequiresApi;
import com.android.webview.chromium.SharedStatics.ApiCall;
import org.chromium.android_webview.ApkType;
import org.chromium.android_webview.AwBrowserContext;
import org.chromium.android_webview.AwBrowserMainParts;
import org.chromium.android_webview.AwBrowserProcess;
import org.chromium.android_webview.AwContentsStatics;
import org.chromium.android_webview.AwSettings;
import org.chromium.android_webview.BrowserSafeModeActionList;
import org.chromium.android_webview.ProductConfig;
import org.chromium.android_webview.R;
import org.chromium.android_webview.WebViewChromiumRunQueue;
import org.chromium.android_webview.common.AwSwitches;
import org.chromium.android_webview.common.CommandLineUtil;
import org.chromium.android_webview.common.DeveloperModeUtils;
import org.chromium.android_webview.common.FlagOverrideHelper;
import org.chromium.android_webview.common.Lifetime;
import org.chromium.android_webview.common.ProductionSupportedFlagList;
import org.chromium.android_webview.common.SafeModeController;
import org.chromium.android_webview.variations.FastVariationsSeedSafeModeAction;
import org.chromium.base.BuildInfo;
import org.chromium.base.CommandLine;
import org.chromium.base.ContextUtils;
import org.chromium.base.Log;
import org.chromium.base.PackageUtils;
import org.chromium.base.PathUtils;
import org.chromium.base.StrictModeContext;
import org.chromium.base.ThreadUtils;
import org.chromium.base.TraceEvent;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.metrics.ScopedSysTraceEvent;
import org.chromium.base.version_info.VersionConstants;
import org.chromium.build.BuildConfig;
import org.chromium.build.NativeLibraries;
import org.chromium.components.embedder_support.application.ClassLoaderContextWrapperFactory;
import org.chromium.content_public.browser.LGEmailActionModeWorkaround;
import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
/**
* Entry point to the WebView. The system framework talks to this class to get instances of the
* implementation classes.
*
* <p>The exact initialization process depends on the platform OS level:
* <ul>
*
* <li>On API 21 (no longer supported), the platform invoked a parameterless constructor. Since we
* didn't have a WebViewDelegate instance, this required us to invoke WebViewDelegate methods via
* reflection. This constructor has been removed from the code as we no longer support Android
* 21.</li>
*
* <li>From API 22 through API 25, the platform instead directly calls the constructor with a
* WebViewDelegate parameter (See internal CL http://ag/577188 or the public AOSP cherrypick
* https://r.android.com/114870). API 22 (no longer supported) would fallback to the
* parameterless constructor if the first constructor call throws an exception, however this
* fallback was removed in API 23.</li>
*
* <li>Starting in API 26, the platform calls {@link #create} instead of calling the constructor
* directly (see internal CLs http://ag/1334128 and http://ag/1846560).</li>
*
* <li>From API 27 onward, the platform code is updated during each release to use the {@code
* WebViewChromiumFactoryProviderForX} subclass, where "X" is replaced by the actual platform API
* version (ex. "ForOMR1"). It still invokes the {@link #create} method on the subclass. While the
* OS version is still under development, the "ForX" subclass implements the new platform APIs (in a
* private codebase). Once the APIs for that version have been finalized, we eventually roll these
* implementations into this class and the "ForX" subclass just calls directly into this
* implementation.</li>
*
* </ul>
*/
@SuppressWarnings("deprecation")
@Lifetime.Singleton
public class WebViewChromiumFactoryProvider implements WebViewFactoryProvider {
private static final String TAG = "WVCFactoryProvider";
private static final String CHROMIUM_PREFS_NAME = "WebViewChromiumPrefs";
private static final String VERSION_CODE_PREF = "lastVersionCodeUsed";
private static final String WEBVIEW_CONTEXT_EXPERIMENT_PREF = "useWebViewResourceContext";
private static final String SUPPORT_LIB_GLUE_AND_BOUNDARY_INTERFACE_PREFIX =
"org.chromium.support_lib_";
// This is an ID hardcoded by WebLayer for resources stored in locale splits. See
// WebLayerImpl.java for more info.
private static final int SHARED_LIBRARY_MAX_ID = 36;
// When true, WebView will return Resources from its own Context rather than using the embedding
// app's.
private static boolean sUseWebViewContext;
/**
* This holds objects of classes that are defined in P and above to ensure that run-time class
* verification does not occur until it is actually used for P and above.
*/
@RequiresApi(Build.VERSION_CODES.P)
private static class ObjectHolderForP {
public TracingController mTracingController;
}
private static final Object sSingletonLock = new Object();
private static WebViewChromiumFactoryProvider sSingleton;
private final WebViewChromiumRunQueue mRunQueue =
new WebViewChromiumRunQueue(
() -> {
return WebViewChromiumFactoryProvider.this.mAwInit.hasStarted();
});
/* package */ WebViewChromiumRunQueue getRunQueue() {
return mRunQueue;
}
// We have a 4 second timeout to try to detect deadlocks to detect and aid in debugging
// deadlocks.
// Do not call this method while on the UI thread!
/* package */ void runVoidTaskOnUiThreadBlocking(Runnable r) {
mRunQueue.runVoidTaskOnUiThreadBlocking(r);
}
/* package */ <T> T runOnUiThreadBlocking(Callable<T> c) {
return mRunQueue.runBlockingFuture(new FutureTask<T>(c));
}
/* package */ void addTask(Runnable task) {
mRunQueue.addTask(task);
}
/**
* Class that takes care of chromium lazy initialization.
* This is package-public so that a downstream subclass can access it.
*/
/* package */ WebViewChromiumAwInit mAwInit;
private SharedPreferences mWebViewPrefs;
private WebViewDelegate mWebViewDelegate;
protected boolean mShouldDisableThreadChecking;
// Initialization guarded by mAwInit.getLock()
private Statics mStaticsAdapter;
private boolean mIsSafeModeEnabled;
private ServiceWorkerController mServiceWorkerController;
public class InitInfo {
// Timestamp of init start and duration, used in the
// 'WebView.Startup.CreationTime.Stage1.FactoryInit' trace event.
public long mStartTime;
public long mDuration;
// Timestamp of the framework getProvider() method start and elapsed time until init is
// finished, used in the 'WebView.Startup.CreationTime.TotalFactoryInitTime'
// trace event.
public long mTotalFactoryInitStartTime;
public long mTotalFactoryInitDuration;
}
;
private InitInfo mInitInfo = new InitInfo();
@RequiresApi(Build.VERSION_CODES.P)
private ObjectHolderForP mObjectHolderForP =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.P ? new ObjectHolderForP() : null;
/** Thread-safe way to set the one and only WebViewChromiumFactoryProvider. */
private static void setSingleton(WebViewChromiumFactoryProvider provider) {
synchronized (sSingletonLock) {
if (sSingleton != null) {
throw new RuntimeException(
"WebViewChromiumFactoryProvider should only be set once!");
}
sSingleton = provider;
}
}
/** Thread-safe way to get the one and only WebViewChromiumFactoryProvider. */
static WebViewChromiumFactoryProvider getSingleton() {
synchronized (sSingletonLock) {
if (sSingleton == null) {
throw new RuntimeException("WebViewChromiumFactoryProvider has not been set!");
}
return sSingleton;
}
}
/** Entry point for Android 26 (Oreo) and above. See class docs for initialization details. */
public static WebViewChromiumFactoryProvider create(WebViewDelegate delegate) {
return new WebViewChromiumFactoryProvider(delegate);
}
/**
* Entry point for Android 22 (LMR1) through Android 25 (NMR1). Although this is still invoked
* by {@link #create}, this constructor was invoked directly before {@link #create} was defined.
* See class docs for initialization details.
*/
public WebViewChromiumFactoryProvider(WebViewDelegate delegate) {
initialize(delegate);
}
// Protected to allow downstream to override.
protected WebViewChromiumAwInit createAwInit() {
try (ScopedSysTraceEvent e2 =
ScopedSysTraceEvent.scoped("WebViewChromiumFactoryProvider.createAwInit")) {
return new WebViewChromiumAwInit(this);
}
}
// Protected to allow downstream to override.
protected ContentSettingsAdapter createContentSettingsAdapter(AwSettings settings) {
return new ContentSettingsAdapter(settings);
}
private void deleteContentsOnPackageDowngrade(PackageInfo packageInfo) {
try (ScopedSysTraceEvent e2 =
ScopedSysTraceEvent.scoped(
"WebViewChromiumFactoryProvider.deleteContentsOnPackageDowngrade")) {
// Use shared preference to check for package downgrade.
int lastVersion = mWebViewPrefs.getInt(VERSION_CODE_PREF, 0);
int currentVersion = packageInfo.versionCode;
if (!versionCodeGE(currentVersion, lastVersion)) {
// The WebView package has been downgraded since we last ran in this
// application. Delete the WebView data directory's contents.
String dataDir = PathUtils.getDataDirectory();
Log.i(
TAG,
"WebView package downgraded from "
+ lastVersion
+ " to "
+ currentVersion
+ "; deleting contents of "
+ dataDir);
deleteContents(new File(dataDir));
}
if (lastVersion != currentVersion) {
mWebViewPrefs.edit().putInt(VERSION_CODE_PREF, currentVersion).apply();
}
}
}
/**
* This must not be called until {@link #initialize(WebViewDelegate)} has set mWebViewDelegate.
*/
public void addWebViewAssetPath(Context ctx) {
mWebViewDelegate.addWebViewAssetPath(ctx);
}
void setWebViewContextExperimentValue(boolean enabled) {
if (enabled == sUseWebViewContext) return;
if (enabled) {
mWebViewPrefs.edit().putBoolean(WEBVIEW_CONTEXT_EXPERIMENT_PREF, true).apply();
} else {
mWebViewPrefs.edit().remove(WEBVIEW_CONTEXT_EXPERIMENT_PREF).apply();
}
}
@SuppressWarnings({"NoContextGetApplicationContext", "DiscouragedApi"})
private void initialize(WebViewDelegate webViewDelegate) {
mInitInfo.mStartTime = SystemClock.uptimeMillis();
try (ScopedSysTraceEvent e1 =
ScopedSysTraceEvent.scoped("WebViewChromiumFactoryProvider.initialize")) {
PackageInfo packageInfo;
try (ScopedSysTraceEvent e2 =
ScopedSysTraceEvent.scoped(
"WebViewChromiumFactoryProvider.getLoadedPackageInfo")) {
// The package is used to locate the services for copying crash minidumps and
// requesting variations seeds. So it must be set before initializing variations and
// before a renderer has a chance to crash.
packageInfo = WebViewFactory.getLoadedPackageInfo();
}
AwBrowserProcess.setWebViewPackageName(packageInfo.packageName);
AwBrowserProcess.initializeApkType(packageInfo.applicationInfo);
mAwInit = createAwInit();
mWebViewDelegate = webViewDelegate;
Application application = webViewDelegate.getApplication();
Context ctx = application.getApplicationContext();
// If the application context is DE, but we have credentials, use a CE context instead
try (ScopedSysTraceEvent e2 =
ScopedSysTraceEvent.scoped("WebViewChromiumFactoryProvider.checkStorage")) {
checkStorageIsNotDeviceProtected(application);
} catch (IllegalArgumentException e) {
if (!ctx.getSystemService(UserManager.class).isUserUnlocked()) {
throw e;
}
ctx = ctx.createCredentialProtectedStorageContext();
}
try (ScopedSysTraceEvent e2 =
ScopedSysTraceEvent.scoped("WebViewChromiumFactoryProvider.initCommandLine")) {
// This may take ~20 ms only on userdebug devices.
CommandLineUtil.initCommandLine();
}
try (StrictModeContext ignored = StrictModeContext.allowDiskWrites()) {
// Since N, getSharedPreferences creates the preference dir if it doesn't exist,
// causing a disk write.
mWebViewPrefs = ctx.getSharedPreferences(CHROMIUM_PREFS_NAME, Context.MODE_PRIVATE);
// Read the experiment value and use it to determine which Context to use.
sUseWebViewContext =
mWebViewPrefs.getBoolean(WEBVIEW_CONTEXT_EXPERIMENT_PREF, false)
&& Build.VERSION.SDK_INT <= Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
}
// Enable if any of the following are true:
// - We found a shared pref to enable the feature as part of an experiment.
// - The command line switch is enabled (overrides experiment value).
// - The app is one of Walton's launcher apps.
if (sUseWebViewContext
|| CommandLine.getInstance()
.hasSwitch(AwSwitches.WEBVIEW_USE_SEPARATE_RESOURCE_CONTEXT)
|| "com.aurora.launcher".equals(ctx.getPackageName())
|| "com.qiku.android.launcher3".equals(ctx.getPackageName())) {
try {
Context override =
ctx.createPackageContext(
packageInfo.packageName,
Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
// Don't enable for standalone WebView. Check package id of the theme resource
// to determine.
// TODO(crbug.com/343756896): Make this work for standalone too.
if ((override.getResources()
.getIdentifier(
"WebViewBaseTheme",
"style",
packageInfo.packageName)
& 0xff000000)
== 0x7f000000) {
ClassLoaderContextWrapperFactory.setOverrideInfo(
packageInfo.packageName,
R.style.WebViewBaseTheme,
Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
// Use this to report the actual state of the feature at runtime.
AwBrowserMainParts.setUseWebViewContext(true);
}
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Could not get resource override context.");
}
}
// WebView needs to make sure to always use the wrapped application context.
ctx = ClassLoaderContextWrapperFactory.get(ctx);
ContextUtils.initApplicationContext(ctx);
// Find the package ID for the package that WebView's resources come from.
// This will be the donor package if there is one, not our main package.
String resourcePackage = packageInfo.packageName;
if (packageInfo.applicationInfo.metaData != null) {
resourcePackage =
packageInfo.applicationInfo.metaData.getString(
"com.android.webview.WebViewDonorPackage", resourcePackage);
}
int packageId;
try {
packageId = webViewDelegate.getPackageId(ctx.getResources(), resourcePackage);
} catch (RuntimeException e) {
// We failed to find the package ID, which likely means this context's AssetManager
// doesn't have WebView loaded in it. This may be because WebViewFactory doesn't add
// the package persistently to ResourcesManager and the app's AssetManager has been
// recreated. Try adding it again using WebViewDelegate, which does add it
// persistently.
addWebViewAssetPath(ctx);
packageId = webViewDelegate.getPackageId(ctx.getResources(), resourcePackage);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
&& AwBrowserProcess.getApkType() != ApkType.TRICHROME
&& packageId > SHARED_LIBRARY_MAX_ID) {
throw new RuntimeException("Package ID too high for WebView: " + packageId);
}
mAwInit.setUpResourcesOnBackgroundThread(packageId, ctx);
AndroidXProcessGlobalConfig.extractConfigFromApp(application.getClassLoader());
// Temporarily disable CHIPS until the CookieManager API supports the feature.
CommandLine cl = CommandLine.getInstance();
cl.appendSwitch("disable-partitioned-cookies");
boolean multiProcess = webViewDelegate.isMultiProcessEnabled();
if (multiProcess) {
cl.appendSwitch(AwSwitches.WEBVIEW_SANDBOXED_RENDERER);
}
Log.i(
TAG,
"version=%s (%s) minSdkVersion=%s isBundle=%s multiprocess=%s packageId=%s",
VersionConstants.PRODUCT_VERSION,
BuildConfig.VERSION_CODE,
BuildConfig.MIN_SDK_VERSION,
ProductConfig.IS_BUNDLE,
multiProcess,
packageId);
// Enable modern SameSite cookie behavior if the app targets at least S.
if (ctx.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.S) {
cl.appendSwitch(AwSwitches.WEBVIEW_ENABLE_MODERN_COOKIE_SAME_SITE);
}
// Enable logging JS console messages in system logs only if the app is debuggable or
// it's a debuggable android build.
if (BuildInfo.isDebugAndroidOrApp()) {
cl.appendSwitch(AwSwitches.WEBVIEW_LOG_JS_CONSOLE_MESSAGES);
}
String webViewPackageName = AwBrowserProcess.getWebViewPackageName();
boolean isDeveloperModeEnabled =
DeveloperModeUtils.isDeveloperModeEnabled(webViewPackageName);
RecordHistogram.recordBooleanHistogram(
"Android.WebView.DevUi.DeveloperModeEnabled", isDeveloperModeEnabled);
Map<String, Boolean> flagOverrides = null;
if (isDeveloperModeEnabled) {
long start = SystemClock.elapsedRealtime();
try {
FlagOverrideHelper helper =
new FlagOverrideHelper(ProductionSupportedFlagList.sFlagList);
flagOverrides = DeveloperModeUtils.getFlagOverrides(webViewPackageName);
helper.applyFlagOverrides(flagOverrides);
RecordHistogram.recordCount100Histogram(
"Android.WebView.DevUi.ToggledFlagCount", flagOverrides.size());
} finally {
long end = SystemClock.elapsedRealtime();
RecordHistogram.recordTimesHistogram(
"Android.WebView.DevUi.FlagLoadingBlockingTime", end - start);
}
}
ThreadUtils.setWillOverrideUiThread();
BuildInfo.setBrowserPackageInfo(packageInfo);
// Trigger the creation of the BuildInfo singleton to avoid potential issues reading
// the command line if this happens on another thread.
BuildInfo.getInstance();
AndroidXProcessGlobalConfig androidXConfig = AndroidXProcessGlobalConfig.getConfig();
try (StrictModeContext ignored = StrictModeContext.allowDiskWrites()) {
try (ScopedSysTraceEvent e2 =
ScopedSysTraceEvent.scoped(
"WebViewChromiumFactoryProvider.loadChromiumLibrary")) {
String dataDirectoryBasePath = androidXConfig.getDataDirectoryBasePathOrNull();
String cacheDirectoryBasePath =
androidXConfig.getCacheDirectoryBasePathOrNull();
String dataDirectorySuffix;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
dataDirectorySuffix =
GlueApiHelperForP.getDataDirectorySuffix(webViewDelegate);
} else {
// Try the AndroidX library version
dataDirectorySuffix = androidXConfig.getDataDirectorySuffixOrNull();
}
AwBrowserProcess.loadLibrary(
dataDirectoryBasePath, cacheDirectoryBasePath, dataDirectorySuffix);
}
try (ScopedSysTraceEvent e2 =
ScopedSysTraceEvent.scoped(
"WebViewChromiumFactoryProvider.loadGlueLayerPlatSupportLibrary")) {
System.loadLibrary("webviewchromium_plat_support");
}
deleteContentsOnPackageDowngrade(packageInfo);
}
// Now safe to use WebView data directory.
if (flagOverrides != null) {
AwContentsStatics.logFlagOverridesWithNative(flagOverrides);
}
SafeModeController controller = SafeModeController.getInstance();
controller.registerActions(BrowserSafeModeActionList.sList);
mIsSafeModeEnabled = controller.isSafeModeEnabled(webViewPackageName);
RecordHistogram.recordBooleanHistogram(
"Android.WebView.SafeMode.SafeModeEnabled", mIsSafeModeEnabled);
if (mIsSafeModeEnabled) {
try {
long safeModeQueryExecuteStart = SystemClock.elapsedRealtime();
Set<String> actions = controller.queryActions(webViewPackageName);
Log.w(
TAG,
"WebViewSafeMode is enabled: received %d SafeModeActions",
actions.size());
controller.executeActions(actions);
long safeModeQueryExecuteEnd = SystemClock.elapsedRealtime();
RecordHistogram.recordTimesHistogram(
"Android.WebView.SafeMode.QueryAndExecuteBlockingTime",
safeModeQueryExecuteEnd - safeModeQueryExecuteStart);
} catch (Throwable t) {
// Don't let SafeMode crash WebView. Instead just log the error.
Log.e(TAG, "WebViewSafeMode threw exception: ", t);
}
}
if (!FastVariationsSeedSafeModeAction.hasRun()) {
mAwInit.startVariationsInit();
}
mShouldDisableThreadChecking = shouldDisableThreadChecking(ctx);
setSingleton(this);
}
mInitInfo.mDuration = SystemClock.uptimeMillis() - mInitInfo.mStartTime;
RecordHistogram.recordTimesHistogram(
"Android.WebView.Startup.CreationTime.Stage1.FactoryInit", mInitInfo.mDuration);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
mInitInfo.mTotalFactoryInitStartTime =
mWebViewDelegate.getStartupTimestamps().getWebViewLoadStart();
mInitInfo.mTotalFactoryInitDuration =
SystemClock.uptimeMillis() - mInitInfo.mTotalFactoryInitStartTime;
RecordHistogram.recordTimesHistogram(
"Android.WebView.Startup.CreationTime.TotalFactoryInitTime",
mInitInfo.mTotalFactoryInitDuration);
}
}
/* package */ static void checkStorageIsNotDeviceProtected(Context context) {
// The PAC processor service uses WebViewFactoryProvider.getPacProcessor() to
// get the JS engine it needs to run PAC scripts. It doesn't use the rest of
// WebView and this use case does not really store any meaningful data in the
// WebView data directory, but the PAC service needs to be able to run before
// the device is unlocked so that other apps running in that state can make
// proxy lookups. So, we just skip the check for it and don't care whether it
// is using DE or CE storage.
if ("com.android.pacprocessor".equals(context.getPackageName())) {
return;
}
if (context.isDeviceProtectedStorage()) {
throw new IllegalArgumentException(
"WebView cannot be used with device protected storage");
}
}
/**
* Both versionCodes should be from a WebView provider package implemented by Chromium.
* VersionCodes from other kinds of packages won't make any sense in this method.
*
* An introduction to Chromium versionCode scheme:
* "BBBBPPPAX"
* BBBB: 4 digit branch number. It monotonically increases over time.
* PPP: patch number in the branch. It is padded with zeroes to the left. These three digits may
* change their meaning in the future.
* A: architecture digit.
* X: A digit to differentiate APKs for other reasons.
*
* This method takes the "BBBB" of versionCodes and compare them.
*
* @return true if versionCode1 is higher than or equal to versionCode2.
*/
private static boolean versionCodeGE(int versionCode1, int versionCode2) {
int v1 = versionCode1 / 100000;
int v2 = versionCode2 / 100000;
return v1 >= v2;
}
private static void deleteContents(File dir) {
File[] files = dir.listFiles();
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
deleteContents(file);
}
if (!file.delete()) {
Log.w(TAG, "Failed to delete " + file);
}
}
}
}
public static boolean preloadInZygote() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P && ProductConfig.IS_BUNDLE) {
// Apply workaround if we're a bundle on O, where the split APK handling bug exists.
SplitApkWorkaround.apply();
}
for (String library : NativeLibraries.LIBRARIES) {
System.loadLibrary(library);
}
return true;
}
SharedPreferences getWebViewPrefs() {
return mWebViewPrefs;
}
@Override
public Statics getStatics() {
synchronized (mAwInit.getLock()) {
SharedStatics sharedStatics = mAwInit.getStatics();
if (mStaticsAdapter == null) {
mStaticsAdapter =
new WebViewChromiumFactoryProvider.Statics() {
@Override
public String findAddress(String addr) {
return sharedStatics.findAddress(addr);
}
@Override
public String getDefaultUserAgent(Context context) {
return sharedStatics.getDefaultUserAgent(context);
}
@Override
public void setWebContentsDebuggingEnabled(boolean enable) {
sharedStatics.setWebContentsDebuggingEnabled(enable);
}
@Override
public void clearClientCertPreferences(Runnable onCleared) {
sharedStatics.clearClientCertPreferences(onCleared);
}
@Override
public void freeMemoryForTests() {
sharedStatics.freeMemoryForTests();
}
@Override
public void enableSlowWholeDocumentDraw() {
sharedStatics.enableSlowWholeDocumentDraw();
}
@Override
public Uri[] parseFileChooserResult(int resultCode, Intent intent) {
return sharedStatics.parseFileChooserResult(resultCode, intent);
}
@Override
public void initSafeBrowsing(
Context context, ValueCallback<Boolean> callback) {
sharedStatics.initSafeBrowsing(
context, CallbackConverter.fromValueCallback(callback));
}
@Override
public void setSafeBrowsingWhitelist(
List<String> urls, ValueCallback<Boolean> callback) {
sharedStatics.setSafeBrowsingAllowlist(
urls, CallbackConverter.fromValueCallback(callback));
}
@Override
public Uri getSafeBrowsingPrivacyPolicyUrl() {
return sharedStatics.getSafeBrowsingPrivacyPolicyUrl();
}
public boolean isMultiProcessEnabled() {
return sharedStatics.isMultiProcessEnabled();
}
public String getVariationsHeader() {
return sharedStatics.getVariationsHeader();
}
};
}
}
return mStaticsAdapter;
}
@Override
public WebViewProvider createWebView(WebView webView, WebView.PrivateAccess privateAccess) {
return new WebViewChromium(this, webView, privateAccess, mShouldDisableThreadChecking);
}
// Workaround for IME thread crashes on legacy OEM apps.
private boolean shouldDisableThreadChecking(Context context) {
String appName = context.getPackageName();
int versionCode = PackageUtils.getPackageVersion(appName);
int appTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
if (versionCode == -1) return false;
boolean shouldDisable = false;
// crbug.com/651706
final String lgeMailPackageId = "com.lge.email";
if (lgeMailPackageId.equals(appName)) {
if (appTargetSdkVersion > Build.VERSION_CODES.N) return false;
if (LGEmailActionModeWorkaround.isSafeVersion(versionCode)) return false;
shouldDisable = true;
}
// crbug.com/655759
// Also want to cover ".att" variant suffix package name.
final String yahooMailPackageId = "com.yahoo.mobile.client.android.mail";
if (appName.startsWith(yahooMailPackageId)) {
if (appTargetSdkVersion > Build.VERSION_CODES.M) return false;
if (versionCode > 1315850) return false;
shouldDisable = true;
}
// crbug.com/622151
final String htcMailPackageId = "com.htc.android.mail";
if (htcMailPackageId.equals(appName)) {
if (appTargetSdkVersion > Build.VERSION_CODES.M) return false;
// This value is provided by HTC.
if (versionCode >= 866001861) return false;
shouldDisable = true;
}
if (shouldDisable) {
Log.w(
TAG,
"Disabling thread check in WebView. "
+ "APK name: "
+ appName
+ ", versionCode: "
+ versionCode
+ ", targetSdkVersion: "
+ appTargetSdkVersion);
}
return shouldDisable;
}
/**
* Returns the cached SafeMode state. This must only be called after initialize(), which is when
* the SafeMode state is cached.
*/
public boolean isSafeModeEnabled() {
return mIsSafeModeEnabled;
}
@Override
public GeolocationPermissions getGeolocationPermissions() {
try (TraceEvent event =
TraceEvent.scoped("WebView.APICall.Framework.GET_GEOLOCATION_PERMISSIONS")) {
SharedStatics.recordStaticApiCall(ApiCall.GET_GEOLOCATION_PERMISSIONS);
return mAwInit.getDefaultGeolocationPermissions();
}
}
@Override
public CookieManager getCookieManager() {
return mAwInit.getDefaultCookieManager();
}
@Override
public ServiceWorkerController getServiceWorkerController() {
synchronized (mAwInit.getLock()) {
if (mServiceWorkerController == null) {
mServiceWorkerController =
new ServiceWorkerControllerAdapter(
mAwInit.getDefaultServiceWorkerController());
}
}
return mServiceWorkerController;
}
@Override
public TokenBindingService getTokenBindingService() {
return null;
}
@Override
public android.webkit.WebIconDatabase getWebIconDatabase() {
return mAwInit.getWebIconDatabase();
}
@Override
public WebStorage getWebStorage() {
return mAwInit.getDefaultWebStorage();
}
@Override
public WebViewDatabase getWebViewDatabase(final Context context) {
return mAwInit.getDefaultWebViewDatabase(context);
}
WebViewDelegate getWebViewDelegate() {
return mWebViewDelegate;
}
WebViewContentsClientAdapter createWebViewContentsClientAdapter(
WebView webView, Context context) {
try (ScopedSysTraceEvent e =
ScopedSysTraceEvent.scoped(
"WebViewChromiumFactoryProvider.insideCreateWebViewContentsClientAdapter")) {
return new WebViewContentsClientAdapter(webView, context, mWebViewDelegate);
}
}
void startYourEngines(boolean onMainThread) {
try (ScopedSysTraceEvent e1 =
ScopedSysTraceEvent.scoped("WebViewChromiumFactoryProvider.startYourEngines")) {
mAwInit.startYourEngines(onMainThread);
}
}
boolean hasStarted() {
return mAwInit.hasStarted();
}
// Only on UI thread.
AwBrowserContext getDefaultBrowserContextOnUiThread() {
return mAwInit.getDefaultBrowserContextOnUiThread();
}
WebViewChromiumAwInit getAwInit() {
return mAwInit;
}
@RequiresApi(Build.VERSION_CODES.P)
@Override
public TracingController getTracingController() {
synchronized (mAwInit.getLock()) {
mAwInit.ensureChromiumStartedLocked(
true, WebViewChromiumAwInit.CallSite.GET_TRACING_CONTROLLER);
// ensureChromiumStartedLocked() can release the lock on first call while
// waiting for startup. Hence check the mTracingController here to ensure
// the singleton property.
if (mObjectHolderForP.mTracingController == null) {
mObjectHolderForP.mTracingController =
GlueApiHelperForP.createTracingControllerAdapter(this, mAwInit);
}
}
return mObjectHolderForP.mTracingController;
}
private static class FilteredClassLoader extends ClassLoader {
FilteredClassLoader(ClassLoader delegate) {
super(delegate);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
final String message =
"This ClassLoader should only be used for the androidx.webkit support library";
if (name == null) {
throw new ClassNotFoundException(message);
}
// We only permit this ClassLoader to load classes required for support library
// reflection, as applications should not use this for any other purpose. So, we permit
// anything in the support_lib_glue and support_lib_boundary packages (and their
// subpackages).
if (name.startsWith(SUPPORT_LIB_GLUE_AND_BOUNDARY_INTERFACE_PREFIX)) {
return super.findClass(name);
}
throw new ClassNotFoundException(message);
}
}
@Override
public ClassLoader getWebViewClassLoader() {
return new FilteredClassLoader(WebViewChromiumFactoryProvider.class.getClassLoader());
}
@RequiresApi(Build.VERSION_CODES.R)
@Override
public PacProcessor getPacProcessor() {
return GlueApiHelperForR.getPacProcessor();
}
@RequiresApi(Build.VERSION_CODES.R)
@Override
public PacProcessor createPacProcessor() {
return GlueApiHelperForR.createPacProcessor();
}
public InitInfo getInitInfo() {
return mInitInfo;
}
}