Intent 优先级与默认应用
多个 Activity 都匹配同一个 Intent 时,Android 并不是简单地“优先级最高者永远胜出”。Android 17 先在组件注册阶段限制危险的 filter priority,再在查询阶段排序,最后由 ResolveIntentHelper.chooseBestActivity 判断是否使用用户首选项、Instant App 域名批准或 ResolverActivity。
1. 三个阶段
| 阶段 | owner | 关键状态 |
|---|---|---|
| 注册限幅 | ComponentResolver | IntentFilter.priority |
| 候选排序 | IntentResolver / resolver | ResolveInfo 字段 |
| 单结果选择 | ResolveIntentHelper | PreferredActivity / ResolverActivity |
2. 结果排序
源码文件:frameworks/base/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
public static final Comparator<ResolveInfo> RESOLVE_PRIORITY_SORTER = (r1, r2) -> {
int v1 = r1.priority;
int v2 = r2.priority;
if (v1 != v2) return (v1 > v2) ? -1 : 1;
v1 = r1.preferredOrder;
v2 = r2.preferredOrder;
if (v1 != v2) return (v1 > v2) ? -1 : 1;
if (r1.isDefault != r2.isDefault) {
return r1.isDefault ? -1 : 1;
}
v1 = r1.match;
v2 = r2.match;
if (v1 != v2) return (v1 > v2) ? -1 : 1;
if (r1.system != r2.system) return r1.system ? -1 : 1;
if (r1.activityInfo != null) {
return r1.activityInfo.packageName.compareTo(
r2.activityInfo.packageName);
}
if (r1.serviceInfo != null) {
return r1.serviceInfo.packageName.compareTo(
r2.serviceInfo.packageName);
}
if (r1.providerInfo != null) {
return r1.providerInfo.packageName.compareTo(
r2.providerInfo.packageName);
}
return 0;
};排序顺序是 priority、preferredOrder、isDefault、match、system、包名。priority 来自 filter;match 是 IntentFilter.match() 的 data 质量分数;system 只在前几项都相同才参与。因此“系统应用一定排第一”并不正确。
3. priority 限幅
源码文件:frameworks/base/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
private static final Set<String> PROTECTED_ACTIONS = new ArraySet<>();
static {
PROTECTED_ACTIONS.add(Intent.ACTION_SEND);
PROTECTED_ACTIONS.add(Intent.ACTION_SENDTO);
PROTECTED_ACTIONS.add(Intent.ACTION_SEND_MULTIPLE);
PROTECTED_ACTIONS.add(Intent.ACTION_VIEW);
}
private void adjustPriority(@NonNull Computer computer,
List<ParsedActivity> systemActivities,
ParsedActivity activity, ParsedIntentInfo intentInfo,
String setupWizardPackage) {
IntentFilter intentFilter = intentInfo.getIntentFilter();
if (intentFilter.getPriority() <= 0) return;
String packageName = activity.getPackageName();
var packageState = computer.getPackageStateInternal(packageName);
final boolean privilegedApp = packageState.isPrivileged();
if (!privilegedApp) {
intentFilter.setPriority(0);
return;
}非 privileged app 的正 priority 会被强制改为 0。这个修改发生在 filter 注册阶段,后续 ResolveInfo.priority 看到的是被限幅后的值,而不是 manifest 中原始值。
4. 受保护 action
if (isProtectedAction(intentFilter)) {
if (mDeferProtectedFilters) {
if (mProtectedFilters == null) {
mProtectedFilters = new ArrayList<>();
}
mProtectedFilters.add(Pair.create(activity, intentInfo));
} else {
if (packageName.equals(setupWizardPackage)) {
return;
}
intentFilter.setPriority(0);
}
return;
}SEND、SENDTO、SEND_MULTIPLE、VIEW 属于受保护 action。系统扫描尚未完成时,resolver 不能知道哪个包是 Setup Wizard,因此先保存 filter;扫描结束后由 fixProtectedFilterPriorities 统一处理。Setup Wizard 是唯一允许在这些 action 上保留高 priority 的例外。
5. 更新包上限
if (systemActivities == null) {
// 系统分区中的 privileged app 可使用声明的 priority
return;
}
ParsedActivity foundActivity =
findMatchingActivity(systemActivities, activity);
if (foundActivity == null) {
intentFilter.setPriority(0);
return;
}
final List<ParsedIntentInfo> intentListCopy =
new ArrayList<>(foundActivity.getIntents());未打包更新的 privileged app 不能凭新增 Activity 获得高 priority;只有系统版本中存在同名 Activity,才继续比较 filter 是否等价。
getIntentListSubset(intentListCopy,
ParsedIntentInfo::actionsIterator,
intentFilter.actionsIterator());
getIntentListSubset(intentListCopy,
ParsedIntentInfo::categoriesIterator,
intentFilter.categoriesIterator());
getIntentListSubset(intentListCopy,
ParsedIntentInfo::schemesIterator,
intentFilter.schemesIterator());
getIntentListSubset(intentListCopy,
ParsedIntentInfo::authoritiesIterator,
intentFilter.authoritiesIterator());
if (intentListCopy.size() == 0) {
intentFilter.setPriority(0);
return;
}
int cappedPriority = 0;
for (int i = intentListCopy.size() - 1; i >= 0; --i) {
cappedPriority = Math.max(cappedPriority,
intentListCopy.get(i).getIntentFilter().getPriority());
}
if (intentFilter.getPriority() > cappedPriority) {
intentFilter.setPriority(cappedPriority);
}更新包会依次比较 action、category、scheme、authority 子集;任一关键集合不等价就降为 0。等价 filter 也只能把 priority 限制在系统版本对应 filter 的最高值,不能超过出厂版本。
6. 单选消费
源码文件:frameworks/base/services/core/java/com/android/server/pm/ResolveIntentHelper.java
if (query != null) {
final int n = query.size();
if (n == 1) return query.get(0);
if (n > 1) {
ResolveInfo r0 = query.get(0);
ResolveInfo r1 = query.get(1);
if (r0.priority != r1.priority
|| r0.preferredOrder != r1.preferredOrder
|| r0.isDefault != r1.isDefault) {
return query.get(0);
}
ResolveInfo preferred =
mPreferredActivityHelper.findPreferredActivityNotLocked(
computer, intent, resolvedType, flags,
query, true, false, debug,
userId, queryMayBeFiltered);
if (preferred != null) return preferred;query 已由 resolver 排序。只有前两名的 priority、preferredOrder、isDefault 全相同,才查用户偏好;因此 PreferredActivity 不是无条件覆盖高 priority 的机制。
7. 偏好持久化
源码文件:frameworks/base/services/core/java/com/android/server/pm/PreferredActivityHelper.java
public ResolveInfo findPreferredActivityNotLocked(
@NonNull Computer snapshot, Intent intent,
String resolvedType, long flags, List<ResolveInfo> query,
boolean always, boolean removeMatches, boolean debug,
int userId, boolean queryMayBeFiltered) {
if (Thread.holdsLock(mPm.mLock)) {
Slog.wtf(TAG, "Calling thread is holding mLock", new Throwable());
}
if (!mPm.mUserManager.exists(userId)) return null;
PackageManagerService.FindPreferredActivityBodyResult body =
snapshot.findPreferredActivityInternal(
intent, resolvedType, flags, query,
always, removeMatches, debug,
userId, queryMayBeFiltered);
if (body.mChanged) {
mPm.scheduleWritePackageRestrictions(userId);
}
return body.mPreferredResolveInfo;
}PreferredActivity 查询要求不持有 PMS 主锁,使用 Computer 快照查找;如果查询过程中清理了过期偏好,才调度 package-restrictions 持久化。读取偏好本身不是纯字符串比较,还要与当前 query 候选集合和 userId 对齐。
8. 添加默认偏好
public void addPreferredActivity(@NonNull Computer snapshot,
WatchedIntentFilter filter, int match,
ComponentName[] set, ComponentName activity,
boolean always, int userId,
String opname, boolean removeExisting) {
final int callingUid = Binder.getCallingUid();
snapshot.enforceCrossUserPermission(
callingUid, userId, true, false,
"add preferred activity");
if (mPm.mContext.checkCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
!= PackageManager.PERMISSION_GRANTED) {
mPm.mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
}
if (filter.countActions() == 0) {
Slog.w(TAG, "Cannot set a preferred activity with no filter actions");
return;
}写入偏好需要跨用户权限和 SET_PREFERRED_APPLICATIONS。过滤器没有 action 时直接拒绝,因为没有稳定的 Intent 匹配键。
synchronized (mPm.mLock) {
final PreferredIntentResolver pir =
mPm.mSettings.editPreferredActivitiesLPw(userId);
final ArrayList<PreferredActivity> existing =
pir.findFilters(filter);
if (removeExisting && existing != null) {
Settings.removeFilters(pir, filter, existing);
}
pir.addFilter(mPm.snapshotComputer(),
new PreferredActivity(filter, match, set, activity, always));
mPm.scheduleWritePackageRestrictions(userId);
}写入在锁内修改该用户的 PreferredIntentResolver,随后写入 package restrictions。always 决定“始终使用”还是临时 last-chosen 语义;set 保存当时参与选择的完整候选集合,用于之后验证偏好是否仍适用。
9. ResolverActivity
if ((privateResolveFlags
& PackageManagerInternal.RESOLVE_NON_RESOLVER_ONLY) != 0) {
return null;
}
ri = new ResolveInfo(mResolveInfoSupplier.get());
ri.handleAllWebDataURI = browserCount == n;
ri.activityInfo = new ActivityInfo(ri.activityInfo);
ri.activityInfo.labelRes =
ResolverActivity.getLabelRes(intent.getAction());
if (ri.userHandle == null) ri.userHandle = UserHandle.of(userId);无明确 priority 差异、无 PreferredActivity、无已批准 Instant App 时,普通 resolve 返回 ResolverActivity;RESOLVE_NON_RESOLVER_ONLY 则让同一场景返回 null。ResolverActivity 是系统构造的选择器结果,不是候选列表中的真实业务 Activity。
10. 排查顺序
- 先打印每个 ResolveInfo 的 priority、preferredOrder、isDefault、match 和 system。
- 若第三方 priority 大于 0,检查
ComponentResolver.adjustPriority是否已将其限为 0。 - 若是 privileged 更新包,比较系统版本和更新包的 Activity/filter 是否等价。
- 若前三个排序字段相同,检查 PreferredActivity 是否存在、候选 set 是否仍匹配。
- 若返回 ResolverActivity,检查
RESOLVE_NON_RESOLVER_ONLY、Instant App 域名批准和 browserCount。 - 修改默认应用后检查 userId、package restrictions 写入和 preferred activity changed 广播。
11. 源码阅读路线
ComponentResolver.RESOLVE_PRIORITY_SORTER:确认排序字段顺序。ComponentResolver.adjustPriority/fixProtectedFilterPriorities:确认 priority 安全限幅。ResolveIntentHelper.chooseBestActivity:确认静态排序、PreferredActivity 和 ResolverActivity 的消费顺序。PreferredActivityHelper.findPreferredActivityNotLocked:确认快照读取与持久化触发。PreferredActivityHelper.addPreferredActivity:确认写权限、候选 set 和 userId。PackageManagerService中的默认 installer、uninstaller、home 查找:观察系统服务如何依赖单结果选择。
默认应用选择的核心不是“谁 priority 高谁赢”,而是注册时先限制 priority,查询时再排序,最后在排序相同的候选集合中应用用户偏好或选择器。把这三层混在一起,会导致对 Android Intent 结果的错误判断。
