| /* |
| * Copyright (C) 2012 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.webkit; |
| |
| import android.os.Build; |
| import android.os.StrictMode; |
| import android.os.SystemProperties; |
| import android.util.AndroidRuntimeException; |
| import android.util.Log; |
| |
| /** |
| * Top level factory, used creating all the main WebView implementation classes. |
| * |
| * @hide |
| */ |
| public final class WebViewFactory { |
| private static final boolean DEFAULT_TO_EXPERIMENTAL_WEBVIEW = true; |
| // REMEMBER: property names must be <= 31 chars total. |
| private static final String EXPERIMENTAL_PROPERTY_DEFAULT_OFF = "persist.sys.webview.exp"; |
| private static final String EXPERIMENTAL_PROPERTY_DEFAULT_ON = |
| "persist.sys.webview." + Build.ID; |
| |
| // Modify the persisted property name when the new webview is on-by-default, so that any user |
| // setting override only lives as long as that build. |
| private static final String LONG_PROPERTY_NAME = DEFAULT_TO_EXPERIMENTAL_WEBVIEW ? |
| EXPERIMENTAL_PROPERTY_DEFAULT_ON : EXPERIMENTAL_PROPERTY_DEFAULT_OFF; |
| private static final String WEBVIEW_EXPERIMENTAL_PROPERTY = |
| LONG_PROPERTY_NAME.length() > SystemProperties.PROP_NAME_MAX ? |
| LONG_PROPERTY_NAME.substring(0, SystemProperties.PROP_NAME_MAX) : LONG_PROPERTY_NAME; |
| |
| private static final String FORCE_PROVIDER_PROPERTY = "webview.force_provider"; |
| private static final String FORCE_PROVIDER_PROPERTY_VALUE_CHROMIUM = "chromium"; |
| private static final String FORCE_PROVIDER_PROPERTY_VALUE_CLASSIC = "classic"; |
| |
| // Default Provider factory class name. |
| // TODO: When the Chromium powered WebView is ready, it should be the default factory class. |
| private static final String DEFAULT_WEBVIEW_FACTORY = "android.webkit.WebViewClassic$Factory"; |
| private static final String CHROMIUM_WEBVIEW_FACTORY = |
| "com.android.webview.chromium.WebViewChromiumFactoryProvider"; |
| |
| private static final String LOGTAG = "WebViewFactory"; |
| |
| private static final boolean DEBUG = false; |
| |
| private static class Preloader { |
| static WebViewFactoryProvider sPreloadedProvider; |
| static { |
| try { |
| sPreloadedProvider = getFactoryClass().newInstance(); |
| } catch (Exception e) { |
| Log.w(LOGTAG, "error preloading provider", e); |
| } |
| } |
| } |
| |
| // Cache the factory both for efficiency, and ensure any one process gets all webviews from the |
| // same provider. |
| private static WebViewFactoryProvider sProviderInstance; |
| private static final Object sProviderLock = new Object(); |
| |
| public static boolean isExperimentalWebViewAvailable() { |
| try { |
| // Pass false so we don't initialize the class at this point, as this will be wasted if |
| // it's not enabled. |
| Class.forName(CHROMIUM_WEBVIEW_FACTORY, false, WebViewFactory.class.getClassLoader()); |
| return true; |
| } catch (ClassNotFoundException e) { |
| return false; |
| } |
| } |
| |
| /** @hide */ |
| public static void setUseExperimentalWebView(boolean enable) { |
| SystemProperties.set(WEBVIEW_EXPERIMENTAL_PROPERTY, enable ? "true" : "false"); |
| Log.i(LOGTAG, "Use Experimental WebView changed: " |
| + SystemProperties.get(WebViewFactory.WEBVIEW_EXPERIMENTAL_PROPERTY, "")); |
| } |
| |
| /** @hide */ |
| public static boolean useExperimentalWebView() { |
| return SystemProperties.getBoolean(WEBVIEW_EXPERIMENTAL_PROPERTY, |
| DEFAULT_TO_EXPERIMENTAL_WEBVIEW); |
| } |
| |
| /** @hide */ |
| public static boolean isUseExperimentalWebViewSet() { |
| return !SystemProperties.get(WEBVIEW_EXPERIMENTAL_PROPERTY).isEmpty(); |
| } |
| |
| static WebViewFactoryProvider getProvider() { |
| synchronized (sProviderLock) { |
| // For now the main purpose of this function (and the factory abstraction) is to keep |
| // us honest and minimize usage of WebViewClassic internals when binding the proxy. |
| if (sProviderInstance != null) return sProviderInstance; |
| |
| Class<WebViewFactoryProvider> providerClass; |
| try { |
| providerClass = getFactoryClass(); |
| } catch (ClassNotFoundException e) { |
| Log.e(LOGTAG, "error loading provider", e); |
| throw new AndroidRuntimeException(e); |
| } |
| |
| // This implicitly loads Preloader even if it wasn't preloaded at boot. |
| if (Preloader.sPreloadedProvider != null && |
| Preloader.sPreloadedProvider.getClass() == providerClass) { |
| sProviderInstance = Preloader.sPreloadedProvider; |
| if (DEBUG) Log.v(LOGTAG, "Using preloaded provider: " + sProviderInstance); |
| return sProviderInstance; |
| } |
| |
| // The preloaded provider isn't the one we wanted; construct our own. |
| StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); |
| try { |
| sProviderInstance = providerClass.newInstance(); |
| if (DEBUG) Log.v(LOGTAG, "Loaded provider: " + sProviderInstance); |
| return sProviderInstance; |
| } catch (Exception e) { |
| Log.e(LOGTAG, "error instantiating provider", e); |
| throw new AndroidRuntimeException(e); |
| } finally { |
| StrictMode.setThreadPolicy(oldPolicy); |
| } |
| } |
| } |
| |
| // We allow a system property to specify that we should use the experimental Chromium powered |
| // WebView. This enables us to switch between implementations at runtime. |
| private static boolean isExperimentalWebViewEnabled() { |
| if (!isExperimentalWebViewAvailable()) return false; |
| String forceProviderName = SystemProperties.get(FORCE_PROVIDER_PROPERTY); |
| if (forceProviderName.isEmpty()) return useExperimentalWebView(); |
| |
| Log.i(LOGTAG, String.format("Provider overridden by property: %s=%s", |
| FORCE_PROVIDER_PROPERTY, forceProviderName)); |
| if (forceProviderName.equals(FORCE_PROVIDER_PROPERTY_VALUE_CHROMIUM)) return true; |
| if (forceProviderName.equals(FORCE_PROVIDER_PROPERTY_VALUE_CLASSIC)) return false; |
| Log.e(LOGTAG, String.format("Unrecognized provider: %s", forceProviderName)); |
| return useExperimentalWebView(); |
| } |
| |
| private static Class<WebViewFactoryProvider> getFactoryClass() throws ClassNotFoundException { |
| if (isExperimentalWebViewEnabled()) { |
| return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY); |
| } else { |
| return (Class<WebViewFactoryProvider>) Class.forName(DEFAULT_WEBVIEW_FACTORY); |
| } |
| } |
| } |