Enable preloading of the appropriate WebView.

Allow the appropriate WebView to be preloaded in the zygote by
constructing the currently selected WebViewFactoryProvider when the
WebViewFactory is preloaded. At runtime, if the preloaded provider is
still the current selection, the preloaded instance is used, otherwise
the provider is loaded at that time.

This change also removes "graceful" fallback from the experimental
WebView to the classic implementation: if the option to use the
experimental WebView is selected and it could not be loaded
successfully at the time a WebView is created, an exception will be
thrown, rather than allowing execution to continue with the classic
implementation, as the fallback may mislead developers who do not
examine logcat output in detail.

Change-Id: I0cd01c784d7048abeac55ab5863ca16b8fd9ecf2
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index a324502..3afab09 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -66,6 +66,7 @@
 import android.text.InputType;
 import android.text.Selection;
 import android.text.TextUtils;
+import android.util.AndroidRuntimeException;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Log;
@@ -1293,6 +1294,19 @@
     // WebViewProvider bindings
 
     static class Factory implements WebViewFactoryProvider,  WebViewFactoryProvider.Statics {
+        Factory() {
+            // Touch JniUtil and WebViewCore in case this is being called from
+            // WebViewFactory.Preloader, to ensure that the JNI libraries that they use are
+            // preloaded in the zygote.
+            try {
+                Class.forName("android.webkit.JniUtil");
+                Class.forName("android.webkit.WebViewCore");
+            } catch (ClassNotFoundException e) {
+                Log.e(LOGTAG, "failed to load JNI libraries");
+                throw new AndroidRuntimeException(e);
+            }
+        }
+
         @Override
         public String findAddress(String addr) {
             return WebViewClassic.findAddress(addr);
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index ea5187a..2ee0961 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -19,10 +19,9 @@
 import android.os.Build;
 import android.os.StrictMode;
 import android.os.SystemProperties;
+import android.util.AndroidRuntimeException;
 import android.util.Log;
 
-import dalvik.system.PathClassLoader;
-
 /**
  * Top level factory, used creating all the main WebView implementation classes.
  *
@@ -45,6 +44,17 @@
 
     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;
@@ -67,32 +77,39 @@
             // us honest and minimize usage of WebViewClassic internals when binding the proxy.
             if (sProviderInstance != null) return sProviderInstance;
 
-            if (isExperimentalWebViewEnabled()) {
-                StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
-                try {
-                    sProviderInstance = getFactoryByName(CHROMIUM_WEBVIEW_FACTORY);
-                    if (DEBUG) Log.v(LOGTAG, "Loaded Chromium provider: " + sProviderInstance);
-                } finally {
-                    StrictMode.setThreadPolicy(oldPolicy);
-                }
+            Class<WebViewFactoryProvider> providerClass;
+            try {
+                providerClass = getFactoryClass();
+            } catch (ClassNotFoundException e) {
+                Log.e(LOGTAG, "error loading provider", e);
+                throw new AndroidRuntimeException(e);
             }
 
-            if (sProviderInstance == null) {
-                if (DEBUG) Log.v(LOGTAG, "Falling back to default provider: "
-                        + DEFAULT_WEBVIEW_FACTORY);
-                sProviderInstance = getFactoryByName(DEFAULT_WEBVIEW_FACTORY);
-                if (sProviderInstance == null) {
-                    if (DEBUG) Log.v(LOGTAG, "Falling back to explicit linkage");
-                    sProviderInstance = new WebViewClassic.Factory();
-                }
+            // 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;
             }
-            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);
+            }
         }
     }
 
-    // For debug builds, we allow a system property to specify that we should use the
-    // experimtanl Chromium powered WebView. This enables us to switch between
-    // implementations at runtime. For user (release) builds, don't allow this.
+    // 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;
         boolean use_experimental_webview = SystemProperties.getBoolean(
@@ -108,19 +125,11 @@
         return use_experimental_webview;
     }
 
-    private static WebViewFactoryProvider getFactoryByName(String providerName) {
-        try {
-            if (DEBUG) Log.v(LOGTAG, "attempt to load class " + providerName);
-            Class<?> c = Class.forName(providerName);
-            if (DEBUG) Log.v(LOGTAG, "instantiating factory");
-            return (WebViewFactoryProvider) c.newInstance();
-        } catch (ClassNotFoundException e) {
-            Log.e(LOGTAG, "error loading " + providerName, e);
-        } catch (IllegalAccessException e) {
-            Log.e(LOGTAG, "error loading " + providerName, e);
-        } catch (InstantiationException e) {
-            Log.e(LOGTAG, "error loading " + providerName, e);
+    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);
         }
-        return null;
     }
 }
diff --git a/preloaded-classes b/preloaded-classes
index 2aa610a..45d27ee 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -1220,7 +1220,6 @@
 android.webkit.HTML5VideoViewProxy
 android.webkit.JWebCoreJavaBridge
 android.webkit.JavascriptInterface
-android.webkit.JniUtil
 android.webkit.L10nUtils
 android.webkit.MockGeolocation
 android.webkit.OverScrollGlow
@@ -1269,7 +1268,6 @@
 android.webkit.WebViewClassic$TrustStorageListener
 android.webkit.WebViewClassic$ViewSizeData
 android.webkit.WebViewClient
-android.webkit.WebViewCore
 android.webkit.WebViewCore$AutoFillData
 android.webkit.WebViewCore$DrawData
 android.webkit.WebViewCore$EventHub
@@ -1283,6 +1281,7 @@
 android.webkit.WebViewDatabaseClassic
 android.webkit.WebViewDatabaseClassic$1
 android.webkit.WebViewFactory
+android.webkit.WebViewFactory$Preloader
 android.webkit.WebViewFactoryProvider
 android.webkit.WebViewFactoryProvider$Statics
 android.webkit.WebViewInputDispatcher