显式 Intent 查找
显式 Intent 指定了 ComponentName,因此不需要遍历 IntentFilter。但“直接查找”不等于无条件返回:Android 17 仍要检查用户、组件是否启用、包是否对调用者可见、Instant App 可见性、启动场景的 exported 约束,以及 Intent 与目标 filter 是否安全匹配。
1. 显式路径的边界
| 阶段 | 真实代码 | 作用 |
|---|---|---|
| 判定 | intent.getComponent() | 是否进入直查分支 |
| 组件查找 | getActivityInfo(ComponentName, ...) | ComponentName → ActivityInfo |
| 状态过滤 | isEnabledAndMatch / shouldFilterApplication | 用户、启用、可见性 |
| Instant 过滤 | blockInstantResolution | Instant App 访问边界 |
| 安全校验 | SaferIntentUtils.enforceIntentFilterMatching | Intent 与 filter 约束 |
2. 组件判定
源码文件:frameworks/base/services/core/java/com/android/server/pm/ComputerEngine.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
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 可见性
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. 启动与包可见性
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. 结果与安全校验
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
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
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。
if (resolveComponentName().equals(component)) {
return PackageInfoUtils.generateDelegateActivityInfo(
mResolveActivity, flags,
PackageUserStateInternal.DEFAULT, userId);
}
return null;找不到真实 Activity 时,只有系统 ResolverActivity 组件名走 delegate 生成的特殊路径。其他不存在、禁用或不可见组件统一返回 null。
9. selector 校验
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. 典型失败原因
- ComponentName 拼写正确但返回 null:检查用户是否存在、包是否安装、组件是否启用。
- 同应用可以启动,其他应用不行:检查 exported 和
isCallerSameApp。 - root/system 能查到,普通应用查不到:检查
shouldFilterApplication。 - Instant App 访问失败:检查目标
FLAG_VISIBLE_TO_INSTANT_APP、显式可见标志和 MATCH flags。 - selector 场景异常:分别打印 selector 与 original Intent,确认二次
enforceIntentFilterMatching的输入。 - ResolverActivity 能返回但真实 Activity 不存在:检查
resolveComponentName()的 delegate 特殊路径。
11. 源码阅读路线
ComputerEngine.queryIntentActivitiesInternal的comp != null分支。getActivityInfo、getActivityInfoInternal、getActivityInfoInternalBody三层调用。ComponentResolverBase.getActivity:确认 ComponentName 直查 map。Settings.isEnabledAndMatch与PackageInfoUtils.generateActivityInfo:理解用户态和结果生成。shouldFilterApplication:理解 package visibility 与调用者身份。SaferIntentUtils.enforceIntentFilterMatching:确认显式结果仍需安全校验。
显式 Intent 省略的是“候选搜索”,不是“安全检查”。它把问题从全量 filter 匹配转换为 ComponentName 直查,再依次经过用户、启用、可见性、Instant App 和安全校验;任何一层失败,最终都可能只是一个空列表或 null。
