Merge "Fix domain collection for wildcards" into sc-dev
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
index 36efb39..080de73 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
@@ -21,11 +21,8 @@
 import android.compat.annotation.EnabledSince;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
 import android.content.pm.parsing.component.ParsedActivity;
 import android.content.pm.parsing.component.ParsedIntentInfo;
-import android.os.Binder;
 import android.os.Build;
 import android.util.ArraySet;
 import android.util.Patterns;
@@ -36,19 +33,31 @@
 
 import java.util.List;
 import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 public class DomainVerificationCollector {
 
+    // The default domain name matcher doesn't account for wildcards, so prefix with *.
+    private static final Pattern DOMAIN_NAME_WITH_WILDCARD =
+            Pattern.compile("(\\*\\.)?" + Patterns.DOMAIN_NAME.pattern());
+
     @NonNull
     private final PlatformCompat mPlatformCompat;
 
     @NonNull
     private final SystemConfig mSystemConfig;
 
+    @NonNull
+    private final Matcher mDomainMatcher;
+
     public DomainVerificationCollector(@NonNull PlatformCompat platformCompat,
             @NonNull SystemConfig systemConfig) {
         mPlatformCompat = platformCompat;
         mSystemConfig = systemConfig;
+
+        // Cache the matcher to avoid calling into native on each check
+        mDomainMatcher = DOMAIN_NAME_WITH_WILDCARD.matcher("");
     }
 
     /**
@@ -144,7 +153,10 @@
                 if (intent.handlesWebUris(false)) {
                     int authorityCount = intent.countDataAuthorities();
                     for (int index = 0; index < authorityCount; index++) {
-                        domains.add(intent.getDataAuthority(index).getHost());
+                        String host = intent.getDataAuthority(index).getHost();
+                        if (isValidHost(host)) {
+                            domains.add(host);
+                        }
                     }
                 }
             }
@@ -188,13 +200,22 @@
                 int authorityCount = intent.countDataAuthorities();
                 for (int index = 0; index < authorityCount; index++) {
                     String host = intent.getDataAuthority(index).getHost();
-                    // It's easy to misconfigure autoVerify intent filters, so to avoid
-                    // adding unintended hosts, check if the host is an HTTP domain.
-                    if (Patterns.DOMAIN_NAME.matcher(host).matches()) {
+                    if (isValidHost(host)) {
                         domains.add(host);
                     }
                 }
             }
         }
     }
+
+    /**
+     * It's easy to mis-configure autoVerify intent filters, so to avoid adding unintended hosts,
+     * check if the host is an HTTP domain. This applies for both legacy and modern versions of
+     * the API, which will strip invalid hosts from the legacy parsing result. This is done to
+     * improve the reliability of any legacy verifiers.
+     */
+    private boolean isValidHost(String host) {
+        mDomainMatcher.reset(host);
+        return mDomainMatcher.matches();
+    }
 }
diff --git a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
index 9389e63..a804065 100644
--- a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
+++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
@@ -30,7 +30,6 @@
 import android.content.pm.verify.domain.DomainVerificationState;
 import android.os.Process;
 import android.os.UserHandle;
-import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Pair;
@@ -45,6 +44,7 @@
 
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.UUID;
@@ -168,22 +168,58 @@
                     return true;
                 }
 
-                Set<String> successfulDomains = new ArraySet<>(info.getHostToStateMap().keySet());
-                successfulDomains.removeAll(response.failedDomains);
+                AndroidPackage pkg = mConnection.getPackage(packageName);
+                if (pkg == null) {
+                    return true;
+                }
+
+                ArraySet<String> failedDomains = new ArraySet<>(response.failedDomains);
+                Map<String, Integer> hostToStateMap = info.getHostToStateMap();
+                Set<String> hostKeySet = hostToStateMap.keySet();
+                ArraySet<String> successfulDomains = new ArraySet<>(hostKeySet);
+                successfulDomains.removeAll(failedDomains);
+
+                // v1 doesn't handle wildcard domains, so check them here for the verifier
+                int size = successfulDomains.size();
+                for (int index = size - 1; index >= 0; index--) {
+                    String domain = successfulDomains.valueAt(index);
+                    if (domain.startsWith("*.")) {
+                        String nonWildcardDomain = domain.substring(2);
+                        if (failedDomains.contains(nonWildcardDomain)) {
+                            failedDomains.add(domain);
+                            successfulDomains.removeAt(index);
+
+                            // It's possible to declare a wildcard without declaring its
+                            // non-wildcard equivalent, so if it wasn't originally declared,
+                            // remove the transformed domain from the failed set. Otherwise the
+                            // manager will not accept the failed set as it contains an undeclared
+                            // domain.
+                            if (!hostKeySet.contains(nonWildcardDomain)) {
+                                failedDomains.remove(nonWildcardDomain);
+                            }
+                        }
+                    }
+                }
 
                 int callingUid = response.callingUid;
-                try {
-                    mManager.setDomainVerificationStatusInternal(callingUid, domainSetId,
-                            successfulDomains, DomainVerificationState.STATE_SUCCESS);
-                } catch (DomainVerificationManager.InvalidDomainSetException
-                        | PackageManager.NameNotFoundException ignored) {
+                if (!successfulDomains.isEmpty()) {
+                    try {
+                        mManager.setDomainVerificationStatusInternal(callingUid, domainSetId,
+                                successfulDomains, DomainVerificationState.STATE_SUCCESS);
+                    } catch (DomainVerificationManager.InvalidDomainSetException
+                            | PackageManager.NameNotFoundException e) {
+                        Slog.e(TAG, "Failure reporting successful domains for " + packageName, e);
+                    }
                 }
-                try {
-                    mManager.setDomainVerificationStatusInternal(callingUid, domainSetId,
-                            new ArraySet<>(response.failedDomains),
-                            DomainVerificationState.STATE_LEGACY_FAILURE);
-                } catch (DomainVerificationManager.InvalidDomainSetException
-                        | PackageManager.NameNotFoundException ignored) {
+
+                if (!failedDomains.isEmpty()) {
+                    try {
+                        mManager.setDomainVerificationStatusInternal(callingUid, domainSetId,
+                                failedDomains, DomainVerificationState.STATE_LEGACY_FAILURE);
+                    } catch (DomainVerificationManager.InvalidDomainSetException
+                            | PackageManager.NameNotFoundException e) {
+                        Slog.e(TAG, "Failure reporting failed domains for " + packageName, e);
+                    }
                 }
 
                 return true;
@@ -235,7 +271,21 @@
         // The collector itself handles the v1 vs v2 behavior, which is based on targetSdkVersion,
         // not the version of the verification agent on device.
         ArraySet<String> domains = mCollector.collectAutoVerifyDomains(pkg);
-        return TextUtils.join(" ", domains);
+
+        // v1 doesn't handle wildcard domains, so transform them here to the root
+        StringBuilder builder = new StringBuilder();
+        int size = domains.size();
+        for (int index = 0; index < size; index++) {
+            if (index > 0) {
+                builder.append(" ");
+            }
+            String domain = domains.valueAt(index);
+            if (domain.startsWith("*.")) {
+                domain = domain.substring(2);
+            }
+            builder.append(domain);
+        }
+        return builder.toString();
     }
 
     private static class Response {