From ff937ac226c73f992df79b61a16973b41b406772 Mon Sep 17 00:00:00 2001 From: "Torne (Richard Coles)" Date: Mon, 2 Oct 2017 17:09:32 -0400 Subject: [PATCH] Improve handling of devices without a WebView. Use the presence of FEATURE_WEBVIEW to determine whether a device is intended to have a WebView implementation or not, instead of trying to fall back to NullWebViewFactoryProvider after loading WebView fails. This removes the need for nullwebview entirely, and eliminates a class of possible bug where unexpected exceptions during loading cause the fallback mechanism not to work reliably. On devices which don't have the feature, don't start WebViewUpdateService at all. Guard all the places which try to access the service to return reasonable (empty/null) results when this is the case, instead of throwing exceptions. Change-Id: I839adaaf0abee28f4989e3fbc0c49c0732d0ec1c Bug: 31849211 Fixes: 28529980 Test: on wear and non-wear, cts-tradefed run cts -m CtsWebkitTestCases --- core/java/android/webkit/WebView.java | 6 ++- core/java/android/webkit/WebViewFactory.java | 40 ++++++++++++------- .../android/webkit/WebViewUpdateService.java | 18 +++++++-- .../java/com/android/server/SystemServer.java | 12 +++--- 4 files changed, 53 insertions(+), 23 deletions(-) diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 077a901898d40..dfc81b2bcb5af 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -2972,8 +2972,12 @@ public class WebView extends AbsoluteLayout return webviewPackage; } + IWebViewUpdateService service = WebViewFactory.getUpdateService(); + if (service == null) { + return null; + } try { - return WebViewFactory.getUpdateService().getCurrentWebViewPackage(); + return service.getCurrentWebViewPackage(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index 994512f7c897f..36b24ffb89543 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -51,9 +51,6 @@ public final class WebViewFactory { private static final String CHROMIUM_WEBVIEW_FACTORY_METHOD = "create"; - private static final String NULL_WEBVIEW_FACTORY = - "com.android.webview.nullwebview.NullWebViewFactoryProvider"; - public static final String CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY = "persist.sys.webview.vmsize"; @@ -66,6 +63,7 @@ public final class WebViewFactory { private static WebViewFactoryProvider sProviderInstance; private static final Object sProviderLock = new Object(); private static PackageInfo sPackageInfo; + private static Boolean sWebViewSupported; // Error codes for loadWebViewNativeLibraryFromPackage public static final int LIBLOAD_SUCCESS = 0; @@ -105,6 +103,16 @@ public final class WebViewFactory { public MissingWebViewPackageException(Exception e) { super(e); } } + private static boolean isWebViewSupported() { + // No lock; this is a benign race as Boolean's state is final and the PackageManager call + // will always return the same value. + if (sWebViewSupported == null) { + sWebViewSupported = AppGlobals.getInitialApplication().getPackageManager() + .hasSystemFeature(PackageManager.FEATURE_WEBVIEW); + } + return sWebViewSupported; + } + /** * @hide */ @@ -135,6 +143,10 @@ public final class WebViewFactory { */ public static int loadWebViewNativeLibraryFromPackage(String packageName, ClassLoader clazzLoader) { + if (!isWebViewSupported()) { + return LIBLOAD_WRONG_PACKAGE_NAME; + } + WebViewProviderResponse response = null; try { response = getUpdateService().waitForAndGetProvider(); @@ -188,6 +200,11 @@ public final class WebViewFactory { "For security reasons, WebView is not allowed in privileged processes"); } + if (!isWebViewSupported()) { + // Device doesn't support WebView; don't try to load it, just throw. + throw new UnsupportedOperationException(); + } + StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getProvider()"); try { @@ -410,15 +427,6 @@ public final class WebViewFactory { Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW); } } catch (MissingWebViewPackageException e) { - // If the package doesn't exist, then try loading the null WebView instead. - // If that succeeds, then this is a device without WebView support; if it fails then - // swallow the failure, complain that the real WebView is missing and rethrow the - // original exception. - try { - return (Class) Class.forName(NULL_WEBVIEW_FACTORY); - } catch (ClassNotFoundException e2) { - // Ignore. - } Log.e(LOGTAG, "Chromium WebView package does not exist", e); throw new AndroidRuntimeException(e); } @@ -483,7 +491,11 @@ public final class WebViewFactory { /** @hide */ public static IWebViewUpdateService getUpdateService() { - return IWebViewUpdateService.Stub.asInterface( - ServiceManager.getService(WEBVIEW_UPDATE_SERVICE_NAME)); + if (isWebViewSupported()) { + return IWebViewUpdateService.Stub.asInterface( + ServiceManager.getService(WEBVIEW_UPDATE_SERVICE_NAME)); + } else { + return null; + } } } diff --git a/core/java/android/webkit/WebViewUpdateService.java b/core/java/android/webkit/WebViewUpdateService.java index 2f7d6854803d3..629891cca4f60 100644 --- a/core/java/android/webkit/WebViewUpdateService.java +++ b/core/java/android/webkit/WebViewUpdateService.java @@ -31,8 +31,12 @@ public final class WebViewUpdateService { * Fetch all packages that could potentially implement WebView. */ public static WebViewProviderInfo[] getAllWebViewPackages() { + IWebViewUpdateService service = getUpdateService(); + if (service == null) { + return new WebViewProviderInfo[0]; + } try { - return getUpdateService().getAllWebViewPackages(); + return service.getAllWebViewPackages(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -42,8 +46,12 @@ public final class WebViewUpdateService { * Fetch all packages that could potentially implement WebView and are currently valid. */ public static WebViewProviderInfo[] getValidWebViewPackages() { + IWebViewUpdateService service = getUpdateService(); + if (service == null) { + return new WebViewProviderInfo[0]; + } try { - return getUpdateService().getValidWebViewPackages(); + return service.getValidWebViewPackages(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -53,8 +61,12 @@ public final class WebViewUpdateService { * Used by DevelopmentSetting to get the name of the WebView provider currently in use. */ public static String getCurrentWebViewPackageName() { + IWebViewUpdateService service = getUpdateService(); + if (service == null) { + return null; + } try { - return getUpdateService().getCurrentWebViewPackageName(); + return service.getCurrentWebViewPackageName(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index d7ec7b6458fdf..1c06662c9e97b 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -668,9 +668,11 @@ public final class SystemServer { traceEnd(); // Tracks whether the updatable WebView is in a ready state and watches for update installs. - traceBeginAndSlog("StartWebViewUpdateService"); - mWebViewUpdateService = mSystemServiceManager.startService(WebViewUpdateService.class); - traceEnd(); + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) { + traceBeginAndSlog("StartWebViewUpdateService"); + mWebViewUpdateService = mSystemServiceManager.startService(WebViewUpdateService.class); + traceEnd(); + } } /** @@ -1687,10 +1689,10 @@ public final class SystemServer { traceEnd(); // No dependency on Webview preparation in system server. But this should - // be completed before allowring 3rd party + // be completed before allowing 3rd party final String WEBVIEW_PREPARATION = "WebViewFactoryPreparation"; Future webviewPrep = null; - if (!mOnlyCore) { + if (!mOnlyCore && mWebViewUpdateService != null) { webviewPrep = SystemServerInitThreadPool.get().submit(() -> { Slog.i(TAG, WEBVIEW_PREPARATION); TimingsTraceLog traceLog = new TimingsTraceLog(