Skip to content

显式 Intent 查找

分析显式 Intent 的 ComponentName 直查、启用状态、包可见性与 Instant App 过滤。

AndroidPMSIntentComponentResolver

显式 Intent 查找 ​

显式 Intent 指定了 ComponentName,因此不需要遍历 IntentFilter。但“直接查找”不等于无条件返回:Android 17 仍要检查用户、组件是否启用、包是否对调用者可见、Instant App 可见性、启动场景的 exported 约束,以及 Intent 与目标 filter 是否安全匹配。

1. 显式路径的边界 ​

阶段真实代码作用
判定intent.getComponent()是否进入直查分支
组件查找getActivityInfo(ComponentName, ...)ComponentName → ActivityInfo
状态过滤isEnabledAndMatch / shouldFilterApplication用户、启用、可见性
Instant 过滤blockInstantResolutionInstant App 访问边界
安全校验SaferIntentUtils.enforceIntentFilterMatchingIntent 与 filter 约束

2. 组件判定 ​

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

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));

没有 component 的 Intent 才进入隐式 resolver。selector 会替换用于查询的 Intent,但保存原始对象;查询结束后必须再次验证原 Intent,防止 selector 让结果绕开外层 Intent 的约束。

3. 显式 fast path ​

java
List<ResolveInfo> list = Collections.emptyList();
if (comp != null) {
    final ActivityInfo ai = getActivityInfo(comp, flags, userId);
    if (ai != null) {
        final boolean matchInstantApp =
                (flags & PackageManager.MATCH_INSTANT) != 0;
        final boolean matchVisibleToInstantAppOnly =
                (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
        final boolean matchExplicitlyVisibleOnly =
                (flags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0;
        final boolean isCallerInstantApp = instantAppPkgName != null;
        final boolean isTargetSameInstantApp =
                comp.getPackageName().equals(instantAppPkgName);
        final boolean isTargetInstantApp =
                (ai.applicationInfo.privateFlags
                        & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;

显式路径只通过 ComponentName 取得 ActivityInfo,不调用 ComponentResolver.queryActivities。但 ActivityInfo 已经经过组件/包状态生成,后续仍会根据 flags 与 caller 身份决定是否能返回。

4. Instant 可见性 ​

java
final boolean isTargetVisibleToInstantApp =
        (ai.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
final boolean isTargetExplicitlyVisibleToInstantApp =
        isTargetVisibleToInstantApp
                && (ai.flags
                & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0;
final boolean isTargetHiddenFromInstantApp =
        !isTargetVisibleToInstantApp
                || (matchExplicitlyVisibleOnly
                && !isTargetExplicitlyVisibleToInstantApp);
final boolean blockInstantResolution =
        !isTargetSameInstantApp
                && ((!matchInstantApp && !isCallerInstantApp
                && isTargetInstantApp)
                || (matchVisibleToInstantAppOnly && isCallerInstantApp
                && isTargetHiddenFromInstantApp));

普通调用者默认不能解析 Instant App;Instant App 调用者只能看到显式标记为可见的组件。目标与调用者属于同一个 Instant App 时,isTargetSameInstantApp 会绕过这部分跨包限制。

5. 启动与包可见性 ​

java
final boolean resolveForStartNonExported =
        resolveForStart && !ai.exported
                && !isCallerSameApp(pkgName, filterCallingUid);
final boolean blockNormalResolution =
        (!resolveForStart || resolveForStartNonExported)
                && !isTargetInstantApp
                && !isCallerInstantApp
                && shouldFilterApplication(
                        getPackageStateInternal(
                                ai.applicationInfo.packageName,
                                Process.SYSTEM_UID),
                        filterCallingUid, userId);

启动场景下,非 exported Activity 只有同应用调用者才允许继续。包可见性过滤在非启动查询、非同应用访问或非 exported 启动条件下参与;因此同一个显式 ComponentName 在 query 与 start 场景可能出现不同结果。

6. 结果与安全校验 ​

java
if (!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);
}

显式查询最多返回一个 ResolveInfo,但仍执行 enforceIntentFilterMatching。该校验确保显式目标组件的 IntentFilter 约束没有被 selector 或调用参数绕过;所以“显式 Intent 不需要 filter”与“显式 Intent 不需要校验 filter”是两件事。

7. Info 三层查找 ​

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

java
public final ActivityInfo getActivityInfo(
        ComponentName component, long flags, int userId) {
    return getActivityInfoInternal(
            component, flags, Binder.getCallingUid(), userId);
}

public final ActivityInfo getActivityInfoInternal(
        ComponentName component, long flags,
        int filterCallingUid, int userId) {
    if (!mUserManager.exists(userId)) return null;
    flags = updateFlagsForComponent(flags, userId);
    if (!isRecentsAccessingChildProfiles(
            Binder.getCallingUid(), userId)) {
        enforceCrossUserPermission(
                Binder.getCallingUid(), userId, false, false,
                "get activity info");
    }
    return getActivityInfoInternalBody(
            component, flags, filterCallingUid, userId);
}

第一层负责用户、flags 与跨用户检查,第二层 body 才访问组件索引。Recents 访问 child profile 有专门例外,但并不等于跳过后续组件状态和包可见性检查。

8. 组件直查 ​

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

java
protected ActivityInfo getActivityInfoInternalBody(
        ComponentName component, long flags,
        int filterCallingUid, int userId) {
    final ParsedActivity a =
            mComponentResolver.getActivity(component);
    flags |= PackageManager.MATCH_QUARANTINED_COMPONENTS;
    final AndroidPackage pkg =
            a == null ? null : mPackages.get(a.getPackageName());
    if (pkg != null && mSettings.isEnabledAndMatch(
            pkg, a, flags, userId)) {
        final PackageStateInternal ps =
                mSettings.getPackage(component.getPackageName());
        if (ps == null) return null;
        if (shouldFilterApplication(
                ps, filterCallingUid, component,
                TYPE_ACTIVITY, userId)) {
            return null;
        }
        return PackageInfoUtils.generateActivityInfo(
                pkg, a, flags,
                ps.getUserStateOrDefault(userId), userId, ps);
    }

直查第一步是 ComponentResolver.getActivity 的 ComponentName map;第二步确认对应 AndroidPackage;第三步 isEnabledAndMatch 检查用户态和 flags;第四步执行 AppsFilter 包可见性;最后才生成 ActivityInfo。

java
if (resolveComponentName().equals(component)) {
    return PackageInfoUtils.generateDelegateActivityInfo(
            mResolveActivity, flags,
            PackageUserStateInternal.DEFAULT, userId);
}
return null;

找不到真实 Activity 时,只有系统 ResolverActivity 组件名走 delegate 生成的特殊路径。其他不存在、禁用或不可见组件统一返回 null。

9. selector 校验 ​

java
if (originalIntent != null) {
    args.intent = originalIntent;
    SaferIntentUtils.enforceIntentFilterMatching(args, list);
}
return skipPostResolution ? list : applyPostResolutionFilter(
        list, instantAppPkgName, allowDynamicSplits,
        filterCallingUid, resolveForStart, userId, intent);

selector 查询成功不代表原始 Intent 一定可以使用该组件。helper 在返回前把 args.intent 恢复为 originalIntent,重新执行安全匹配;之后仍可能经过动态 split、Web Instant App 和可见性后处理。

10. 典型失败原因 ​

  1. ComponentName 拼写正确但返回 null:检查用户是否存在、包是否安装、组件是否启用。
  2. 同应用可以启动,其他应用不行:检查 exported 和 isCallerSameApp。
  3. root/system 能查到,普通应用查不到:检查 shouldFilterApplication。
  4. Instant App 访问失败:检查目标 FLAG_VISIBLE_TO_INSTANT_APP、显式可见标志和 MATCH flags。
  5. selector 场景异常:分别打印 selector 与 original Intent,确认二次 enforceIntentFilterMatching 的输入。
  6. ResolverActivity 能返回但真实 Activity 不存在:检查 resolveComponentName() 的 delegate 特殊路径。

11. 源码阅读路线 ​

  1. ComputerEngine.queryIntentActivitiesInternal 的 comp != null 分支。
  2. getActivityInfo、getActivityInfoInternal、getActivityInfoInternalBody 三层调用。
  3. ComponentResolverBase.getActivity:确认 ComponentName 直查 map。
  4. Settings.isEnabledAndMatch 与 PackageInfoUtils.generateActivityInfo:理解用户态和结果生成。
  5. shouldFilterApplication:理解 package visibility 与调用者身份。
  6. SaferIntentUtils.enforceIntentFilterMatching:确认显式结果仍需安全校验。

显式 Intent 省略的是“候选搜索”,不是“安全检查”。它把问题从全量 filter 匹配转换为 ComponentName 直查,再依次经过用户、启用、可见性、Instant App 和安全校验;任何一层失败,最终都可能只是一个空列表或 null。