10、PackageManagerService(PMS):APK解析、权限管理、安装与卸载流程

各位好,今天我们来聊聊Android系统里一个重量级角色——PackageManagerService,简称PMS。说实话,PMS是我在系统开发中打交道最多的服务之一。你每次安装一个App、打开一个应用、甚至只是查看一下应用列表,背后都有PMS在默默工作。

PMS到底负责什么?简单说,它管理着系统中所有APK的“户口本”。谁安装了、谁卸载了、谁有什么权限、谁依赖谁——这些信息全在PMS手里。我习惯把PMS比作Android系统的“物业公司”,它管着每个应用的“入住”和“退租”。

核心职责一句话:PMS负责APK的解析、权限的授予与检查、应用的安装与卸载,以及所有包信息的维护。

10.1 APK解析:PMS是怎么读懂一个APK的?

APK本质上是个压缩包,但PMS要从中提取出大量结构化信息。这个过程我称之为“APK的体检”。

PMS解析APK的核心路径是这样的:

  1. 读取AndroidManifest.xml——这是APK的“身份证”,所有关键信息都在这里
  2. 解析四大组件——Activity、Service、BroadcastReceiver、ContentProvider
  3. 提取权限声明——应用声明了哪些权限,需要哪些权限
  4. 解析签名信息——验证APK的签名是否合法
  5. 收集资源与so库——记录APK的资源路径和native库路径

嗯,这里要注意:解析过程是在PackageParser类中完成的。我曾在项目中遇到过一个问题——某个APK的AndroidManifest.xml格式有轻微错误,导致PMS解析失败,整个系统启动卡在“正在优化应用”界面。后来发现是某个标签的属性值多了一个空格。你想想看,一个空格就能让系统起不来,这就是Android的严谨之处。

避坑指南:我曾经在定制ROM时发现,如果APK的AndroidManifest.xml中android:name属性写成了全小写,PMS解析时不会报错,但后续通过PackageManager.getActivityInfo()获取信息时会返回null。所以,组件名称的大小写一定要和代码中一致。

10.2 权限管理:PMS的“门禁系统”

权限管理是PMS最核心的功能之一。说白了,它就是Android系统的“门禁系统”——每个应用想访问敏感资源,都得经过PMS的检查。

权限分为几类:

权限类型 特点 举例
普通权限(Normal) 安装时自动授予,无需用户确认 INTERNET、ACCESS_NETWORK_STATE
危险权限(Dangerous) 运行时需用户授权,可撤销 CAMERA、ACCESS_FINE_LOCATION
签名权限(Signature) 只有相同签名的应用才能获取 系统级API调用
特权权限(Privileged) 系统应用专属,位于/system/priv-app INSTALL_PACKAGES

PMS在权限检查时,会走一套完整的流程:

// 权限检查的核心逻辑(简化版)
int checkUidPermission(String permName, int uid) {
    // 1. 获取uid对应的包名列表
    String[] pkgNames = getPackagesForUid(uid);
    
    // 2. 遍历每个包,检查是否拥有该权限
    for (String pkgName : pkgNames) {
        PackageSetting ps = mSettings.getPackageSetting(pkgName);
        if (ps != null && ps.grantedPermissions.contains(permName)) {
            return PackageManager.PERMISSION_GRANTED;
        }
    }
    
    // 3. 检查是否为系统权限
    if (isSystemPermission(permName, uid)) {
        return PackageManager.PERMISSION_GRANTED;
    }
    
    return PackageManager.PERMISSION_DENIED;
}

我个人习惯把权限检查分为两个阶段:安装时检查运行时检查。安装时检查主要针对普通权限和签名权限,运行时检查则针对危险权限。Android 6.0之后引入的运行时权限模型,说白了就是把“门禁”的开关从安装时挪到了运行时,让用户有更多控制权。

注意:权限的授予状态存储在/data/system/packages.xml中。如果你在调试时手动修改了这个文件,记得重启SystemServer,否则PMS不会感知到变化。我曾经因为忘记重启,排查了半天权限问题,最后发现是缓存没刷新。

10.3 安装流程:一个APK的“入住手续”

APK安装流程,我把它分为四个阶段:

  1. 拷贝阶段——将APK文件复制到/data/app/目录下
  2. 解析阶段——PMS解析APK,提取包信息
  3. 优化阶段——dex2oat编译,生成odex文件
  4. 注册阶段——将包信息写入系统数据库,更新组件信息

安装的入口是PackageInstaller,它通过Binder调用PMS的installStage()方法。PMS会创建一个InstallParams对象,然后交给InstallHandler处理。整个过程是异步的,所以安装大应用时不会阻塞系统。

来看看核心代码路径:

// PMS中的安装入口
public void installStage(String packageName, File stagedFile, 
        String installerPackageName, int installFlags) {
    // 创建安装参数
    InstallParams params = new InstallParams(stagedFile, installerPackageName, 
            installFlags, null, null);
    // 提交到安装队列
    mInstallQueue.add(params);
    // 唤醒安装线程
    mInstallHandler.sendEmptyMessage(INSTALL_START);
}

我记得有一次,客户反馈说某个应用安装后无法打开。排查后发现是dex2oat优化阶段出了问题——APK中包含了一个超大dex文件,导致编译超时。解决方案是在build.prop中调整了dalvik.vm.dex2oat-timeout参数。嗯,这种问题在低端设备上尤其常见。

关键点:安装过程中,PMS会检查APK的签名是否与已安装版本一致。如果签名不一致,即使包名相同,也会安装失败。这是Android防止应用被恶意替换的重要机制。

10.4 卸载流程:一个APK的“退租手续”

卸载流程相对简单,但同样需要谨慎处理。PMS卸载一个应用时,主要做以下几件事:

  • 删除/data/app/下的APK文件
  • 删除/data/data/下的应用数据目录
  • 从packages.xml中移除包信息
  • 通知其他组件(如Launcher)刷新界面
  • 清理权限授予记录

卸载的入口是deletePackage()方法。PMS会先检查调用者是否有卸载权限(需要DELETE_PACKAGES权限或系统应用),然后执行卸载操作。

// 卸载核心逻辑(简化版)
public void deletePackage(String packageName, int flags) {
    // 1. 检查调用者权限
    if (!checkCallingPermission(DELETE_PACKAGES)) {
        throw new SecurityException("No permission to delete packages");
    }
    
    // 2. 获取包设置信息
    PackageSetting ps = mSettings.getPackageSetting(packageName);
    if (ps == null) {
        return; // 包不存在
    }
    
    // 3. 删除应用数据
    deleteApplicationData(packageName);
    
    // 4. 删除APK文件
    deletePackageFile(ps.codePath);
    
    // 5. 从系统中移除包信息
    removePackageFromSettings(packageName);
    
    // 6. 发送广播通知
    sendPackageRemovedBroadcast(packageName);
}

这里有个坑:如果应用有persistent属性(即android:persistent="true"),PMS卸载时会特殊处理——先杀掉进程,再删除数据。我曾在项目中遇到过,一个persistent应用卸载后,它的进程还在运行,导致系统认为应用还在,重新安装时报错“已存在”。后来发现是卸载时没有正确清理进程。

个人经验:调试卸载问题时,可以查看/data/system/packages.xml中是否还有残留的包信息。如果手动清理,记得同时删除/data/data//data/app/下的对应目录,否则重新安装时会出现签名冲突。

10.5 PMS的知识体系总览

为了让大家更直观地理解PMS的整体架构,我画了一张图:

PackageManagerService 核心架构 PackageManagerService APK解析器 权限管理器 安装引擎 卸载处理器 Manifest解析 签名验证 权限检查 权限授予 APK拷贝 dex2oat 数据清理 广播 数据存储层 packages.xml packages.list packages-staging.xml APK解析 权限管理 安装 卸载 数据存储

从这张图可以看出,PMS的四大功能模块都依赖于底层的持久化存储。每次系统启动时,PMS都会从packages.xml中恢复所有包信息。这也是为什么系统启动后,你之前安装的应用都还在——PMS把它们的“户口”都记在文件里了。

好了,关于PMS的APK解析、权限管理、安装与卸载流程,我就讲到这里。这些内容在实际的系统开发中非常实用,尤其是做定制ROM或系统应用开发时,理解PMS的工作原理能帮你少走很多弯路。


公众号:蓝海资料掘金营,微信deep3321