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

Commit 72f54dea authored by William Loh's avatar William Loh
Browse files

Support wildcard domains for UriRelativeFilterGroups

When fetching groups for matching groups to a domain in the
DomainVericationManager, check if any groups indexed to wildcard domains
that match the given domain exists if no groups are indexed to the given
domain.

Bug: 326285683
Test: atest CtsDomainVerificationDeviceStandaloneTestCases
Change-Id: I816c7d9a8c42cd6a4f612cae093f543568e2380f
parent 384dc99e
Loading
Loading
Loading
Loading
+20 −3
Original line number Diff line number Diff line
@@ -163,14 +163,31 @@ public final class DomainVerificationManager {
    }

    /**
     * Update the URI relative filter groups for a package. All previously existing groups
     * will be cleared before the new groups will be applied.
     * Update the URI relative filter groups for a package. The groups set using this API acts
     * as an additional filtering layer during intent resolution. It does not replace any
     * existing groups that have been added to the package's intent filters either using the
     * {@link android.content.IntentFilter#addUriRelativeFilterGroup(UriRelativeFilterGroup)}
     * API or defined in the manifest.
     * <p>
     * Groups can be indexed to any domain or can be indexed for all subdomains by prefixing the
     * hostname with a wildcard (i.e. "*.example.com"). Priority will be first given to groups
     * that are indexed to the specific subdomain of the intent's data URI followed by any groups
     * indexed to wildcard subdomains. If the subdomain consists of more than one label, priority
     * will decrease corresponding to the decreasing number of subdomain labels after the wildcard.
     * For example "a.b.c.d" will match "*.b.c.d" before "*.c.d".
     * <p>
     * All previously existing groups set for a domain index using this API will be cleared when
     * new groups are set.
     *
     * @param packageName The name of the package.
     * @param domainToGroupsMap A map of domains to a list of {@link UriRelativeFilterGroup}s that
     *                         should apply to them. Groups for each domain will replace any groups
     *                         provided for that domain in a prior call to this method. Groups will
     *                         provided for that domain in a prior call to this method. To clear
     *                         existing groups, set the list to null or a empty list. Groups will
     *                         be evaluated in the order they are provided.
     *
     * @see UriRelativeFilterGroup
     * @see android.content.IntentFilter
     * @hide
     */
    @SystemApi
+37 −8
Original line number Diff line number Diff line
@@ -16,6 +16,10 @@

package com.android.server.pm.verify.domain;

import static android.content.IntentFilter.WILDCARD;

import static com.android.server.pm.verify.domain.DomainVerificationUtils.isValidDomain;

import static java.util.Collections.emptyList;
import static java.util.Collections.emptySet;

@@ -253,9 +257,18 @@ public class DomainVerificationService extends SystemService
            Map<String, List<UriRelativeFilterGroup>> domainToGroupsMap =
                    pkgState.getUriRelativeFilterGroupMap();
            for (String domain : bundle.keySet()) {
                if (!isValidDomain(domain)) {
                    continue;
                }
                ArrayList<UriRelativeFilterGroupParcel> parcels =
                        bundle.getParcelableArrayList(domain, UriRelativeFilterGroupParcel.class);
                domainToGroupsMap.put(domain, UriRelativeFilterGroup.parcelsToGroups(parcels));
                List<UriRelativeFilterGroup> groups =
                        UriRelativeFilterGroup.parcelsToGroups(parcels);
                if (groups == null || groups.isEmpty()) {
                    domainToGroupsMap.remove(domain);
                } else {
                    domainToGroupsMap.put(domain, groups);
                }
            }
        }
    }
@@ -273,28 +286,44 @@ public class DomainVerificationService extends SystemService
                Map<String, List<UriRelativeFilterGroup>> map =
                        pkgState.getUriRelativeFilterGroupMap();
                for (int i = 0; i < domains.size(); i++) {
                    if (map.containsKey(domains.get(i))) {
                        List<UriRelativeFilterGroup> groups = map.get(domains.get(i));
                        bundle.putParcelableList(domains.get(i),
                                UriRelativeFilterGroup.groupsToParcels(groups));
                    }
                }
            }
        }
        return bundle;
    }

    @NonNull
    private List<UriRelativeFilterGroup> getUriRelativeFilterGroups(@NonNull String packageName,
            @NonNull String domain) {
        List<UriRelativeFilterGroup> groups = Collections.emptyList();
        List<UriRelativeFilterGroup> groups;
        synchronized (mLock) {
            DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
            if (pkgState != null) {
                groups = pkgState.getUriRelativeFilterGroupMap().getOrDefault(domain,
                        Collections.emptyList());
            }
                Map<String, List<UriRelativeFilterGroup>> groupMap =
                        pkgState.getUriRelativeFilterGroupMap();
                groups = groupMap.get(domain);
                if (groups != null) {
                    return groups;
                }
                int first = domain.indexOf(".");
                int second = domain.indexOf('.', first + 1);
                while (first > 0 && second > 0) {
                    groups = groupMap.get(WILDCARD + domain.substring(first));
                    if (groups != null) {
                        return groups;
                    }
                    first = second;
                    second = domain.indexOf('.', second + 1);
                }
            }
        }
        return Collections.emptyList();
    }

    @NonNull
    public List<String> queryValidVerificationPackageNames() {
+40 −0
Original line number Diff line number Diff line
@@ -35,6 +35,9 @@ import java.util.regex.Matcher;

public final class DomainVerificationUtils {

    public static final int MAX_DOMAIN_LENGTH = 254;
    public static final int MAX_DOMAIN_LABEL_LENGTH = 63;

    private static final ThreadLocal<Matcher> sCachedMatcher = ThreadLocal.withInitial(
            () -> Patterns.DOMAIN_NAME.matcher(""));

@@ -108,4 +111,41 @@ public final class DomainVerificationUtils {
        appInfo.targetSdkVersion = pkg.getTargetSdkVersion();
        return appInfo;
    }

    static boolean isValidDomain(String domain) {
        if (domain.length() > MAX_DOMAIN_LENGTH || domain.equals("*")) {
            return false;
        }
        if (domain.charAt(0) == '*') {
            if (domain.charAt(1) != '.') {
                return false;
            }
            domain = domain.substring(2);
        }
        int labels = 1;
        int labelStart = -1;
        for (int i = 0; i < domain.length(); i++) {
            char c = domain.charAt(i);
            if (c == '.') {
                int labelLength = i - labelStart - 1;
                if (labelLength == 0 || labelLength > MAX_DOMAIN_LABEL_LENGTH) {
                    return false;
                }
                labelStart = i;
                labels += 1;
            } else if (!isValidDomainChar(c)) {
                return false;
            }
        }
        int lastLabelLength = domain.length() - labelStart - 1;
        if (lastLabelLength == 0 || lastLabelLength > 63) {
            return false;
        }
        return labels > 1;
    }

    private static boolean isValidDomainChar(char c) {
        return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
                || (c >= '0' && c <= '9') || c == '-';
    }
}
+1 −5
Original line number Diff line number Diff line
@@ -83,11 +83,7 @@ class DomainVerificationManagerApiTest {
        }

        val bundle = service.getUriRelativeFilterGroups(PKG_ONE, listOf(DOMAIN_1, DOMAIN_2))
        assertThat(bundle.keySet()).containsExactlyElementsIn(listOf(DOMAIN_1, DOMAIN_2))
        assertThat(bundle.getParcelableArrayList(DOMAIN_1, UriRelativeFilterGroup::class.java))
            .isEmpty()
        assertThat(bundle.getParcelableArrayList(DOMAIN_2, UriRelativeFilterGroup::class.java))
            .isEmpty()
        assertThat(bundle.keySet()).isEmpty()

        val pathGroup = UriRelativeFilterGroup(UriRelativeFilterGroup.ACTION_ALLOW)
        pathGroup.addUriRelativeFilter(