diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index af2fdf70ca501..f15a71063b537 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -2636,7 +2636,8 @@ public class PackageManagerService extends IPackageManager.Stub // We'll want to include browser possibilities in a few cases boolean includeBrowser = false; - if (!DomainVerificationUtils.isDomainVerificationIntent(intent, matchFlags)) { + if (!DomainVerificationUtils.isDomainVerificationIntent(intent, candidates, + matchFlags)) { result.addAll(undefinedList); // Maybe add one for the other profile. if (xpDomainInfo != null && xpDomainInfo.highestApprovalLevel @@ -2820,8 +2821,8 @@ public class PackageManagerService extends IPackageManager.Stub } result.highestApprovalLevel = Math.max(mDomainVerificationManager - .approvalLevelForDomain(ps, intent, flags, riTargetUser.targetUserId), - result.highestApprovalLevel); + .approvalLevelForDomain(ps, intent, resultTargetUser, flags, + riTargetUser.targetUserId), result.highestApprovalLevel); } if (result != null && result.highestApprovalLevel <= DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE) { @@ -3067,8 +3068,8 @@ public class PackageManagerService extends IPackageManager.Stub final String packageName = info.activityInfo.packageName; final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps.getInstantApp(userId)) { - if (hasAnyDomainApproval(mDomainVerificationManager, ps, intent, flags, - userId)) { + if (hasAnyDomainApproval(mDomainVerificationManager, ps, intent, + instantApps, flags, userId)) { if (DEBUG_INSTANT) { Slog.v(TAG, "Instant app approved for intent; pkg: " + packageName); @@ -3995,8 +3996,8 @@ public class PackageManagerService extends IPackageManager.Stub if (ps != null) { // only check domain verification status if the app is not a browser if (!info.handleAllWebDataURI) { - if (hasAnyDomainApproval(mDomainVerificationManager, ps, intent, flags, - userId)) { + if (hasAnyDomainApproval(mDomainVerificationManager, ps, intent, + resolvedActivities, flags, userId)) { if (DEBUG_INSTANT) { Slog.v(TAG, "DENY instant app;" + " pkg: " + packageName + ", approved"); @@ -9575,7 +9576,7 @@ public class PackageManagerService extends IPackageManager.Stub final String packageName = ri.activityInfo.packageName; final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps != null && hasAnyDomainApproval(mDomainVerificationManager, ps, - intent, flags, userId)) { + intent, query, flags, userId)) { return ri; } } @@ -9632,10 +9633,10 @@ public class PackageManagerService extends IPackageManager.Stub */ private static boolean hasAnyDomainApproval( @NonNull DomainVerificationManagerInternal manager, @NonNull PackageSetting pkgSetting, - @NonNull Intent intent, @PackageManager.ResolveInfoFlags int resolveInfoFlags, - @UserIdInt int userId) { - return manager.approvalLevelForDomain(pkgSetting, intent, resolveInfoFlags, userId) - > DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE; + @NonNull Intent intent, @NonNull List candidates, + @PackageManager.ResolveInfoFlags int resolveInfoFlags, @UserIdInt int userId) { + return manager.approvalLevelForDomain(pkgSetting, intent, candidates, resolveInfoFlags, + userId) > DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE; } /** diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java index 9e22d82910df1..a68b3da0f0b68 100644 --- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java @@ -310,6 +310,7 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan */ @ApprovalLevel int approvalLevelForDomain(@NonNull PackageSetting pkgSetting, @NonNull Intent intent, + @NonNull List candidates, @PackageManager.ResolveInfoFlags int resolveInfoFlags, @UserIdInt int userId); /** 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 b58c1ff374d52..dbf55576e38b7 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 @@ -1494,9 +1494,11 @@ public class DomainVerificationService extends SystemService @Override public int approvalLevelForDomain(@NonNull PackageSetting pkgSetting, @NonNull Intent intent, + @NonNull List candidates, @PackageManager.ResolveInfoFlags int resolveInfoFlags, @UserIdInt int userId) { String packageName = pkgSetting.getName(); - if (!DomainVerificationUtils.isDomainVerificationIntent(intent, resolveInfoFlags)) { + if (!DomainVerificationUtils.isDomainVerificationIntent(intent, candidates, + resolveInfoFlags)) { if (DEBUG_APPROVAL) { debugApproval(packageName, intent, userId, false, "not valid intent"); } 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 783aff6ccb55f..883bbad1bd2da 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 @@ -22,12 +22,17 @@ import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ResolveInfo; import android.os.Binder; +import com.android.internal.util.CollectionUtils; import com.android.server.compat.PlatformCompat; import com.android.server.pm.PackageManagerService; import com.android.server.pm.parsing.pkg.AndroidPackage; +import java.util.List; +import java.util.Set; + public final class DomainVerificationUtils { /** @@ -40,13 +45,61 @@ public final class DomainVerificationUtils { throw new NameNotFoundException("Package " + packageName + " unavailable"); } - public static boolean isDomainVerificationIntent(Intent intent, int resolveInfoFlags) { - if (!intent.isWebIntent() || !intent.hasCategory(Intent.CATEGORY_BROWSABLE)) { + public static boolean isDomainVerificationIntent(Intent intent, + @NonNull List candidates, + @PackageManager.ResolveInfoFlags int resolveInfoFlags) { + if (!intent.isWebIntent()) { return false; } - return ((resolveInfoFlags & PackageManager.MATCH_DEFAULT_ONLY) != 0) - || intent.hasCategory(Intent.CATEGORY_DEFAULT); + Set categories = intent.getCategories(); + int categoriesSize = CollectionUtils.size(categories); + if (categoriesSize > 2) { + // Specifying at least one non-app-link category + return false; + } else if (categoriesSize == 2) { + // Check for explicit app link intent with exactly BROWSABLE && DEFAULT + return intent.hasCategory(Intent.CATEGORY_DEFAULT) + && intent.hasCategory(Intent.CATEGORY_BROWSABLE); + } + + // In cases where at least one browser is resolved and only one non-browser is resolved, + // the Intent is coerced into an app links intent, under the assumption the browser can + // be skipped if the app is approved at any level for the domain. + boolean foundBrowser = false; + boolean foundOneApp = false; + + final int candidatesSize = candidates.size(); + for (int index = 0; index < candidatesSize; index++) { + final ResolveInfo info = candidates.get(index); + if (info.handleAllWebDataURI) { + foundBrowser = true; + } else if (foundOneApp) { + // Already true, so duplicate app + foundOneApp = false; + break; + } else { + foundOneApp = true; + } + } + + boolean matchDefaultByFlags = (resolveInfoFlags & PackageManager.MATCH_DEFAULT_ONLY) != 0; + boolean onlyOneNonBrowser = foundBrowser && foundOneApp; + + // Check if matches (BROWSABLE || none) && DEFAULT + if (categoriesSize == 0) { + // No categories, run coerce case, matching DEFAULT by flags + return onlyOneNonBrowser && matchDefaultByFlags; + } else if (intent.hasCategory(Intent.CATEGORY_DEFAULT)) { + // Run coerce case, matching by explicit DEFAULT + return onlyOneNonBrowser; + } else if (intent.hasCategory(Intent.CATEGORY_BROWSABLE)) { + // Intent matches BROWSABLE, must match DEFAULT by flags + return matchDefaultByFlags; + } else { + // Otherwise not matching any app link categories + return false; + } } static boolean isChangeEnabled(PlatformCompat platformCompat, AndroidPackage pkg,