WebViewFactory.java revision dc1573595ca6d8b362c3ce0883a35069d2b08f13
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
17package android.webkit;
18
19import android.os.Build;
20import android.os.StrictMode;
21import android.os.SystemProperties;
22import android.util.AndroidRuntimeException;
23import android.util.Log;
24
25/**
26 * Top level factory, used creating all the main WebView implementation classes.
27 *
28 * @hide
29 */
30public final class WebViewFactory {
31    private static final boolean DEFAULT_TO_EXPERIMENTAL_WEBVIEW = true;
32    // REMEMBER: property names must be <= 31 chars total.
33    private static final String EXPERIMENTAL_PROPERTY_DEFAULT_OFF = "persist.sys.webview.exp";
34    private static final String EXPERIMENTAL_PROPERTY_DEFAULT_ON =
35            "persist.sys.webview." + Build.ID;
36
37    // Modify the persisted property name when the new webview is on-by-default, so that any user
38    // setting override only lives as long as that build.
39    private static final String LONG_PROPERTY_NAME = DEFAULT_TO_EXPERIMENTAL_WEBVIEW ?
40            EXPERIMENTAL_PROPERTY_DEFAULT_ON : EXPERIMENTAL_PROPERTY_DEFAULT_OFF;
41    private static final String WEBVIEW_EXPERIMENTAL_PROPERTY =
42            LONG_PROPERTY_NAME.length() > SystemProperties.PROP_NAME_MAX ?
43            LONG_PROPERTY_NAME.substring(0, SystemProperties.PROP_NAME_MAX) : LONG_PROPERTY_NAME;
44
45    private static final String FORCE_PROVIDER_PROPERTY = "webview.force_provider";
46    private static final String FORCE_PROVIDER_PROPERTY_VALUE_CHROMIUM = "chromium";
47    private static final String FORCE_PROVIDER_PROPERTY_VALUE_CLASSIC = "classic";
48
49    // Default Provider factory class name.
50    // TODO: When the Chromium powered WebView is ready, it should be the default factory class.
51    private static final String DEFAULT_WEBVIEW_FACTORY = "android.webkit.WebViewClassic$Factory";
52    private static final String CHROMIUM_WEBVIEW_FACTORY =
53            "com.android.webview.chromium.WebViewChromiumFactoryProvider";
54
55    private static final String LOGTAG = "WebViewFactory";
56
57    private static final boolean DEBUG = false;
58
59    private static class Preloader {
60        static WebViewFactoryProvider sPreloadedProvider;
61        static {
62            try {
63                sPreloadedProvider = getFactoryClass().newInstance();
64            } catch (Exception e) {
65                Log.w(LOGTAG, "error preloading provider", e);
66            }
67        }
68    }
69
70    // Cache the factory both for efficiency, and ensure any one process gets all webviews from the
71    // same provider.
72    private static WebViewFactoryProvider sProviderInstance;
73    private static final Object sProviderLock = new Object();
74
75    public static boolean isExperimentalWebViewAvailable() {
76        try {
77            // Pass false so we don't initialize the class at this point, as this will be wasted if
78            // it's not enabled.
79            Class.forName(CHROMIUM_WEBVIEW_FACTORY, false, WebViewFactory.class.getClassLoader());
80            return true;
81        } catch (ClassNotFoundException e) {
82            return false;
83        }
84    }
85
86    /** @hide */
87    public static void setUseExperimentalWebView(boolean enable) {
88        SystemProperties.set(WEBVIEW_EXPERIMENTAL_PROPERTY, enable ? "true" : "false");
89        Log.i(LOGTAG, "Use Experimental WebView changed: "
90                + SystemProperties.get(WebViewFactory.WEBVIEW_EXPERIMENTAL_PROPERTY, ""));
91    }
92
93    /** @hide */
94    public static boolean useExperimentalWebView() {
95        return SystemProperties.getBoolean(WEBVIEW_EXPERIMENTAL_PROPERTY,
96            DEFAULT_TO_EXPERIMENTAL_WEBVIEW);
97    }
98
99    /** @hide */
100    public static boolean isUseExperimentalWebViewSet() {
101        return !SystemProperties.get(WEBVIEW_EXPERIMENTAL_PROPERTY).isEmpty();
102    }
103
104    static WebViewFactoryProvider getProvider() {
105        synchronized (sProviderLock) {
106            // For now the main purpose of this function (and the factory abstraction) is to keep
107            // us honest and minimize usage of WebViewClassic internals when binding the proxy.
108            if (sProviderInstance != null) return sProviderInstance;
109
110            Class<WebViewFactoryProvider> providerClass;
111            try {
112                providerClass = getFactoryClass();
113            } catch (ClassNotFoundException e) {
114                Log.e(LOGTAG, "error loading provider", e);
115                throw new AndroidRuntimeException(e);
116            }
117
118            // This implicitly loads Preloader even if it wasn't preloaded at boot.
119            if (Preloader.sPreloadedProvider != null &&
120                Preloader.sPreloadedProvider.getClass() == providerClass) {
121                sProviderInstance = Preloader.sPreloadedProvider;
122                if (DEBUG) Log.v(LOGTAG, "Using preloaded provider: " + sProviderInstance);
123                return sProviderInstance;
124            }
125
126            // The preloaded provider isn't the one we wanted; construct our own.
127            StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
128            try {
129                sProviderInstance = providerClass.newInstance();
130                if (DEBUG) Log.v(LOGTAG, "Loaded provider: " + sProviderInstance);
131                return sProviderInstance;
132            } catch (Exception e) {
133                Log.e(LOGTAG, "error instantiating provider", e);
134                throw new AndroidRuntimeException(e);
135            } finally {
136                StrictMode.setThreadPolicy(oldPolicy);
137            }
138        }
139    }
140
141    // We allow a system property to specify that we should use the experimental Chromium powered
142    // WebView. This enables us to switch between implementations at runtime.
143    private static boolean isExperimentalWebViewEnabled() {
144        if (!isExperimentalWebViewAvailable()) return false;
145        String forceProviderName = SystemProperties.get(FORCE_PROVIDER_PROPERTY);
146        if (forceProviderName.isEmpty()) return useExperimentalWebView();
147
148        Log.i(LOGTAG, String.format("Provider overridden by property: %s=%s",
149                FORCE_PROVIDER_PROPERTY, forceProviderName));
150        if (forceProviderName.equals(FORCE_PROVIDER_PROPERTY_VALUE_CHROMIUM)) return true;
151        if (forceProviderName.equals(FORCE_PROVIDER_PROPERTY_VALUE_CLASSIC)) return false;
152        Log.e(LOGTAG, String.format("Unrecognized provider: %s", forceProviderName));
153        return useExperimentalWebView();
154    }
155
156    private static Class<WebViewFactoryProvider> getFactoryClass() throws ClassNotFoundException {
157        if (isExperimentalWebViewEnabled()) {
158            return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY);
159        } else  {
160            return (Class<WebViewFactoryProvider>) Class.forName(DEFAULT_WEBVIEW_FACTORY);
161        }
162    }
163}
164