Skip to content

DomainVerification

分析 Web Intent 的域名收集、批准级别、验证状态与解析筛选关系。

AndroidPMSIntentApp Links

DomainVerification ​

域名验证不是给 IntentFilter 加一个“可信”布尔值。Android 17 将 Web Intent 的域名声明收集到包状态,为每个包/域名维护批准级别,并在解析阶段用批准级别筛选候选。autoVerify 只是触发验证收集的条件,真正决定解析结果的是 DomainVerificationManagerInternal 的 approval level。

1. 三层状态 ​

层代表类回答的问题
ManifestDomainVerificationCollector包声明了哪些有效 Web 域名
验证状态DomainVerificationService域名是否被验证器或用户批准
Intent 解析ComputerEngine / ResolveIntentHelper哪个候选能优先处理 URI

2. 有效域名收集 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java

java
public ArraySet<String> collectAllWebDomains(
        @NonNull AndroidPackage pkg) {
    return collectDomains(pkg, false, true);
}

public ArraySet<String> collectValidAutoVerifyDomains(
        @NonNull AndroidPackage pkg) {
    return collectDomains(pkg, true, true);
}

public ArraySet<String> collectInvalidAutoVerifyDomains(
        @NonNull AndroidPackage pkg) {
    return collectDomains(pkg, true, false);
}

collectAllWebDomains 收集所有 Web filter 域名;collectValidAutoVerifyDomains 还要求 autoVerify=true;invalid 版本专门返回配置了 autoVerify 但 host 不是有效 HTTP 域名的条目。

java
if (checkAutoVerify && !intentFilter.getAutoVerify()) {
    continue;
}
if (!intentFilter.hasCategory(Intent.CATEGORY_DEFAULT)
        || !intentFilter.handlesWebUris(checkAutoVerify)) {
    continue;
}
for (int index = 0;
        index < intentFilter.countDataAuthorities(); index++) {
    String host = intentFilter.getDataAuthority(index).getHost();
    if (isValidHost(host) == valid) {
        domainCollector.apply(initialValue, host);
    }
}

验证收集要求 DEFAULT category 和 Web URI;autoVerify 收集还要求 filter 自身启用 autoVerify。域名按 authority host 提取,并通过域名格式校验和总字节数限制。

3. 安装时附加状态 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/InstallPackageHelper.java

java
if (!request.isArchived()) {
    if (oldPkgSetting == null || oldPkgSetting.getPkg() == null) {
        mPm.mDomainVerificationManager.addPackage(
                pkgSetting, request.getPreVerifiedDomains());
    } else {
        mPm.mDomainVerificationManager.migrateState(
                oldPkgSetting, pkgSetting,
                request.getPreVerifiedDomains());
    }
}

新包创建域名状态,更新包迁移旧状态;归档安装跳过,因为归档包没有声明可用的 App Links。安装器可以传入 preVerifiedDomains,在验证代理返回真实结果前暂时视为已验证。

4. 批准级别 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java

java
int APPROVAL_LEVEL_NOT_INSTALLED = -4;
int APPROVAL_LEVEL_DISABLED = -3;
int APPROVAL_LEVEL_UNDECLARED = -2;
int APPROVAL_LEVEL_UNVERIFIED = -1;
int APPROVAL_LEVEL_NONE = 0;
int APPROVAL_LEVEL_LEGACY_ASK = 1;
int APPROVAL_LEVEL_LEGACY_ALWAYS = 2;
int APPROVAL_LEVEL_SELECTION = 3;
int APPROVAL_LEVEL_VERIFIED = 4;
int APPROVAL_LEVEL_INSTANT_APP = 5;

级别可比较,数值越高优先级越高。负值用于解释未批准原因:未安装、禁用、未声明或验证失败;正值表示旧 API 状态、用户选择、验证成功或 Instant App 权威。

5. 批准与过滤 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java

java
public static boolean hasAnyDomainApproval(
        DomainVerificationManagerInternal manager,
        PackageStateInternal pkgSetting, Intent intent,
        long resolveInfoFlags, int userId) {
    return manager.approvalLevelForDomain(
            pkgSetting, intent, resolveInfoFlags, userId)
            > DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE;
}

源码明确说明:hasAnyDomainApproval 只适合判断“是否存在任意正批准”,不能替代 Intent 解析过滤。需要在候选列表中选择批准应用时,应调用 filterToApprovedApp,因为它还要返回最高 approval level 和对应候选。

6. Instant App 分支 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/ResolveIntentHelper.java

java
for (int i = 0; i < n; i++) {
    ResolveInfo ri = query.get(i);
    if (ri.activityInfo.applicationInfo.isInstantApp()) {
        final PackageStateInternal ps =
                computer.getPackageStateInternal(
                        ri.activityInfo.packageName);
        if (ps != null && PackageManagerServiceUtils.hasAnyDomainApproval(
                mDomainVerificationManager, ps,
                intent, flags, userId)) {
            return ri;
        }
    }
}

多候选且静态排序无法分出胜负时,通过域名批准的 Instant App 可以直接返回。这里使用 hasAnyDomainApproval 是因为候选已经由 resolver 生成,当前只需确认该 Instant App 有正批准;普通 Web 候选的整体筛选由后续 domain 逻辑负责。

7. Profile 域名 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/ComputerEngine.java

java
List<ResolveInfo> resultTargetUser =
        mComponentResolver.queryActivities(
                this, intent, resolvedType, flags, parentUserId);
for (ResolveInfo riTargetUser : resultTargetUser) {
    if (riTargetUser.handleAllWebDataURI) {
        continue;
    }
    PackageStateInternal ps =
            mSettings.getPackage(
                    riTargetUser.activityInfo.packageName);
    if (ps == null) continue;
    int approvalLevel = mDomainVerificationManager
            .approvalLevelForDomain(
                    ps, intent, flags, parentUserId);
    // 保存最高 approvalLevel
}

跨 profile 域名转发只考虑带 host 的 Web filter;handleAllWebDataURI 的通用浏览器不会作为已验证域名候选。系统保存目标 profile 中的最高批准级别,若不高于 APPROVAL_LEVEL_NONE,则不创建跨 profile 域名结果。

8. 验证器写回状态 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java

java
@DomainVerificationManager.Error
@RequiresPermission(Manifest.permission.DOMAIN_VERIFICATION_AGENT)
int setDomainVerificationStatusInternal(
        int callingUid, UUID domainSetId,
        Set<String> domains, int state)
        throws NameNotFoundException;

验证代理通过 domain set ID 和域名集合写回状态。服务会验证调用者是否为批准的 verifier、domainSetId 是否属于包、域名是否在声明集合内,再更新内部状态并安排 settings 持久化。应用本身不能凭普通 manifest 声明把状态写成 VERIFIED。

9. 卸载与用户清理 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/RemovePackageHelper.java

java
if (userId == UserHandle.USER_ALL) {
    mPm.mDomainVerificationManager.clearPackage(packageName);
    // 同步清理所有用户的包级状态
} else {
    mPm.mDomainVerificationManager.clearPackageForUser(
            packageName, userId);
    preferredActivityHelper.clearPackagePreferredActivities(
            packageName, userId);
}

卸载所有用户时删除包的域名状态;单用户卸载只清理该用户状态。域名验证和 PreferredActivity 分开维护,但卸载路径会同时清理,避免已删除包继续成为 Web Intent 的默认目标。

10. 调试顺序 ​

  1. 先确认 filter 有 VIEW、DEFAULT、BROWSABLE 和有效 http/https host。
  2. 再区分 host 是否被 autoVerify 收集,invalid host 不会进入有效验证集合。
  3. 查看包/域名的 approval level,区分 UNDECLARED、UNVERIFIED、SELECTION、VERIFIED。
  4. 解析列表中若有 Instant App,检查 hasAnyDomainApproval 和 Web Instant App 开关。
  5. 跨 profile 无结果时,检查 parent profile restriction、host filter 和最高 approval level。
  6. 卸载/更新后状态异常,检查 addPackage/migrateState 与 clearPackageForUser。

11. 源码阅读路线 ​

  1. DomainVerificationCollector:域名提取、autoVerify、host 校验和大小限制。
  2. DomainVerificationManagerInternal:approval level 与状态写入契约。
  3. InstallPackageHelper:安装/更新时创建或迁移域名状态。
  4. ComputerEngine.getCrossProfileDomainPreferredLpr:跨 profile 的批准判断。
  5. ResolveIntentHelper.chooseBestActivity:Instant App 域名批准如何影响单选。
  6. PackageManagerShellCommand 的 domain verification 命令:把内部状态映射到设备诊断输出。

域名验证的核心是“声明、验证、批准、解析”四层状态。autoVerify=true 只说明系统应该收集并验证域名;只有批准级别进入解析阶段,才会改变 Web Intent 的候选和最终目标。