diff options
| author | 2024-03-07 05:18:21 +0000 | |
|---|---|---|
| committer | 2024-03-07 05:18:21 +0000 | |
| commit | 260d9c9ec6752e48e372d8de467d065b61de49ef (patch) | |
| tree | 79dd6928b6366a23307bb8bff6eff2c73a098624 | |
| parent | 48429de1130d461675147083cc37dd16e461ae53 (diff) | |
| parent | 72f54dea247bae69c95983561898c0ae19826096 (diff) | |
Merge "Support wildcard domains for UriRelativeFilterGroups" into main
4 files changed, 98 insertions, 16 deletions
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java index 4dcc51729a61..a9084565180a 100644 --- a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java +++ b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java @@ -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 diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java index 305b087190d6..5c8215e4bfdb 100644 --- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java @@ -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,9 +286,11 @@ public class DomainVerificationService extends SystemService Map<String, List<UriRelativeFilterGroup>> map = pkgState.getUriRelativeFilterGroupMap(); for (int i = 0; i < domains.size(); i++) { - List<UriRelativeFilterGroup> groups = map.get(domains.get(i)); - bundle.putParcelableList(domains.get(i), - UriRelativeFilterGroup.groupsToParcels(groups)); + if (map.containsKey(domains.get(i))) { + List<UriRelativeFilterGroup> groups = map.get(domains.get(i)); + bundle.putParcelableList(domains.get(i), + UriRelativeFilterGroup.groupsToParcels(groups)); + } } } } @@ -285,15 +300,29 @@ public class DomainVerificationService extends SystemService @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 groups; + return Collections.emptyList(); } @NonNull diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java index 3fd00c6993cf..b8c4d22f186a 100644 --- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java @@ -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 == '-'; + } } diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt index 66e07175e7f5..c54a94eada0b 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt @@ -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( |