使用系统相关接口,可以根据 Intent 过滤查询对应的 Context
PackageManager.java
- queryIntentActivities? 查询符合Intent Activity
- queryBroadcastReceivers??查询符合Intent BroadcastReceiver
- queryIntentServices??查询符合Intent Service
- queryIntentContentProviders?查询符合Intent ContentProvider
按照系统接口使用说明,查询桌面应用图标的 actvity 代码demo如下:
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
List<ResolveInfo> list = getPackageManager().queryIntentActivities(intent, 0);
for (ResolveInfo resolveInfo : list) {
Log.i(Activity_TAG, "resolveInfo " + resolveInfo);
}
实际打印中会发现,打印出来的 activity 不全。原因在于系统对查询的结果进行过滤了一部分
private @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
String resolvedType, int flags, @PrivateResolveFlags int privateResolveFlags,
int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits) {
if (!mUserManager.exists(userId)) return Collections.emptyList();
final String instantAppPkgName = getInstantAppPackageName(filterCallingUid);
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
false /* requireFullPermission */, false /* checkShell */,
"query intent activities");
final String pkgName = intent.getPackage();
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
intent = intent.getSelector();
comp = intent.getComponent();
}
}
flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart,
comp != null || pkgName != null /*onlyExposedExplicitly*/,
isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
flags));
if (comp != null) {
// 对指定了 ComponentName 的 Intent 查询
return result;
}
// reader
boolean sortResult = false;
boolean addInstant = false;
List<ResolveInfo> result;
synchronized (mLock) {
if (pkgName == null) {
List<CrossProfileIntentFilter> matchingFilters =
getMatchingCrossProfileIntentFilters(intent, resolvedType, userId);
// 判断当前查询的应用是否跳过接下来配置文件
ResolveInfo xpResolveInfo = querySkipCurrentProfileIntents(matchingFilters, intent,
resolvedType, flags, userId);
if (xpResolveInfo != null) {
List<ResolveInfo> xpResult = new ArrayList<>(1);
xpResult.add(xpResolveInfo);
return applyPostResolutionFilter(
filterIfNotSystemUser(xpResult, userId), instantAppPkgName,
allowDynamicSplits, filterCallingUid, resolveForStart, userId, intent);
}
// 判断当前用户是否是系统用户
result = filterIfNotSystemUser(mComponentResolver.queryActivities(
intent, resolvedType, flags, userId), userId);
addInstant = isInstantAppResolutionAllowed(intent, result, userId,
false /*skipPackageCheck*/);
// Check for cross profile results.
boolean hasNonNegativePriorityResult = hasNonNegativePriority(result);
xpResolveInfo = queryCrossProfileIntents(
matchingFilters, intent, resolvedType, flags, userId,
hasNonNegativePriorityResult);
if (xpResolveInfo != null && isUserEnabled(xpResolveInfo.targetUserId)) {
boolean isVisibleToUser = filterIfNotSystemUser(
Collections.singletonList(xpResolveInfo), userId).size() > 0;
if (isVisibleToUser) {
result.add(xpResolveInfo);
sortResult = true;
}
}
if (intent.hasWebURI()) {
// 设置 weburi 的部分
}
} else {
// 设置了pkgName 的intent查询方式
}
}
... ...
// 进行过滤操作
return applyPostResolutionFilter(
result, instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart,
userId, intent);
}
queryIntentActivitiesInternal 中初步对调用的应用做了判断,最后调用?applyPostResolutionFilter 对进行进一步判断,在?applyPostResolutionFilter 方法中,一般的APP都是通过?!mAppsFilter.shouldFilterApplication( filterCallingUid, callingSetting, resolvedSetting, userId) 进行判断是否需要过滤删除查询到的?ResolveInfo?
AppsFilter -> shouldFilterApplication
方法中通过对 uid、sharedUserId 数据判断,进行过滤,按照系统默认规则:非 systemUid 应用只能获取到 systemUid 应用、静态库等模块中的?ResolveInfo。开启查询 pkg Feature 或者添加如下权限可以查询获取到全部模块中匹配项。?
android.permission.QUERY_ALL_PACKAGES
这里已经安装应用 uid 等信息都是保存在?/data/system/packages.xml 。系统的 PkgSetting?类就是获取这里内容,debug时候可以手动导出来查看下相关内容。
|