Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ff937ac2 authored by Torne (Richard Coles)'s avatar Torne (Richard Coles)
Browse files

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
parent f27d5f0a
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -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();
        }
+26 −14
Original line number Diff line number Diff line
@@ -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<WebViewFactoryProvider>) 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() {
        if (isWebViewSupported()) {
            return IWebViewUpdateService.Stub.asInterface(
                    ServiceManager.getService(WEBVIEW_UPDATE_SERVICE_NAME));
        } else {
            return null;
        }
    }
}
+15 −3
Original line number Diff line number Diff line
@@ -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();
        }
+7 −5
Original line number Diff line number Diff line
@@ -668,10 +668,12 @@ public final class SystemServer {
        traceEnd();

        // Tracks whether the updatable WebView is in a ready state and watches for update installs.
        if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) {
            traceBeginAndSlog("StartWebViewUpdateService");
            mWebViewUpdateService = mSystemServiceManager.startService(WebViewUpdateService.class);
            traceEnd();
        }
    }

    /**
     * Starts a miscellaneous grab bag of stuff that has yet to be refactored
@@ -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(