Android PMS(Package Manager Service)详细梳理
一、PMS概述
Android PMS,即Package Manager Service,管理和维护安装在设备上的应用程序包(APK文件)。 它不仅确保应用程序的安全性和稳定性,还提供了对应用程序包信息的全面访问和管理功能,是连接用户与应用程序的桥梁。
二、PMS的主要功能
-
应用程序的安装、卸载和更新:
- 安装:处理用户发起的安装请求,验证APK文件的签名和完整性,提取并存储元数据信息,最后将应用程序文件放置在系统指定位置(如/data/app/)。
- 卸载:响应用户的卸载请求,删除应用程序的文件、数据以及相关的元数据信息,并通知其他系统组件进行清理。
- 更新:执行应用程序的更新流程,包括卸载旧版本、安装新版本,并处理更新过程中的数据迁移等问题。
-
应用程序的权限管理:
- 在应用程序安装时,PMS会审核其申请的权限列表,并根据系统的安全策略决定是否授予这些权限。
- 在应用程序运行时,PMS负责权限的动态检查,确保应用程序只能访问其被授权的资源和功能。
-
维护应用程序包清单信息:
- PMS维护一个包含所有已安装应用程序元数据的数据库和XML文件(如AndroidManifest.xml),这些信息对于系统管理和用户交互至关重要。
三、PMS的工作流程
-
应用程序的安装流程:
- 用户发起安装请求。
- PackageInstaller将APK文件发送给PMS。
- PMS利用PackageParser解析APK文件,提取元数据信息并进行签名验证。
- 验证通过后,PMS将应用程序文件复制至系统指定目录,并分配UID。
- 更新内部数据库和XML文件,记录应用程序的元数据信息。
- 发送安装成功广播,通知系统其他组件。
-
应用程序的卸载流程:
- 用户发起卸载请求。
- PMS删除应用程序文件及相关数据。
- 从内部数据库和XML文件中移除应用程序的元数据信息。
- 发送卸载成功广播,通知其他系统组件进行清理。
-
权限管理流程:
- 在安装时,PMS根据应用程序的AndroidManifest.xml文件及系统安全策略,授予或拒绝权限请求。
- 运行时,PMS根据权限等级和应用状态动态管理权限访问。
四、PMS的核心类和模块
- PackageManagerService(PMS的实现类):提供应用程序包管理的核心逻辑,如安装、卸载、更新和权限管理等。
- PackageInstaller:负责处理APK文件的安装请求,与PMS协同工作完成安装流程。
- PackageParser:专门用于解析APK文件,提取其中的元数据信息(如包名、版本号、权限等)。
- PackageManager:作为对外接口,为其他系统组件和应用程序提供查询和管理应用程序包信息的服务。
五、权限检查异常
PMS(PackageManager Service)在检查APK(Android应用程序包)权限时,可能会抛出多种错误异常,这些异常通常与APK文件的格式、权限声明、签名验证等方面的问题相关。以下是一些常见的错误异常类型:
-
ParseError(解析错误):
- 当PMS尝试解析APK文件时,如果APK文件的格式不正确、损坏或缺失必要的部分(如AndroidManifest.xml文件),PMS可能会抛出解析错误。这通常是由于APK文件在传输或存储过程中损坏,或者APK文件本身就不符合Android的规范。
-
SecurityException(安全异常):
- 如果APK文件尝试声明或使用它不应该有的权限,或者APK的签名不符合预期(例如,尝试安装一个被篡改的应用),PMS可能会抛出安全异常。这是Android安全机制的一部分,用于防止恶意软件安装到设备上。
-
PackageManager.NameNotFoundException(包名未找到异常):
- 当PMS在尝试查询或操作一个不存在的包时,会抛出此异常。例如,如果APK文件尝试通过Intent启动一个不存在的Activity或服务,或者尝试访问一个不存在的包的数据,就可能触发此异常。
-
BadPackageException(不良包异常):
- 当APK文件存在某些严重问题,如应用程序ID(application ID)冲突、权限冲突或签名不一致等,PMS可能会抛出此异常。这通常表示APK文件在构建或发布过程中存在问题。
-
InstallationException(安装异常):
- 虽然这不是一个直接由PMS抛出的异常,但在APK安装过程中,如果PMS检测到任何问题(如存储空间不足、APK文件损坏、权限问题等),都可能导致安装失败,并可能通过安装器(如PackageInstaller)返回安装异常。
需要注意的是,具体的异常类型和错误信息可能会因Android版本的不同而有所差异。此外,由于Android系统的更新和变化,新的异常类型或错误代码可能会被引入,而旧的则可能不再使用。
六、伪代码示例
由于直接提供Android系统内部(如PackageManagerService, PMS)的完整源码示例可能涉及复杂的系统架构和大量代码,这里我将以一个简化的伪代码或概念性描述来概述PMS的工作流程,特别是应用程序的安装流程。请注意,这并非实际的Android源代码,而是对流程的逻辑抽象。
1、应用程序的安装流程伪代码
// 假设这是PMS的一个简化版本
class PackageManagerService {
// 安装应用程序
void installApp(File apkFile) {
// 1. 用户发起安装请求,这里简化为直接调用此方法
// 2. PackageInstaller将APK文件发送给PMS(这里简化为直接传递)
// 3. PMS利用PackageParser解析APK文件
PackageParser parser = new PackageParser();
PackageInfo packageInfo = parser.parsePackage(apkFile);
// 4. 提取元数据信息并进行签名验证
if (!verifySignature(packageInfo)) {
// 签名验证失败,处理错误
return;
}
// 5. 将应用程序文件复制至系统指定目录,并分配UID
File destDir = new File("/data/app/");
File appFile = new File(destDir, packageInfo.packageName + ".apk");
copyFile(apkFile, appFile);
int uid = allocateUid();
// 6. 更新内部数据库和XML文件
updateDatabase(packageInfo, uid);
updateXml(packageInfo);
// 7. 发送安装成功广播
sendBroadcast(new Intent(Intent.ACTION_PACKAGE_ADDED).putExtra("packageName", packageInfo.packageName));
}
// 签名验证方法(伪实现)
private boolean verifySignature(PackageInfo packageInfo) {
// 实际实现会复杂得多,包括读取APK的签名信息等
return true; // 假设总是验证成功
}
// 分配UID的伪实现
private int allocateUid() {
// 真实环境中,这将是一个更复杂的逻辑,确保UID的唯一性
return 12345; // 假设分配的UID
}
// 复制文件的伪实现
private void copyFile(File src, File dest) {
// 真实环境中,这里会使用文件IO操作
}
// 更新数据库的伪实现
private void updateDatabase(PackageInfo packageInfo, int uid) {
// 这里会更新PMS维护的数据库
}
// 更新XML文件的伪实现
private void updateXml(PackageInfo packageInfo) {
// 这里会更新PMS维护的XML文件(如AndroidManifest.xml的汇总)
}
// 发送广播的伪实现
private void sendBroadcast(Intent intent) {
// 真实环境中,这将调用Context的sendBroadcast方法
}
}
2、应用程序的卸载流程伪代码
// 假设这是PMS中的一个方法
public void uninstallApplication(String packageName) {
// 用户发起卸载请求,这里直接模拟请求处理
// 1. 删除应用程序文件及相关数据
deleteApplicationFiles(packageName);
// 2. 从内部数据库和XML文件中移除应用程序的元数据信息
removeFromDatabaseAndXml(packageName);
// 3. 发送卸载成功广播,通知其他系统组件进行清理
sendBroadcast(new Intent(Intent.ACTION_PACKAGE_REMOVED, Uri.parse("package:" + packageName)));
}
// 模拟删除应用程序文件及相关数据的方法
private void deleteApplicationFiles(String packageName) {
// 实现细节:删除文件、数据库记录等
System.out.println("Deleting files and data for package: " + packageName);
}
// 模拟从数据库和XML文件中移除应用程序元数据的方法
private void removeFromDatabaseAndXml(String packageName) {
// 实现细节:更新数据库和XML文件
System.out.println("Removing metadata from database and XML for package: " + packageName);
}
// 模拟发送广播的方法
private void sendBroadcast(Intent intent) {
// 在Android中,实际会调用Context的sendBroadcast方法
System.out.println("Sending broadcast: " + intent.getAction());
}
3、权限管理流程伪代码
权限管理流程涉及安装时和运行时两个阶段,这里分别给出伪代码示例。
安装时权限授予/拒绝伪代码
// 假设这是PMS中处理安装请求时的方法
public void handleInstall(PackageInfo packageInfo) {
// ... 其他安装流程 ...
// 权限处理
List<String> requestedPermissions = packageInfo.requestedPermissions;
List<String> grantedPermissions = new ArrayList<>();
for (String permission : requestedPermissions) {
if (isPermissionGranted(permission, packageInfo)) {
grantedPermissions.add(permission);
}
}
// 假设这里有一个方法来更新包信息中的权限列表
updatePackagePermissions(packageInfo, grantedPermissions);
// ... 其他安装流程 ...
}
// 假设这是检查权限是否应该被授予的方法
private boolean isPermissionGranted(String permission, PackageInfo packageInfo) {
// 实现细节:根据系统安全策略判断
// 这里简单模拟总是授予权限
return true; // 或者基于某些逻辑返回true或false
}
// 假设这是更新包信息中权限列表的方法
private void updatePackagePermissions(PackageInfo packageInfo, List<String> grantedPermissions) {
// 实现细节:更新packageInfo中的权限列表
// 这里只是模拟
System.out.println("Granted permissions for package: " + packageInfo.packageName + " = " + grantedPermissions);
}
运行时权限管理伪代码
运行时权限管理通常涉及到应用程序的Context或Activity等组件与系统的交互,但在这里我们主要关注PMS的逻辑。
// 假设PMS中有一个方法来检查运行时权限
public boolean checkRuntimePermission(String packageName, String permission) {
// 实现细节:检查当前应用的状态和权限等级
// 这里简单模拟总是允许
return true; // 或者基于某些逻辑返回true或false
}
// 注意:在实际Android系统中,运行时权限检查通常不是由PMS直接完成的,
// 而是由应用程序通过Context的checkSelfPermission等方法进行。
// 这里只是为了展示PMS在权限管理流程中的角色而提供的伪代码。
请注意,这些伪代码只是为了说明PMS在卸载和权限管理流程中的大致逻辑,并不是Android系统内部实际的代码实现。在真实的Android系统中,这些功能由复杂的系统服务和框架代码支持。