PackageManagerService(PKMS)是 Android 系统中负责应用包管理的核心服务。从 APK 的扫描解析、安装卸载,到权限管理、组件解析、共享 UID 管理,PKMS 是整个应用生态的底座。本文基于 Android 11 AOSP 源码,深入分析 PKMS 的启动流程、APK 扫描管线、安装流程、权限模型和关键数据结构。
一、PKMS 的体系定位
PKMS 运行在 system_server 进程中,是引导服务(Bootstrap Service)之一,在 SystemServer 的 startBootstrapServices 阶段被创建,早于 AMS 和 WMS。
SystemServer.startBootstrapServices() └── PMS.main() // 静态工厂方法 └── PKMS(Context) // 构造函数(执行扫描等主要逻辑) ├── Settings // 所有已安装包配置(packages.xml) ├── PackageParser // APK 解析器(Android 11 改为 PackageParser2) ├── PermissionManagerService // 权限管理(Android 11+ 拆分) └── Installer // installd 守护进程代理
|
AOSP 核心路径:
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
frameworks/base/services/core/java/com/android/server/pm/Settings.java
frameworks/base/services/core/java/com/android/server/pm/PackageInstallerService.java
二、PKMS 启动流程与扫描管线
2.1 SystemServer 中启动 PKMS
private void startBootstrapServices() { mPackageManagerService = PackageManagerService.main(mSystemContext, installer, mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore); ServiceManager.addService("package", mPackageManagerService); mFirstBoot = mPackageManagerService.isFirstBoot(); mPackageManager = mSystemContext.getPackageManager(); }
|
2.2 PKMS 构造函数——扫描管线
PKMS 的构造函数是理解 APK 扫描的核心入口。在 Android 11 中,扫描流程大致分为以下几个阶段:
public PackageManagerService(Context context, Installer installer, boolean factoryTest, boolean onlyCore) {
mContext = context; mInstaller = installer; mPmsBgHandler = new Handler(BackgroundThread.getHandler().getLooper());
mSettings = new Settings(mPmsBgHandler.getLooper()); mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID, ...); mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID, ...);
File frameworkDir = new File(Environment.getRootDirectory(), "framework");
scanDirTracedLI(systemAppDir, ...);
for (PackageSetting ps : mSettings.mPackages.values()) { }
mComponentResolver = new ComponentResolver(activityResolvers, ...);
updateAllPermissions(null, true); }
|
2.3 scanDirTracedLI——目录扫描
private void scanDirTracedLI(File scanDir, int parseFlags, int scanFlags, long currentTime, PackageParser2 packageParser) { final File[] files = scanDir.listFiles(); if (ArrayUtils.isEmpty(files)) { return; }
try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(packageParser, mExecutorService)) { for (File file : files) { parallelPackageParser.submit(file, parseFlags); }
for (ParseResult result; (result = parallelPackageParser.take()) != null; ) { scanPackageChildLI(result.pkg, parseFlags, scanFlags, currentTime, null ); } } }
|
2.4 PackageParser2——APK 解析器
Android 11 用 PackageParser2 替代了 PackageParser,核心改进是使用了 TypedKeyValue 和增量解析特性,提升了扫描速度:
public class PackageParser2 { public ParsedPackage parsePackage(File packageFile, int flags) throws PackageManagerException { ApkLite baseApk = getApkLite(packageFile, flags);
ParseInput input = mParsingThreadLocal.get().reset(); ParsedPackage pkg = parseMonolithicPackage(input, baseApk, flags);
return pkg; }
private ParsedPackage parseMonolithicPackage(ParseInput input, ApkLite baseApk, int flags) { return PackageCacher.parseMonolithicPackage(input, baseApk.getPath(), baseApk.getSigningDetails(), ...); } }
|
2.5 scanPackageChildLI——深度扫描与登记
private PackageSetting scanPackageChildLI(ParsedPackage pkg, ...) { PackageSetting pkgSetting = mSettings.getPackageLPr(pkg.getPackageName()); if (pkgSetting == null) { pkgSetting = mSettings.insertPackageSettingLPw(pkg, ...); }
if (pkg.getSharedUserId() != null) { SharedUserSetting sharedUser = mSettings.getSharedUserLPw( pkg.getSharedUserId(), ...); pkgSetting.setSharedUserSetting(sharedUser); }
if (!mSettings.isSignatureMatch(pkgSetting, pkg.getSigningDetails())) { }
String primaryCpuAbi = PackageManagerService.computePrimaryCpuAbi( pkg.getCpuAbiOverride(), scanFlags);
commitReconciledScanResultLocked(reconciledPkg, mUserManager.getUserIds());
return pkgSetting; }
|
三、APK 安装流程
3.1 安装入口
APK 安装可以通过多种方式触发:PackageInstaller(系统安装器 UI)、adb install、Google Play 等。所有方式最终都通过 PackageInstallerService 的 Session 机制实现。
public class PackageInstallerService extends IPackageInstaller.Stub { public int createSession(SessionParams params, String installerPackageName, int userId) { final int sessionId = allocateSessionIdLocked(); File stageDir = buildStageDir(params, sessionId); PackageInstallerSession session = new PackageInstallerSession( mInternalCallback, mContext, mPm, ...); mSessions.put(sessionId, session); return sessionId; }
public void commitSession(int sessionId) { synchronized (mSessions) { PackageInstallerSession session = mSessions.get(sessionId); session.commit(PreapprovalDetails.DETAILS_UNKNOWN); } } }
|
3.2 安装的执行阶段
void installStage(ActiveInstallSession activeInstallSession) { mPendingInstalls.put(activeInstallSession.getSessionId(), ...);
mHandler.post(() -> { processInstallRequests(installRequests, ...); }); }
void processInstallRequests(List<InstallRequest> installRequests, ...) { }
|
3.3 dexopt 触发
private void performDexopt(List<PackageSetting> pkgSettings, ...) { for (PackageSetting pkgSetting : pkgSettings) { mInstaller.dexopt(pkgSetting.getPath(), pkgSetting.getAppId(), pkgSetting.getPkgName(), instructionSet, dexoptNeeded, null , dexFlags, compilerFilter, ...); } }
|
四、Settings 与 packages.xml
4.1 Settings 类
Settings 是 PKMS 的持久化配置管理类,管理所有已安装包的状态信息,并在重启后从 packages.xml 恢复。
final class Settings { final ArrayMap<String, PackageSetting> mPackages = new ArrayMap<>();
final ArrayMap<String, SharedUserSetting> mSharedUsers = new ArrayMap<>();
boolean readLPw(@NonNull List<UserInfo> users) { FileInputStream str = new FileInputStream(mSettingsFilename); XmlPullParser parser = Xml.newPullParser(); while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { if (type == XmlPullParser.START_TAG) { String tagName = parser.getName(); if (tagName.equals("package")) { readPackageLPw(parser); } else if (tagName.equals("shared-user")) { readSharedUserLPw(parser); } } } return true; }
void writeLPr() { } }
|
4.2 packages.xml 内容示例
packages.xml 位于 /data/system/packages.xml,记录了每个已安装应用的完整配置信息:
<package name="com.example.app" codePath="/data/app/com.example.app-xxx" nativeLibraryPath="/data/app/com.example.app-xxx/lib" primaryCpuAbi="arm64-v8a" userId="10123" ...> <sigs count="1"> <cert index="0" key="..."/> </sigs> <perms> <item name="android.permission.INTERNET" granted="true" flags="0"/> </perms> <proper-signing-keyset identifier="1"/> </package>
|
五、权限管理
5.1 BasePermission 与 PermissionInfo
final class Permission { final PermissionInfo info; final int type; boolean mReconciled;
static final int TYPE_MANIFEST = 0; static final int TYPE_CONFIG = 1; static final int TYPE_DYNAMIC = 2; }
final class PermissionRegistry { final ArrayMap<String, Permission> mPermissions = new ArrayMap<>(); final ArrayMap<String, Permission> mTreePermissions = new ArrayMap<>(); }
|
5.2 权限授予逻辑
private int grantPermissions(PackageState packageState, ...) { for (String permName : allPermissions) { Permission permission = mPermissionRegistry.getPermission(permName); if (permission == null) continue;
if (permission.getProtection() == PermissionInfo.PROTECTION_NORMAL) { grantPermission(packageState, permName, userId); } else if (permission.getProtection() == PermissionInfo.PROTECTION_DANGEROUS) { if (hasUserApproval || restoredFromBackup) { grantPermission(packageState, permName, userId); } } else if (permission.getProtection() == PermissionInfo.PROTECTION_SIGNATURE) { if (packageState.isSignedWithPlatformKey()) { grantPermission(packageState, permName, userId); } } } }
|
六、组件解析(Component Resolution)
当应用通过 PackageManager.queryIntentActivities() 查询能处理某个 Intent 的 Activity 时,PKMS 通过 ComponentResolver 高效地进行组件匹配。
public class ComponentResolver { private final ActivityIntentResolver mActivities; private final ServiceIntentResolver mServices; private final ReceiverIntentResolver mReceivers; private final ProviderIntentResolver mProviders;
List<ResolveInfo> queryActivities(Intent intent, String resolvedType, int flags, int userId) { return mActivities.queryIntent(intent, resolvedType, flags, userId); } }
|
根 IntentResolver 的匹配逻辑:
- 匹配 Action(如果 Intent 指定了 action)
- 匹配 Category(所有 category 都要匹配)
- 匹配 Data(URI scheme、host、path、mimeType)
- 按优先级排序(priority 值和 preferred)
七、ABI 确定与 Native Library 管理
PKMS 在安装时需要为 APK 确定正确的 CPU ABI,以便在 /data/app/<pkg>/lib/ 下建立正确的 native 库目录。
static String computePrimaryCpuAbi(ScanPackage pkg, int scanFlags) { String[] abis = pkg.getCpuAbis();
for (String buildAbi : Build.SUPPORTED_ABIS) { for (String pkgAbi : abis) { if (buildAbi.equals(pkgAbi)) { return buildAbi; } } }
return null; }
|
八、核心面试题
Q1:PKMS 如何实现应用间共享 UID?它的安全风险是什么?
PKMS 通过 SharedUserSetting 管理共享 UID。当多个 APK 声明相同的 android:sharedUserId 且使用相同的签名时,它们会被分配到相同的 Linux UID,从而可以相互访问对方的文件(/data/data/<pkg>/)。风险显而易见:如果一个共享 UID 的应用存在安全漏洞,同 UID 的所有应用的数据都可能被利用。在 Android 11+ 中,Google 进一步收紧了 sharedUserId 的使用,新应用不推荐使用。
Q2:PKMS 中的 packages.xml 和 packages.list 分别存储什么信息?
packages.xml(位于 /data/system/)存储所有已安装应用的完整元数据:包名、代码路径、native 库路径、签名证书、授予的权限、SharedUserId 信息等。每次应用安装/卸载时更新。
packages.list(位于 /data/system/)以更简洁的格式存储包名和 UID 的映射关系,格式为:<pkgName> <uid> <dataDir>,供快速查询使用。
Q3:Android 11 中 PermissionManagerService 为什么从 PKMS 中独立出来?
主要出于架构解耦和安全性考虑:(1) 权限检查是高频操作,独立成单独服务后可以更好地优化;(2) 分离权限管理代码使得代码边界更清晰,便于安全审计;(3) 为未来的权限模型演进(如 Scoped Storage、One-Time Permissions)提供更灵活的基础。
AOSP 核心路径参考:
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
frameworks/base/services/core/java/com/android/server/pm/Settings.java
frameworks/base/services/core/java/com/android/server/pm/ComponentResolver.java
frameworks/base/services/core/java/com/android/server/pm/PackageInstallerService.java
frameworks/base/services/core/java/com/android/server/pm/parsing/PackageParser2.java
frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
frameworks/base/services/core/java/com/android/server/pm/permission/PermissionRegistry.java