Loading core/java/android/content/pm/verify/domain/DomainVerificationManager.java +20 −3 Original line number Diff line number Diff line Loading @@ -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 Loading services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java +37 −8 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); } } } } Loading @@ -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() { Loading services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java +40 −0 Original line number Diff line number Diff line Loading @@ -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("")); Loading Loading @@ -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 == '-'; } } services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt +1 −5 Original line number Diff line number Diff line Loading @@ -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( Loading Loading
core/java/android/content/pm/verify/domain/DomainVerificationManager.java +20 −3 Original line number Diff line number Diff line Loading @@ -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 Loading
services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java +37 −8 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); } } } } Loading @@ -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() { Loading
services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java +40 −0 Original line number Diff line number Diff line Loading @@ -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("")); Loading Loading @@ -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 == '-'; } }
services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt +1 −5 Original line number Diff line number Diff line Loading @@ -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( Loading