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

Commit 7d014cec authored by Fabrice Di Meglio's avatar Fabrice Di Meglio
Browse files

Add IntentFilter auto verification - part 4

- add domain verification priming at boot when the PackageManagerService
singleton is created. This will mainly set the domain verification status
to INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS for all Apps that
have an IntentFilter with action VIEW and data scheme HTTP or HTTPS.

- also optimize Intent resolution by taking into account Browser Apps

Change-Id: Id8e66c9759a99e79b07051595ca89a168dc5ae0e
parent 88fa96c7
Loading
Loading
Loading
Loading
+33 −4
Original line number Diff line number Diff line
@@ -516,6 +516,38 @@ public class IntentFilter implements Parcelable {
        return ((mVerifyState & STATE_VERIFY_AUTO) == 1);
    }

    /**
     * Return if this filter handle all HTTP or HTTPS data URI or not.
     *
     * @return True if the filter handle all HTTP or HTTPS data URI. False otherwise.
     *
     * This will check if if the Intent action is {@link android.content.Intent#ACTION_VIEW} and
     * the Intent category is {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent
     * data scheme is "http" or "https" and that there is no specific host defined.
     *
     * @hide
     */
    public final boolean handleAllWebDataURI() {
        return hasWebDataURI() && (countDataAuthorities() == 0);
    }

    /**
     * Return if this filter has any HTTP or HTTPS data URI or not.
     *
     * @return True if the filter has any HTTP or HTTPS data URI. False otherwise.
     *
     * This will check if if the Intent action is {@link android.content.Intent#ACTION_VIEW} and
     * the Intent category is {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent
     * data scheme is "http" or "https".
     *
     * @hide
     */
    public final boolean hasWebDataURI() {
        return hasAction(Intent.ACTION_VIEW) &&
                hasCategory(Intent.CATEGORY_BROWSABLE) &&
                (hasDataScheme(SCHEME_HTTP) || hasDataScheme(SCHEME_HTTPS));
    }

    /**
     * Return if this filter needs to be automatically verified again its data URIs or not.
     *
@@ -530,10 +562,7 @@ public class IntentFilter implements Parcelable {
     * @hide
     */
    public final boolean needsVerification() {
        return hasAction(Intent.ACTION_VIEW) &&
                hasCategory(Intent.CATEGORY_BROWSABLE) &&
                (hasDataScheme(SCHEME_HTTP) || hasDataScheme(SCHEME_HTTPS)) &&
                getAutoVerify();
        return hasWebDataURI() && getAutoVerify();
    }

    /**
+1 −1
Original line number Diff line number Diff line
@@ -36,7 +36,7 @@ import java.util.ArrayList;

/**
 * The {@link com.android.server.pm.PackageManagerService} maintains some
 * {@link IntentFilterVerificationInfo}s for each domain / package / class name per user.
 * {@link IntentFilterVerificationInfo}s for each domain / package name.
 *
 * @hide
 */
+1 −0
Original line number Diff line number Diff line
@@ -2794,6 +2794,7 @@ public class PackageParser {
                if (!aii.hasAction(Intent.ACTION_VIEW)) continue;
                if (aii.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
                        aii.hasDataScheme(IntentFilter.SCHEME_HTTPS)) {
                    Slog.d(TAG, "hasDomainURLs:true for package:" + pkg.packageName);
                    return true;
                }
            }
+5 −5
Original line number Diff line number Diff line
@@ -144,9 +144,9 @@ public class ResolveInfo implements Parcelable {
    public boolean system;

    /**
     * @hide Does the associated IntentFilter needs verification ?
     * @hide Does the associated IntentFilter comes from a Browser ?
     */
    public boolean filterNeedsVerification;
    public boolean handleAllWebDataURI;

    private ComponentInfo getComponentInfo() {
        if (activityInfo != null) return activityInfo;
@@ -288,7 +288,7 @@ public class ResolveInfo implements Parcelable {
        resolvePackageName = orig.resolvePackageName;
        system = orig.system;
        targetUserId = orig.targetUserId;
        filterNeedsVerification = orig.filterNeedsVerification;
        handleAllWebDataURI = orig.handleAllWebDataURI;
    }

    public String toString() {
@@ -350,7 +350,7 @@ public class ResolveInfo implements Parcelable {
        dest.writeInt(targetUserId);
        dest.writeInt(system ? 1 : 0);
        dest.writeInt(noResourceId ? 1 : 0);
        dest.writeInt(filterNeedsVerification ? 1 : 0);
        dest.writeInt(handleAllWebDataURI ? 1 : 0);
    }

    public static final Creator<ResolveInfo> CREATOR
@@ -396,7 +396,7 @@ public class ResolveInfo implements Parcelable {
        targetUserId = source.readInt();
        system = source.readInt() != 0;
        noResourceId = source.readInt() != 0;
        filterNeedsVerification = source.readInt() != 0;
        handleAllWebDataURI = source.readInt() != 0;
    }
    
    public static class DisplayNameComparator
+96 −17
Original line number Diff line number Diff line
@@ -560,7 +560,6 @@ public class PackageManagerService extends IPackageManager.Stub {
                        mIntentFilterVerificationStates.get(verificationId);
                String packageName = ivs.getPackageName();
                boolean modified = false;
                ArrayList<PackageParser.ActivityIntentInfo> filters = ivs.getFilters();
                final int filterCount = filters.size();
@@ -571,9 +570,8 @@ public class PackageManagerService extends IPackageManager.Stub {
                }
                ArrayList<String> domainsList = new ArrayList<>(domainsSet);
                synchronized (mPackages) {
                    modified = mSettings.createIntentFilterVerificationIfNeededLPw(
                            packageName, domainsList);
                    if (modified) {
                    if (mSettings.createIntentFilterVerificationIfNeededLPw(
                            packageName, domainsList) != null) {
                        scheduleWriteSettingsLocked();
                    }
                }
@@ -698,8 +696,7 @@ public class PackageManagerService extends IPackageManager.Stub {
                ivs = createDomainVerificationState(verifierId, userId, verificationId,
                        packageName);
            }
            ArrayList<String> hosts = filter.getHostsList();
            if (!hasValidHosts(hosts)) {
            if (!hasValidDomains(filter)) {
                return false;
            }
            ivs.addFilter(filter);
@@ -719,17 +716,35 @@ public class PackageManagerService extends IPackageManager.Stub {
        }
    }
    private static boolean hasValidHosts(ArrayList<String> hosts) {
    private static boolean hasValidDomains(ActivityIntentInfo filter) {
        return hasValidDomains(filter, true);
    }
    private static boolean hasValidDomains(ActivityIntentInfo filter, boolean logging) {
        boolean hasHTTPorHTTPS = filter.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
                filter.hasDataScheme(IntentFilter.SCHEME_HTTPS);
        if (!hasHTTPorHTTPS) {
            if (logging) {
                Slog.d(TAG, "IntentFilter does not contain any HTTP or HTTPS data scheme");
            }
            return false;
        }
        ArrayList<String> hosts = filter.getHostsList();
        if (hosts.size() == 0) {
            if (logging) {
                Slog.d(TAG, "IntentFilter does not contain any data hosts");
            return false;
            }
            // We still return true as this is the case of any Browser
            return true;
        }
        String hostEndBase = null;
        for (String host : hosts) {
            String[] hostParts = host.split("\\.");
            // Should be at minimum a host like "example.com"
            if (hostParts.length < 2) {
                if (logging) {
                    Slog.d(TAG, "IntentFilter does not contain a valid data host name: " + host);
                }
                return false;
            }
            // Verify that we have the same ending domain
@@ -739,7 +754,9 @@ public class PackageManagerService extends IPackageManager.Stub {
                hostEndBase = hostEnd;
            }
            if (!hostEnd.equalsIgnoreCase(hostEndBase)) {
                if (logging) {
                    Slog.d(TAG, "IntentFilter does not contain the same data domains");
                }
                return false;
            }
        }
@@ -2176,6 +2193,8 @@ public class PackageManagerService extends IPackageManager.Stub {
            mIntentFilterVerifier = new IntentVerifierProxy(mContext,
                    mIntentFilterVerifierComponent);
            primeDomainVerificationsLPw(false);
        } // synchronized (mPackages)
        } // synchronized (mInstallLock)
@@ -2272,6 +2291,50 @@ public class PackageManagerService extends IPackageManager.Stub {
        return verifierComponentName;
    }
    private void primeDomainVerificationsLPw(boolean logging) {
        Slog.d(TAG, "Start priming domain verification");
        boolean updated = false;
        ArrayList<String> allHosts = new ArrayList<>();
        for (PackageParser.Package pkg : mPackages.values()) {
            final String packageName = pkg.packageName;
            if (!hasDomainURLs(pkg)) {
                if (logging) {
                    Slog.d(TAG, "No priming domain verifications for " +
                            "package with no domain URLs: " + packageName);
                }
                continue;
            }
            for (PackageParser.Activity a : pkg.activities) {
                for (ActivityIntentInfo filter : a.intents) {
                    if (hasValidDomains(filter, false)) {
                        allHosts.addAll(filter.getHostsList());
                    }
                }
            }
            if (allHosts.size() > 0) {
                allHosts.add("*");
            }
            IntentFilterVerificationInfo ivi =
                    mSettings.createIntentFilterVerificationIfNeededLPw(packageName, allHosts);
            if (ivi != null) {
                // We will always log this
                Slog.d(TAG, "Priming domain verifications for package: " + packageName);
                ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS);
                updated = true;
            }
            else {
                if (logging) {
                    Slog.d(TAG, "No priming domain verifications for package: " + packageName);
                }
            }
            allHosts.clear();
        }
        if (updated) {
            scheduleWriteSettingsLocked();
        }
        Slog.d(TAG, "End priming domain verification");
    }
    @Override
    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
            throws RemoteException {
@@ -3947,6 +4010,8 @@ public class PackageManagerService extends IPackageManager.Stub {
        }
        final int userId = UserHandle.getCallingUserId();
        ArrayList<ResolveInfo> result = new ArrayList<ResolveInfo>();
        ArrayList<ResolveInfo> neverList = new ArrayList<ResolveInfo>();
        ArrayList<ResolveInfo> matchAllList = new ArrayList<ResolveInfo>();
        synchronized (mPackages) {
            final int count = candidates.size();
            // First, try to use the domain prefered App
@@ -3959,13 +4024,28 @@ public class PackageManagerService extends IPackageManager.Stub {
                    int status = getDomainVerificationStatusLPr(ps, userId);
                    if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
                        result.add(info);
                    } else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
                        neverList.add(info);
                    }
                    // Add to the special match all list (Browser use case)
                    if (info.handleAllWebDataURI) {
                        matchAllList.add(info);
                    }
                }
            }
            // There is not much we can do, add all candidates
            // If there is nothing selected, add all candidates and remove the ones that the User
            // has explicitely put into the INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER state and
            // also remove any .
            // If there is still none after this pass, add all Browser Apps and let the User decide
            // with the Disambiguation dialog if there are several ones.
            if (result.size() == 0) {
                result.addAll(candidates);
            }
            result.removeAll(neverList);
            result.removeAll(matchAllList);
            if (result.size() == 0) {
                result.addAll(matchAllList);
            }
        }
        if (DEBUG_PREFERRED) {
            Slog.v("TAG", "Filtered results with prefered activities. New candidates count: " +
@@ -7843,7 +7923,7 @@ public class PackageManagerService extends IPackageManager.Stub {
                res.filter = info;
            }
            if (info != null) {
                res.filterNeedsVerification = info.needsVerification();
                res.handleAllWebDataURI = info.handleAllWebDataURI();
            }
            res.priority = info.getPriority();
            res.preferredOrder = activity.owner.mPreferredOrder;
@@ -11234,9 +11314,8 @@ public class PackageManagerService extends IPackageManager.Stub {
                        count++;
                    } else {
                        Slog.d(TAG, "No verification needed for IntentFilter:" + filter.toString());
                        ArrayList<String> list = filter.getHostsList();
                        if (hasValidHosts(list)) {
                            allHosts.addAll(list);
                        if (hasValidDomains(filter)) {
                            allHosts.addAll(filter.getHostsList());
                        }
                    }
                }
@@ -11251,7 +11330,7 @@ public class PackageManagerService extends IPackageManager.Stub {
            Slog.d(TAG, "No need to start any IntentFilter verification!");
            if (allHosts.size() > 0 && hasDomainURLs(pkg) &&
                    mSettings.createIntentFilterVerificationIfNeededLPw(
                            packageName, allHosts)) {
                            packageName, allHosts) != null) {
                scheduleWriteSettingsLocked();
            }
        }
Loading