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

Commit ef40c9ac authored by Gustav Sennton's avatar Gustav Sennton Committed by Android (Google) Code Review
Browse files

Merge "Simplify WebViewProviderInfo - move its logic into WebViewUpdateService." into nyc-dev

parents 7492e75b dbf5eb04
Loading
Loading
Loading
Loading
+8 −118
Original line number Diff line number Diff line
@@ -16,32 +16,16 @@

package android.webkit;

import android.app.AppGlobals;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AndroidRuntimeException;
import android.util.Base64;

import java.util.Arrays;

/** @hide */
public class WebViewProviderInfo implements Parcelable {
public final class WebViewProviderInfo implements Parcelable {

    /**
     * @hide
     */
    public static class WebViewPackageNotFoundException extends AndroidRuntimeException {
        public WebViewPackageNotFoundException(String message) { super(message); }
        public WebViewPackageNotFoundException(Exception e) { super(e); }
    }

    public WebViewProviderInfo(String packageName, String description, boolean availableByDefault,
            boolean isFallback, String[] signatures) {
    public WebViewProviderInfo(String packageName, String description,
            boolean availableByDefault, boolean isFallback, String[] signatures) {
        this.packageName = packageName;
        this.description = description;
        this.availableByDefault = availableByDefault;
@@ -49,92 +33,6 @@ public class WebViewProviderInfo implements Parcelable {
        this.signatures = signatures;
    }

    private boolean hasValidSignature() {
        if (Build.IS_DEBUGGABLE)
            return true;
        Signature[] packageSignatures;
        try {
            // If no signature is declared, instead check whether the package is included in the
            // system.
            if (signatures == null || signatures.length == 0)
                return getPackageInfo().applicationInfo.isSystemApp();

            packageSignatures = getPackageInfo().signatures;
        } catch (WebViewPackageNotFoundException e) {
            return false;
        }
        if (packageSignatures.length != 1)
            return false;

        final byte[] packageSignature = packageSignatures[0].toByteArray();
        // Return whether the package signature matches any of the valid signatures
        for (String signature : signatures) {
            final byte[] validSignature = Base64.decode(signature, Base64.DEFAULT);
            if (Arrays.equals(packageSignature, validSignature))
                return true;
        }
        return false;
    }

    /**
     * Returns whether this provider is valid for use as a WebView provider.
     */
    public boolean isValidProvider() {
        ApplicationInfo applicationInfo;
        try {
            applicationInfo = getPackageInfo().applicationInfo;
        } catch (WebViewPackageNotFoundException e) {
            return false;
        }
        if (hasValidSignature() && WebViewFactory.getWebViewLibrary(applicationInfo) != null) {
            return true;
        }
        return false;
    }

    /**
     * Returns whether this package is enabled.
     * This state can be changed by the user from Settings->Apps
     */
    public boolean isEnabled() {
        try {
            // Explicitly fetch up-to-date package info here since the enabled-state of the package
            // might have changed since we last fetched its package info.
            updatePackageInfo();
            return getPackageInfo().applicationInfo.enabled;
        } catch (WebViewPackageNotFoundException e) {
            return false;
        }
    }

    /**
     * Returns whether the provider is always available as long as it is valid.
     * If this returns false, the provider will only be used if the user chose this provider.
     */
    public boolean isAvailableByDefault() {
        return availableByDefault;
    }

    public boolean isFallbackPackage() {
        return isFallback;
    }

    private void updatePackageInfo() {
        try {
            PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
            packageInfo = pm.getPackageInfo(packageName, PACKAGE_FLAGS);
        } catch (PackageManager.NameNotFoundException e) {
            throw new WebViewPackageNotFoundException(e);
        }
    }

    public PackageInfo getPackageInfo() {
        if (packageInfo == null) {
            updatePackageInfo();
        }
        return packageInfo;
    }

    // aidl stuff
    public static final Parcelable.Creator<WebViewProviderInfo> CREATOR =
        new Parcelable.Creator<WebViewProviderInfo>() {
@@ -153,7 +51,6 @@ public class WebViewProviderInfo implements Parcelable {
        availableByDefault = (in.readInt() > 0);
        isFallback = (in.readInt() > 0);
        signatures = in.createStringArray();
        packageInfo = null;
    }

    @Override
@@ -171,16 +68,9 @@ public class WebViewProviderInfo implements Parcelable {
    }

    // fields read from framework resource
    public String packageName;
    public String description;
    private boolean availableByDefault;
    private boolean isFallback;

    private String[] signatures;

    private PackageInfo packageInfo;

    // flags declaring we want extra info from the package manager
    private final static int PACKAGE_FLAGS = PackageManager.GET_META_DATA
            | PackageManager.GET_SIGNATURES | PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
    public final String packageName;
    public final String description;
    public final boolean availableByDefault;
    public final boolean isFallback;
    public final String[] signatures;
}
+124 −55
Original line number Diff line number Diff line
@@ -26,9 +26,11 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
import android.content.pm.UserInfo;
import android.os.Binder;
import android.os.Build;
import android.os.PatternMatcher;
import android.os.Process;
import android.os.RemoteException;
@@ -38,6 +40,7 @@ import android.os.UserManager;
import android.provider.Settings.Global;
import android.provider.Settings;
import android.util.AndroidRuntimeException;
import android.util.Base64;
import android.util.Slog;
import android.webkit.IWebViewUpdateService;
import android.webkit.WebViewFactory;
@@ -72,8 +75,6 @@ public class WebViewUpdateService extends SystemService {

    // The WebView package currently in use (or the one we are preparing).
    private PackageInfo mCurrentWebViewPackage = null;
    // The WebView providers that are currently available.
    private WebViewProviderInfo[] mCurrentValidWebViewPackages = null;

    private BroadcastReceiver mWebViewUpdatedReceiver;
    private WebViewUtilityInterface mWebViewUtility;
@@ -126,7 +127,6 @@ public class WebViewUpdateService extends SystemService {
                            PackageInfo newPackage = null;
                            synchronized(WebViewUpdateService.this) {
                                try {
                                    updateValidWebViewPackages();
                                    newPackage = findPreferredWebViewPackage();
                                    if (mCurrentWebViewPackage != null)
                                        oldProviderName = mCurrentWebViewPackage.packageName;
@@ -180,12 +180,18 @@ public class WebViewUpdateService extends SystemService {
        publishBinderService("webviewupdate", new BinderService(), true /*allowIsolated*/);
    }

    private static boolean existsValidNonFallbackProvider(WebViewProviderInfo[] providers) {
    private boolean existsValidNonFallbackProvider(WebViewProviderInfo[] providers) {
        for (WebViewProviderInfo provider : providers) {
            if (provider.isAvailableByDefault() && provider.isEnabled()
                    && provider.isValidProvider() && !provider.isFallbackPackage()) {
            if (provider.availableByDefault && !provider.isFallback) {
                try {
                    PackageInfo packageInfo = getPackageInfoForProvider(provider);
                    if (isEnabledPackage(packageInfo) && isValidProvider(provider, packageInfo)) {
                        return true;
                    }
                } catch (NameNotFoundException e) {
                    // A non-existent provider is neither valid nor enabled
                }
            }
        }
        return false;
    }
@@ -211,11 +217,9 @@ public class WebViewUpdateService extends SystemService {
        WebViewProviderInfo[] webviewProviders = mWebViewUtility.getWebViewPackages();
        WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
        if (fallbackProvider == null) return;
        boolean existsValidNonFallbackProvider =
            existsValidNonFallbackProvider(webviewProviders);

        enablePackageForUser(fallbackProvider.packageName, !existsValidNonFallbackProvider,
                userId);
        enablePackageForUser(fallbackProvider.packageName,
                !existsValidNonFallbackProvider(webviewProviders), userId);
    }

    /**
@@ -236,7 +240,7 @@ public class WebViewUpdateService extends SystemService {
            for (WebViewProviderInfo provider : webviewProviders) {
                String webviewPackage = "package:" + provider.packageName;
                if (webviewPackage.equals(intent.getDataString())) {
                    if (provider.isAvailableByDefault()) {
                    if (provider.availableByDefault) {
                        changedPackage = provider.packageName;
                    }
                    break;
@@ -251,10 +255,16 @@ public class WebViewUpdateService extends SystemService {
        if (fallbackProvider == null) return;
        boolean existsValidNonFallbackProvider = existsValidNonFallbackProvider(webviewProviders);

        boolean isFallbackEnabled = false;
        try {
            isFallbackEnabled = isEnabledPackage(getPackageInfoForProvider(fallbackProvider));
        } catch (NameNotFoundException e) {
        }

        if (existsValidNonFallbackProvider
                // During an OTA the primary user's WebView state might differ from other users', so
                // ignore the state of that user during boot.
                && (fallbackProvider.isEnabled() || intent == null)) {
                && (isFallbackEnabled || intent == null)) {
            // Uninstall and disable fallback package for all users.
            context.getPackageManager().deletePackage(fallbackProvider.packageName,
                    new IPackageDeleteObserver.Stub() {
@@ -273,7 +283,7 @@ public class WebViewUpdateService extends SystemService {
        } else if (!existsValidNonFallbackProvider
                // During an OTA the primary user's WebView state might differ from other users', so
                // ignore the state of that user during boot.
                && (!fallbackProvider.isEnabled() || intent==null)) {
                && (!isFallbackEnabled || intent==null)) {
            // Enable the fallback package for all users.
            UserManager userManager =
                (UserManager)context.getSystemService(Context.USER_SERVICE);
@@ -299,24 +309,13 @@ public class WebViewUpdateService extends SystemService {
     */
    private static WebViewProviderInfo getFallbackProvider(WebViewProviderInfo[] webviewPackages) {
        for (WebViewProviderInfo provider : webviewPackages) {
            if (provider.isFallbackPackage()) {
            if (provider.isFallback) {
                return provider;
            }
        }
        return null;
    }

    private static boolean containsAvailableNonFallbackProvider(
            WebViewProviderInfo[] webviewPackages) {
        for (WebViewProviderInfo provider : webviewPackages) {
            if (provider.isAvailableByDefault() && provider.isEnabled()
                    && provider.isValidProvider() && !provider.isFallbackPackage()) {
                return true;
            }
        }
        return false;
    }

    private boolean isFallbackPackage(String packageName) {
        if (packageName == null || !isFallbackLogicEnabled()) return false;

@@ -336,7 +335,6 @@ public class WebViewUpdateService extends SystemService {
        updateFallbackState(getContext(), null);
        try {
            synchronized(this) {
                updateValidWebViewPackages();
                mCurrentWebViewPackage = findPreferredWebViewPackage();
                onWebViewProviderChanged(mCurrentWebViewPackage);
            }
@@ -408,24 +406,41 @@ public class WebViewUpdateService extends SystemService {
        }
    }

    private ProviderAndPackageInfo[] getValidWebViewPackagesAndInfos() {
        WebViewProviderInfo[] allProviders = mWebViewUtility.getWebViewPackages();
        List<ProviderAndPackageInfo> providers = new ArrayList<>();
        for(int n = 0; n < allProviders.length; n++) {
            try {
                PackageInfo packageInfo = getPackageInfoForProvider(allProviders[n]);
                if (isValidProvider(allProviders[n], packageInfo)) {
                    providers.add(new ProviderAndPackageInfo(allProviders[n], packageInfo));
                }
            } catch (NameNotFoundException e) {
                // Don't add non-existent packages
            }
        }
        return providers.toArray(new ProviderAndPackageInfo[providers.size()]);
    }

    /**
     * Updates the currently valid WebView provider packages.
     * Should be used when a provider has been installed or removed.
     * @hide
     * Fetch only the currently valid WebView packages.
     **/
    private void updateValidWebViewPackages() {
        List<WebViewProviderInfo> webViewProviders  =
            new ArrayList<WebViewProviderInfo>(Arrays.asList(mWebViewUtility.getWebViewPackages()));
        Iterator<WebViewProviderInfo> it = webViewProviders.iterator();
        // remove non-valid packages
        while(it.hasNext()) {
            WebViewProviderInfo current = it.next();
            if (!current.isValidProvider())
                it.remove();
    private WebViewProviderInfo[] getValidWebViewPackages() {
        ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos();
        WebViewProviderInfo[] providers = new WebViewProviderInfo[providersAndPackageInfos.length];
        for(int n = 0; n < providersAndPackageInfos.length; n++) {
            providers[n] = providersAndPackageInfos[n].provider;
        }
        synchronized(this) {
            mCurrentValidWebViewPackages =
                webViewProviders.toArray(new WebViewProviderInfo[webViewProviders.size()]);
        return providers;
    }

    private class ProviderAndPackageInfo {
        public final WebViewProviderInfo provider;
        public final PackageInfo packageInfo;

        public ProviderAndPackageInfo(WebViewProviderInfo provider, PackageInfo packageInfo) {
            this.provider = provider;
            this.packageInfo = packageInfo;
        }
    }

@@ -437,28 +452,30 @@ public class WebViewUpdateService extends SystemService {
     * @hide
     */
    private PackageInfo findPreferredWebViewPackage() {
        WebViewProviderInfo[] providers = mCurrentValidWebViewPackages;
        ProviderAndPackageInfo[] providers = getValidWebViewPackagesAndInfos();

        String userChosenProvider = mWebViewUtility.getUserChosenWebViewProvider(getContext());

        // If the user has chosen provider, use that
        for (WebViewProviderInfo provider : providers) {
            if (provider.packageName.equals(userChosenProvider) && provider.isEnabled()) {
                return provider.getPackageInfo();
        for (ProviderAndPackageInfo providerAndPackage : providers) {
            if (providerAndPackage.provider.packageName.equals(userChosenProvider)
                    && isEnabledPackage(providerAndPackage.packageInfo)) {
                return providerAndPackage.packageInfo;
            }
        }

        // User did not choose, or the choice failed; use the most stable provider that is
        // enabled and available by default (not through user choice).
        for (WebViewProviderInfo provider : providers) {
            if (provider.isAvailableByDefault() && provider.isEnabled()) {
                return provider.getPackageInfo();
        for (ProviderAndPackageInfo providerAndPackage : providers) {
            if (providerAndPackage.provider.availableByDefault
                    && isEnabledPackage(providerAndPackage.packageInfo)) {
                return providerAndPackage.packageInfo;
            }
        }

        // Could not find any enabled package either, use the most stable provider.
        for (WebViewProviderInfo provider : providers) {
            return provider.getPackageInfo();
        for (ProviderAndPackageInfo providerAndPackage : providers) {
            return providerAndPackage.packageInfo;
        }

        mAnyWebViewInstalled = false;
@@ -466,6 +483,60 @@ public class WebViewUpdateService extends SystemService {
                "Could not find a loadable WebView package");
    }

    /**
     * Returns whether this provider is valid for use as a WebView provider.
     */
    private static boolean isValidProvider(WebViewProviderInfo configInfo,
            PackageInfo packageInfo) {
        if (providerHasValidSignature(configInfo, packageInfo) &&
                WebViewFactory.getWebViewLibrary(packageInfo.applicationInfo) != null) {
            return true;
        }
        return false;
    }

    private static boolean providerHasValidSignature(WebViewProviderInfo provider,
            PackageInfo packageInfo) {
        if (Build.IS_DEBUGGABLE)
            return true;
        Signature[] packageSignatures;
        // If no signature is declared, instead check whether the package is included in the
        // system.
        if (provider.signatures == null || provider.signatures.length == 0) {
            return packageInfo.applicationInfo.isSystemApp();
        }
        packageSignatures = packageInfo.signatures;
        if (packageSignatures.length != 1)
            return false;

        final byte[] packageSignature = packageSignatures[0].toByteArray();
        // Return whether the package signature matches any of the valid signatures
        for (String signature : provider.signatures) {
            final byte[] validSignature = Base64.decode(signature, Base64.DEFAULT);
            if (Arrays.equals(packageSignature, validSignature))
                return true;
        }
        return false;
    }

    /**
     * Returns whether the given package is enabled.
     * This state can be changed by the user from Settings->Apps
     */
    private static boolean isEnabledPackage(PackageInfo packageInfo) {
        return packageInfo.applicationInfo.enabled;
    }

    private static PackageInfo getPackageInfoForProvider(WebViewProviderInfo configInfo)
            throws NameNotFoundException {
        PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
        return pm.getPackageInfo(configInfo.packageName, PACKAGE_FLAGS);
    }

    // flags declaring we want extra info from the package manager for webview providers
    private final static int PACKAGE_FLAGS = PackageManager.GET_META_DATA
            | PackageManager.GET_SIGNATURES | PackageManager.MATCH_DEBUG_TRIAGED_MISSING;

    /**
     * Returns whether WebView is ready and is not going to go through its preparation phase again
     * directly.
@@ -593,9 +664,7 @@ public class WebViewUpdateService extends SystemService {

        @Override // Binder call
        public WebViewProviderInfo[] getValidWebViewPackages() {
            synchronized(WebViewUpdateService.this) {
                return mCurrentValidWebViewPackages;
            }
            return WebViewUpdateService.this.getValidWebViewPackages();
        }

        @Override // Binder call
+4 −4
Original line number Diff line number Diff line
@@ -87,10 +87,10 @@ public class WebViewUtilityImpl implements WebViewUtilityInterface {
                            parser.getAttributeValue(null, TAG_AVAILABILITY));
                    boolean isFallback = "true".equals(
                            parser.getAttributeValue(null, TAG_FALLBACK));
                    WebViewProviderInfo currentProvider =
                            new WebViewProviderInfo(packageName, description, availableByDefault,
                                isFallback, readSignatures(parser));
                    if (currentProvider.isFallbackPackage()) {
                    WebViewProviderInfo currentProvider = new WebViewProviderInfo(
                            packageName, description, availableByDefault, isFallback,
                            readSignatures(parser));
                    if (currentProvider.isFallback) {
                        numFallbackPackages++;
                        if (numFallbackPackages > 1) {
                            throw new AndroidRuntimeException(