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

Commit cd8f2737 authored by Gustav Sennton's avatar Gustav Sennton
Browse files

Verify WebView package info before loading WebView

To ensure that the package we are using to load WebView is a valid
provider we need to verify this before loading WebView - not only when
choosing what package to use.

Bug: 27900925
Change-Id: If57ca32a7a3fa08735a3b8ea9ed268c1bb1b8ede
parent e767e1bc
Loading
Loading
Loading
Loading
+112 −43
Original line number Original line Diff line number Diff line
@@ -18,13 +18,14 @@ package android.webkit;


import android.annotation.SystemApi;
import android.annotation.SystemApi;
import android.app.ActivityManagerInternal;
import android.app.ActivityManagerInternal;
import android.app.ActivityManagerNative;
import android.app.AppGlobals;
import android.app.AppGlobals;
import android.app.Application;
import android.app.Application;
import android.content.Context;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager;
import android.content.res.XmlResourceParser;
import android.content.pm.Signature;
import android.os.Build;
import android.os.Build;
import android.os.Process;
import android.os.Process;
import android.os.RemoteException;
import android.os.RemoteException;
@@ -32,27 +33,21 @@ import android.os.ServiceManager;
import android.os.StrictMode;
import android.os.StrictMode;
import android.os.SystemProperties;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.Trace;
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.text.TextUtils;
import android.text.TextUtils;
import android.util.AndroidRuntimeException;
import android.util.AndroidRuntimeException;
import android.util.ArraySet;
import android.util.Log;
import android.util.Log;


import com.android.internal.util.XmlUtils;
import com.android.server.LocalServices;
import com.android.server.LocalServices;


import dalvik.system.VMRuntime;
import dalvik.system.VMRuntime;


import java.io.File;
import java.io.File;
import java.io.IOException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Arrays;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipFile;


import org.xmlpull.v1.XmlPullParserException;

/**
/**
 * Top level factory, used creating all the main WebView implementation classes.
 * Top level factory, used creating all the main WebView implementation classes.
 *
 *
@@ -192,52 +187,126 @@ public final class WebViewFactory {
        }
        }
    }
    }


    private static Class<WebViewFactoryProvider> getProviderClass() {
    /**
     * Returns true if the signatures match, false otherwise
     */
    private static boolean signaturesEquals(Signature[] s1, Signature[] s2) {
        if (s1 == null) {
            return s2 == null;
        }
        if (s2 == null) return false;

        ArraySet<Signature> set1 = new ArraySet<>();
        for(Signature signature : s1) {
            set1.add(signature);
        }
        ArraySet<Signature> set2 = new ArraySet<>();
        for(Signature signature : s2) {
            set2.add(signature);
        }
        return set1.equals(set2);
    }

    // Throws MissingWebViewPackageException on failure
    private static void verifyPackageInfo(PackageInfo chosen, PackageInfo toUse) {
        if (!chosen.packageName.equals(toUse.packageName)) {
            throw new MissingWebViewPackageException("Failed to verify WebView provider, "
                    + "packageName mismatch, expected: "
                    + chosen.packageName + " actual: " + toUse.packageName);
        }
        if (chosen.versionCode > toUse.versionCode) {
            throw new MissingWebViewPackageException("Failed to verify WebView provider, "
                    + "version code mismatch, expected: " + chosen.versionCode
                    + " actual: " + toUse.versionCode);
        }
        if (getWebViewLibrary(toUse.applicationInfo) == null) {
            throw new MissingWebViewPackageException("Tried to load an invalid WebView provider: "
                    + toUse.packageName);
        }
        if (!signaturesEquals(chosen.signatures, toUse.signatures)) {
            throw new MissingWebViewPackageException("Failed to verify WebView provider, "
                    + "signature mismatch");
        }
    }

    private static Context getWebViewContextAndSetProvider() {
        Application initialApplication = AppGlobals.getInitialApplication();
        try {
        try {
            WebViewProviderResponse response = null;
            Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
            Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
                    "WebViewFactory.waitForProviderAndSetPackageInfo()");
                    "WebViewUpdateService.waitForAndGetProvider()");
            try {
            try {
                // First fetch the package info so we can log the webview package version.
                response = getUpdateService().waitForAndGetProvider();
                int res = waitForProviderAndSetPackageInfo();
            } finally {
                if (res != LIBLOAD_SUCCESS) {
                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
                    throw new MissingWebViewPackageException(
            }
                            "Failed to load WebView provider, error: "
            if (response.status != LIBLOAD_SUCCESS) {
                            + getWebViewPreparationErrorReason(res));
                throw new MissingWebViewPackageException("Failed to load WebView provider: "
                        + getWebViewPreparationErrorReason(response.status));
            }
            }
            // Register to be killed before fetching package info - so that we will be
            // killed if the package info goes out-of-date.
            Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "ActivityManager.addPackageDependency()");
            try {
                ActivityManagerNative.getDefault().addPackageDependency(
                        response.packageInfo.packageName);
            } finally {
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
            }
            }
            Log.i(LOGTAG, "Loading " + sPackageInfo.packageName + " version " +
            // Fetch package info and verify it against the chosen package
                sPackageInfo.versionName + " (code " + sPackageInfo.versionCode + ")");
            PackageInfo newPackageInfo = null;

            Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "PackageManager.getPackageInfo()");
            Application initialApplication = AppGlobals.getInitialApplication();
            Context webViewContext = null;
            Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "PackageManager.getApplicationInfo()");
            try {
            try {
                // Construct a package context to load the Java code into the current app.
                newPackageInfo = initialApplication.getPackageManager().getPackageInfo(
                // This is done as early as possible since by constructing a package context we
                    response.packageInfo.packageName,
                // register the WebView package as a dependency for the current application so that
                    PackageManager.GET_SHARED_LIBRARY_FILES
                // when the WebView package is updated this application will be killed.
                ApplicationInfo applicationInfo =
                    initialApplication.getPackageManager().getApplicationInfo(
                        sPackageInfo.packageName, PackageManager.GET_SHARED_LIBRARY_FILES
                    | PackageManager.MATCH_DEBUG_TRIAGED_MISSING
                    | PackageManager.MATCH_DEBUG_TRIAGED_MISSING
                        // make sure that we fetch the current provider even if its not installed
                    // Make sure that we fetch the current provider even if its not
                        // for the current user
                    // installed for the current user
                        | PackageManager.MATCH_UNINSTALLED_PACKAGES);
                    | PackageManager.MATCH_UNINSTALLED_PACKAGES
                    // Fetch signatures for verification
                    | PackageManager.GET_SIGNATURES
                    // Get meta-data for meta data flag verification
                    | PackageManager.GET_META_DATA);
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
            }

            // Validate the newly fetched package info, throws MissingWebViewPackageException on
            // failure
            verifyPackageInfo(response.packageInfo, newPackageInfo);

            Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
            Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
                    "initialApplication.createApplicationContext");
                    "initialApplication.createApplicationContext");
            try {
            try {
                    webViewContext = initialApplication.createApplicationContext(applicationInfo,
                // Construct an app context to load the Java code into the current app.
                Context webViewContext = initialApplication.createApplicationContext(
                        newPackageInfo.applicationInfo,
                        Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
                        Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
                sPackageInfo = response.packageInfo;
                return webViewContext;
            } finally {
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
            }
            }
            } catch (PackageManager.NameNotFoundException e) {
        } catch (RemoteException | PackageManager.NameNotFoundException e) {
                throw new MissingWebViewPackageException(e);
            throw new MissingWebViewPackageException("Failed to load WebView provider: " + e);
        }
    }

    private static Class<WebViewFactoryProvider> getProviderClass() {
        Context webViewContext = null;
        Application initialApplication = AppGlobals.getInitialApplication();

        try {
            Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
                    "WebViewFactory.getWebViewContextAndSetProvider()");
            try {
                webViewContext = getWebViewContextAndSetProvider();
            } finally {
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
            }
            }
            Log.i(LOGTAG, "Loading " + sPackageInfo.packageName + " version " +
                    sPackageInfo.versionName + " (code " + sPackageInfo.versionCode + ")");


            Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getChromiumProviderClass()");
            Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getChromiumProviderClass()");
            try {
            try {