Android从上帝视角来看PackageManagerService
The following article is from 牛晓伟 Author 牛晓伟
本文摘要
这是包管理系列的最后一篇文章,本文的标题是从上帝视角来看PackageManagerService,为啥要起这么“狂妄”的名字呢?其主要的原因是我希望从一个更全面、更高的、更清晰的视角来看明白PackageManagerService的每个模块之间是如何协作来保证PackageManagerService的关键工作顺利完成。通过本文您将了解到PackageManagerService被划分为哪些模块,模块之间是如何协作来保证各项工作的顺利完成。(文中代码基于Android13)
1.1 权限管理模块
1.1.1 声明权限
<permission android:description="string resource"
android:icon="drawable resource"
android:label="string resource"
android:name="string"
android:permissionGroup="string"
android:protectionLevel=["normal" | "dangerous" |
"signature" | ...] />
1.1.2 请求权限
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
1.1.3 权限管理模块所做的事情
把所有的apk声明的权限收集并集中管理起来。 保存每个apk请求的权限和请求权限对应的状态,请求权限对应的状态是指比如某个apk的请求的权限是否被允许、是否拒绝、是否只是允许一次。 处理apk权限请求,当用户不管是点击了允许、拒绝等,都需要经过权限管理模块。
1.2 共享库模块
把所有声明的共享库收集起来,若共享库之间存在依赖,则把它们的依赖也初始化。 若apk中使用了某个共享库,则会根据共享库名称、版本信息从共享库模块把共享库的信息查询出来 (如共享库文件路径) 交给该apk。
1.3 记录存储模块
1.4 所有apk信息模块
//下面属性位于PackageManagerService类
//key是包名,而AndroidPackage存储了解析出的AndroidManifest的信息
final WatchedArrayMap<String, AndroidPackage> mPackages = new WatchedArrayMap<>();
1.5 四大组件模块
ActivityTaskManagerService启动某个Activity时,需要从我这获取对应的Activity信息,获取到则返回;否则启动Activity失效。 ActivityManagerService启动某个Service时,也需要从我这获取对应的Service信息。同理启动某个BroadcastReceiver、ContentProvider也需要从我哦这获取对应的信息。
//下面属性位于PackageManagerService类
final ComponentResolver mComponentResolver;
1.6 apk管理模块
1.7 快照管理模块
//Settings类
//获取Settings的快照
public Settings snapshot() {
return mSnapshot.snapshot();
}
private Settings(Settings r) {
//Settings的很多属性也都有自己的快照
mPackages = r.mPackagesSnapshot.snapshot();
mPackagesSnapshot = new SnapshotCache.Sealed<>();
mKernelMapping = r.mKernelMappingSnapshot.snapshot();
mKernelMappingSnapshot = new SnapshotCache.Sealed<>();
省略其他代码······
// Do not register any Watchables and do not create a snapshot cache.
mSnapshot = new SnapshotCache.Sealed();
}
1.8 小结
权限管理模块负责apk权限相关的事情,比如请求某个权限,apk权限状态存储,收集所有apk声明的权限。 共享库模块负责apk使用到的所有共享库。 记录存储模块会把apk相关的很多信息记录并且存储到文件中,比如apk安装后关于apk安装的信息会存储下来,这样就可以供其他使用者检索。 所有apk信息模块会收集所有已安装apk的AndroidManifest解析出来的信息,以供其他使用者检索。 四大组件模块为了加快检索四大组件的速度,会把所有已安装apk的四大组件信息收集起来。 apk管理模块主要负责apk的安装/卸载/更新,它是根基模块,因为它的某个功能会对其他模块产生影响。 快照管理模块主要目的为加快访问PackageManagerService中的各种数据。
2.1 共享库模块启动
//下面代码位于PackageManagerService构造方法中
//从 SystemConfig.getInstance() 中把所有的内置共享库信息读取出来
ArrayMap<String, SystemConfig.SharedLibraryEntry> libConfig = systemConfig.getSharedLibraries();
final int builtInLibCount = libConfig.size();
for (int i = 0; i < builtInLibCount; i++) {
//依次把共享库信息添加到 mSharedLibraries 中
mSharedLibraries.addBuiltInSharedLibraryLPw(libConfig.valueAt(i));
}
long undefinedVersion = SharedLibraryInfo.VERSION_UNDEFINED;
//下面代码处理共享库之间的依赖
for (int i = 0; i < builtInLibCount; i++) {
String name = libConfig.keyAt(i);
SystemConfig.SharedLibraryEntry entry = libConfig.valueAt(i);
final int dependencyCount = entry.dependencies.length;
for (int j = 0; j < dependencyCount; j++) {
final SharedLibraryInfo dependency =
computer.getSharedLibraryInfo(entry.dependencies[j], undefinedVersion);
if (dependency != null) {
computer.getSharedLibraryInfo(name, undefinedVersion).addDependency(dependency);
}
}
}
//下面代码位于PackageManagerService构造方法中
//从 /data/system/packages.xml及其他文件中把保存的数据读出来
mFirstBoot = !mSettings.readLPw(computer,
mInjector.getUserManagerInternal().getUsers(
/* excludePartial= */ true,
/* excludeDying= */ false,
/* excludePreCreated= */ false));
//下面代码位于PackageManagerService构造方法中
//把所有apk声明的权限交给mPermissionManager
mPermissionManager.readLegacyPermissionsTEMP(mSettings.mPermissions);
//下面方法会使用每个apk的权限状态初始化自己
mPermissionManager.readLegacyPermissionStateTEMP();
把apk信息从AndroidManifest文件中解析出来。 在依据解析出来的apk信息,去记录存储模块查询该apk是否已经安装,若安装的话进行升级方面的操作 (比如新老apk进行版本比较);若没有安装则进行安装方面的操作,比如为apk创建data目录等。 如果apk中AndroidManifest文件中的权限发生了则会通知权限管理模块进行增加/删除/更新对应权限;如果apk中声明了共享库 (只有系统apk才可以声明共享库),则会通知共享库模块增加相应的共享库等操作。 解析出来的apk信息经过步步验证后,最终会存放在所有apk信息模块和四大组件模块,这样就可以供其他使用者使用了。
//下面代码位于PackageManagerService构造方法中
final int[] userIds = mUserManager.getUserIds();
//packageParser的作用是解析apk
PackageParser2 packageParser = mInjector.getScanningCachingPackageParser();
//扫描系统apk
mOverlayConfig = mInitAppsHelper.initSystemApps(packageParser, packageSettings, userIds,startTime);
//扫描非系统apk
mInitAppsHelper.initNonSystemApps(packageParser, userIds, startTime);
packageParser.close();
//下面代码位于PackageManagerService构造方法中
//下面方法会把所有apk的共享库信息补全 ((如果该apk确实使用了某个共享库)
mSharedLibraries.updateAllSharedLibrariesLPw(null, null, Collections.unmodifiableMap(mPackages));
而扫描所有apk也会或多或少的影响共享库模块、记录存储模块、权限管理模块,比如扫描某个系统apk,该系统apk声明了新的权限、声明了新的共享库,则会把声明的共享库信息记录在共享库模块,而声明的权限则会通知权限管理模块增加此权限,同时记录存储模块也会把该权限存储到文件中。
3.1 apk的安装
3.2 各模块协作
该apk的安装信息 (apk包名、版本信息、apk文件路径、appid、声明和使用的权限等) 以PackageSetting对象添加到记录存储模块,记录存储模块会把所有的PackageSetting对象重新写入到文件中。 该apk信息 (apk包名、声明的四大组件、版本信息、解析的权限等) 添加到所有apk信息模块。 该apk信息中的声明的四大组件信息添加到四大组件模块。 因为该apk使用了某个共享库,则会从共享库模块把该共享库信息查询出来交给PackageSetting对象。 因为该apk声明和使用了权限,则声明和使用的权限会被添加到权限管理模块。
3.3 apk安装最后一步
3.4 小结
会在记录存储模块存储下该apk的安装信息 (apk包名、appid、apk路径等)、声明的权限 (若声明了权限)、使用的权限及状态 (若使用了权限),并且会把这些信息写入文件。 apk信息会存放在所有apk信息模块。 apk声明的四大组件会存放在四大组件模块。 apk的权限 (若声明了权限或者使用了权限)会存放在 权限管理模块。
4.1 快照管理模块
//ComputerEngine类
public final @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
@PackageManagerInternal.PrivateResolveFlags long privateResolveFlags,
int filterCallingUid, int userId, boolean resolveForStart,
boolean allowDynamicSplits) {
省略代码······
}
//ComputerEngine类
protected ActivityInfo getActivityInfoInternalBody(ComponentName component,
@PackageManager.ResolveInfoFlagsBits long flags, int filterCallingUid, int userId) {
//四大组件模块的实现者就是mComponentResolver,调用它的getActivity方法查询到Activity信息
ParsedActivity a = mComponentResolver.getActivity(component);
省略代码······
}
//ComputerEngine类
protected ActivityInfo getActivityInfoInternalBody(ComponentName component,
@PackageManager.ResolveInfoFlagsBits long flags, int filterCallingUid, int userId) {
//四大组件模块的实现者就是mComponentResolver,调用它的getActivity方法查询到Activity信息
ParsedActivity a = mComponentResolver.getActivity(component);
AndroidPackage pkg = a == null ? null : mPackages.get(a.getPackageName());
if (pkg != null && mSettings.isEnabledAndMatch(pkg, a, flags, userId)) {
PackageStateInternal ps = mSettings.getPackage(component.getPackageName());
if (ps == null) return null;
if (shouldFilterApplication(
ps, filterCallingUid, component, TYPE_ACTIVITY, userId)) {
return null;
}
//调用generateActivityInfo方法
return PackageInfoUtils.generateActivityInfo(pkg,
a, flags, ps.getUserStateOrDefault(userId), userId, ps);
}
省略代码······
}
//PackageInfoUtils类
private static ActivityInfo generateActivityInfo(AndroidPackage pkg, ParsedActivity a,
@PackageManager.ComponentInfoFlagsBits long flags,
@NonNull PackageUserStateInternal state, @Nullable ApplicationInfo applicationInfo,
@UserIdInt int userId, @Nullable PackageStateInternal pkgSetting) {
省略代码······
//构建ApplicationInfo
if (applicationInfo == null) {
applicationInfo = generateApplicationInfo(pkg, flags, state, userId, pkgSetting);
}
if (applicationInfo == null) {
return null;
}
//构建ActivityInfo
final ActivityInfo info = PackageInfoWithoutStateUtils.generateActivityInfoUnchecked(
a, flags, applicationInfo);
assignSharedFieldsForComponentInfo(info, a, pkgSetting, userId);
return info;
}
不论是启动一个Activity或者启动一个Service或者启动一个ContentProvider或者启动一个BroadcastReceiver的方式来启动一个app,都需要去PackageManagerService查询相应的组件信息和ApplicationInfo信息,只有查询到正确的信息后,才能执行下一步操作;否则停止启动过程。
最后推荐一下我做的网站,玩Android: wanandroid.com ,包含详尽的知识体系、好用的工具,还有本公众号文章合集,欢迎体验和收藏!
推荐阅读:
扫一扫 关注我的公众号
如果你想要跟大家分享你的文章,欢迎投稿~
┏(^0^)┛明天见!