Skip to content

queryIntentActivities

解析 Activity 查询的参数、候选生成、可见性过滤与 ResolveInfo 排序。

AndroidPMSIntent

queryIntentActivities ​

queryIntentActivities 返回的是“当前调用者可见、当前用户可用、匹配过滤器且经过后处理”的 Activity 列表,而不是所有注册了相同 action 的组件。Android 17 的查询链会处理 selector、显式组件、指定 package、跨 profile、Instant App、动态 split 和包可见性。

1. API 契约 ​

源码文件:frameworks/base/core/java/android/content/pm/PackageManager.java

java
@NonNull
public abstract List<ResolveInfo> queryIntentActivities(
        @NonNull Intent intent, int flags);

@NonNull
public abstract List<ResolveInfo> queryIntentActivitiesAsUser(
        @NonNull Intent intent, int flags, @UserIdInt int userId);

返回列表按 best-to-worst 排序,列表第一项就是 resolveActivity 通常会选择的候选。MATCH_DEFAULT_ONLY 限制过滤器必须声明 CATEGORY_DEFAULT,MATCH_ALL 则改变默认过滤策略。查询 API 返回列表,即使没有结果也返回空列表而不是 null。

2. PMS 到 Computer ​

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

java
public ParceledListSlice<ResolveInfo> queryIntentActivities(
        Intent intent, String resolvedType, long flags, int userId) {
    final Computer computer = snapshotComputer();
    final List<ResolveInfo> result =
            computer.queryIntentActivitiesInternal(
                    intent, resolvedType, flags, userId);
    return new ParceledListSlice<>(result);
}

PMS 对外入口先取得 Computer 快照,再把查询交给 ComputerEngine。快照让本次查询看到一致的包和组件状态;Binder 返回层只负责把结果包装成 ParceledListSlice。

3. 主入口前置处理 ​

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

java
public final @NonNull List<ResolveInfo> queryIntentActivitiesInternal(
        Intent intent, String resolvedType, long flags,
        long privateResolveFlags, int filterCallingUid,
        int callingPid, int userId, boolean resolveForStart,
        boolean allowDynamicSplits) {
    if (!mUserManager.exists(userId)) {
        return Collections.emptyList();
    }
    flags |= PackageManager.MATCH_QUARANTINED_COMPONENTS;
    final String instantAppPkgName =
            getInstantAppPackageName(filterCallingUid);
    enforceCrossUserPermission(
            Binder.getCallingUid(), userId, false, false,
            "query intent activities");

用户不存在时直接返回空列表。Android 17 还强制加入 MATCH_QUARANTINED_COMPONENTS,并用真实 Binder caller 做跨用户检查;filterCallingUid 则用于结果可见性。

4. selector 与显式 ​

java
final String pkgName = intent.getPackage();
Intent originalIntent = null;
ComponentName comp = intent.getComponent();
if (comp == null && intent.getSelector() != null) {
    originalIntent = intent;
    intent = intent.getSelector();
    comp = intent.getComponent();
}
flags = updateFlagsForResolve(
        flags, userId, filterCallingUid, resolveForStart,
        comp != null || pkgName != null,
        isImplicitImageCaptureIntentAndNotSetByDpc(
                intent, userId, resolvedType, flags));

selector 只用于本次候选查询,原 Intent 被保留,查询结束后还要验证结果是否满足原 Intent。component 和 package 都属于“显式约束”,会影响 flags 修正和后续查询分支。

java
if (comp != null) {
    final ActivityInfo ai = getActivityInfo(comp, flags, userId);
    if (ai != null && !blockInstantResolution
            && !blockNormalResolution) {
        final ResolveInfo ri = new ResolveInfo();
        ri.activityInfo = ai;
        ri.userHandle = UserHandle.of(userId);
        list = new ArrayList<>(1);
        list.add(ri);
        SaferIntentUtils.enforceIntentFilterMatching(args, list);
    }
}

显式组件不遍历所有 filters,但仍检查 instant app 可见性、包可见性和安全 filter matching;不存在或被阻止时返回空列表。

5. 全局隐式查询 ​

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

java
if (pkgName == null) {
    if (!mCrossProfileIntentResolverEngine.shouldSkipCurrentProfile(
            this, intent, resolvedType, userId)) {
        final List<ResolveInfo> queryResult =
                mComponentResolver.queryActivities(
                        this, intent, resolvedType, flags, userId);
        if (queryResult != null) {
            result.addAll(filterIfNotSystemUser(queryResult, userId));
        }
    }
    addInstant = isInstantAppResolutionAllowed(
            intent, result, userId, false, flags);
    boolean hasNonNegativePriorityResult =
            hasNonNegativePriority(result);
    crossProfileResults = mCrossProfileIntentResolverEngine.resolveIntent(
            this, intent, resolvedType, userId, flags, pkgName,
            hasNonNegativePriorityResult, resolveForStart,
            mSettings::getPackage);
}

当前 profile 查询可能被 SKIP_CURRENT_PROFILE 跳过;结果还会经过 system user 过滤。只有当前结果没有非负 priority 候选时,Instant App 解析才更可能被加入。随后 CrossProfileIntentResolverEngine 查询关联 profile。

6. 包内查询 ​

java
} else {
    final PackageStateInternal setting =
            getPackageStateInternal(pkgName, Process.SYSTEM_UID);
    if (setting != null && setting.getAndroidPackage() != null
            && (resolveForStart
            || !shouldFilterApplication(
                    setting, filterCallingUid, userId))) {
        final List<ResolveInfo> queryResult =
                mComponentResolver.queryActivities(
                        this, intent, resolvedType, flags,
                        setting.getAndroidPackage().getActivities(),
                        userId);
        if (queryResult != null) {
            result.addAll(filterIfNotSystemUser(queryResult, userId));
        }
    }
    if (result.isEmpty()) {
        addInstant = isInstantAppResolutionAllowed(
                intent, null, userId, true, flags);
    }
}

指定 package 时只在该包的 Activity 列表中匹配,不扫描全局 resolver。包不可见时普通查询为空,但 resolveForStart 有不同的过滤语义;指定 package 仍可能尝试 Instant App 结果。

7. 跨 profile 合并 ​

java
return mCrossProfileIntentResolverEngine
        .combineFilterAndCreateQueryActivitiesResponse(
                this, intent, resolvedType, instantAppPkgName,
                pkgName, allowDynamicSplits, flags, userId,
                filterCallingUid, resolveForStart, result,
                crossProfileResults,
                areWebInstantAppsDisabled(userId), addInstant,
                sortResult, mSettings::getPackage);

合并函数负责把当前 profile、关联 profile、Instant App 和域名偏好组合成最终候选。sortResult 在 Web URI 或存在跨 profile 结果时开启,不能假设 ComponentResolver 返回的局部顺序就是最终顺序。

8. 后置过滤 ​

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

java
public final List<ResolveInfo> applyPostResolutionFilter(
        List<ResolveInfo> resolveInfos, String ephemeralPkgName,
        boolean allowDynamicSplits, int filterCallingUid,
        boolean resolveForStart, int userId, Intent intent) {
    final boolean blockInstant = intent.isWebIntent()
            && areWebInstantAppsDisabled(userId);
    for (int i = resolveInfos.size() - 1; i >= 0; i--) {
        final ResolveInfo info = resolveInfos.get(i);
        if (info.isInstantAppAvailable && blockInstant) {
            resolveInfos.remove(i);
            continue;
        }
        if (allowDynamicSplits && info.activityInfo != null
                && info.activityInfo.splitName != null
                && !ArrayUtils.contains(
                        info.activityInfo.applicationInfo.splitNames,
                        info.activityInfo.splitName)) {
            if (instantAppInstallerActivity() == null) {
                resolveInfos.remove(i);
                continue;
            }
            // 用 Instant App installer 替换尚未安装的 split 结果
        }
    }
    return resolveInfos;
}

Web Instant App 被禁用时先移除对应结果;缺少动态 split 时,如果有 installer,就把候选替换为安装器 ResolveInfo,否则移除。这个后处理发生在 filter match 之后,所以“匹配成功但列表没有原 Activity”可能是动态 split 或 Web Instant App 规则造成的。

9. 结果进入列表 ​

底层 IntentResolver 按 action/type/scheme 选择候选桶,并在 buildResolveList 中调用 IntentFilter.match。匹配成功后由 Activity resolver 生成 ResolveInfo;MATCH_DEFAULT_ONLY 还要求 filter 拥有 CATEGORY_DEFAULT。同一 Activity 的多个 filter 会通过 allowFilterResult 去重。

java
match = intentFilter.match(action, resolvedType, scheme, data, categories, TAG);
if (match >= 0) {
    if (!defaultOnly ||
            intentFilter.hasCategory(Intent.CATEGORY_DEFAULT)) {
        final R oneResult = newResult(
                computer, filter, match, userId, customFlags);
        if (oneResult != null) dest.add(oneResult);
    }
}

queryIntentActivities 的排序输入来自 ResolveInfo.priority、preferredOrder、isDefault、match 和 system 属性。PMS 的最终列表还可能因跨 profile、域名验证和 package visibility 改变。

10. 排查清单 ​

  1. 先确认目标 user 存在,且调用者有跨用户查询权限。
  2. 检查 Intent 是否带 selector、component 或 package;它们决定查询分支。
  3. 查看 action/type/scheme/category 是否进入正确 resolver 候选桶。
  4. 检查 MATCH_DEFAULT_ONLY 是否丢弃没有 DEFAULT category 的 filter。
  5. 检查 stopped、包可见性、system user 和 instant app 过滤。
  6. 对工作资料场景检查 CrossProfileIntentResolverEngine 是否跳过当前 profile。
  7. 对 Web/动态 feature 场景检查 domain approval、Web Instant App 开关和 split installer。
  8. 需要解释顺序时,比较 ResolveInfo 的 priority、preferredOrder、isDefault 和 match,而不是只看包名。

11. 源码阅读路线 ​

  1. PackageManager 的 queryIntentActivities 契约。
  2. PackageManagerService 的 Binder 转发和 snapshotComputer()。
  3. ComputerEngine.queryIntentActivitiesInternal:前置 flags、selector 和显式分流。
  4. queryIntentActivitiesInternalBody:当前 profile、指定 package 与跨 profile。
  5. applyPostResolutionFilter:Instant App、动态 split 和最终可见性。
  6. ComponentResolver、IntentResolver 和 IntentFilter.match:候选生成与底层匹配。

queryIntentActivities 的结果是多个阶段共同收敛的产物:注册索引只负责产生候选,Computer 负责用户和包范围,后置过滤负责可见性与动态安装,最终列表才具有调用方可以观察到的语义。