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