文章目录
- 前言
- [1. PMS 的初始化](#1. PMS 的初始化)
-
- [1.1 SystemServer 启动 PMS](#1.1 SystemServer 启动 PMS)
- [1.2 PMS 的入口方法 main](#1.2 PMS 的入口方法 main)
- [1.3 PMS 构造函数](#1.3 PMS 构造函数)
- [1.4 扫描 APK 文件](#1.4 扫描 APK 文件)
- [1.5 权限初始化](#1.5 权限初始化)
- [1.6 提供对外服务](#1.6 提供对外服务)
- [2. APK 安装机制](#2. APK 安装机制)
-
- [2.1. 安装请求的触发](#2.1. 安装请求的触发)
- [2.2 APK 文件解析与验证](#2.2 APK 文件解析与验证)
- [2.3 签名校验](#2.3 签名校验)
- [2.4 权限管理](#2.4 权限管理)
-
- [2.4.1 权限声明](#2.4.1 权限声明)
- [2.4.2 权限校验与分配](#2.4.2 权限校验与分配)
- [2.5 持久化存储](#2.5 持久化存储)
- [2.6 广播通知](#2.6 广播通知)
- [3. APK 解析流程](#3. APK 解析流程)
-
- [3.1 APK 文件读取](#3.1 APK 文件读取)
- [3.2 APK 校验](#3.2 APK 校验)
-
- [3.2.1 签名校验](#3.2.1 签名校验)
- [3.2.2 APK 格式校验](#3.2.2 APK 格式校验)
- [3.2.3 权限检查](#3.2.3 权限检查)
- [3.2.4 其他格式校验](#3.2.4 其他格式校验)
- [3.3 安装信息持久化](#3.3 安装信息持久化)
- [4. Intent 和组件管理](#4. Intent 和组件管理)
-
- [4.1 Intent 的作用](#4.1 Intent 的作用)
- [4.2 组件的管理](#4.2 组件的管理)
-
- 源码解析
-
-
- [resolveIntent() 方法](#resolveIntent() 方法)
- [queryIntentActivities() 方法](#queryIntentActivities() 方法)
-
- [5. 卸载流程](#5. 卸载流程)
-
- [5.1 卸载请求的触发](#5.1 卸载请求的触发)
- [5.2 PackageManagerService 接收到卸载请求](#5.2 PackageManagerService 接收到卸载请求)
- [5.3 停用应用](#5.3 停用应用)
-
- [5.4 删除应用数据](#5.4 删除应用数据)
- [5.5 更新系统数据库](#5.5 更新系统数据库)
- [5.6 广播通知](#5.6 广播通知)
- [5.7 删除 APK 文件](#5.7 删除 APK 文件)
- [5.8 更新 UI](#5.8 更新 UI)
- [6. 持久化机制](#6. 持久化机制)
-
- [6.1 持久化数据存储位置](#6.1 持久化数据存储位置)
- [6.2 数据存储格式](#6.2 数据存储格式)
- [6.3 持久化流程](#6.3 持久化流程)
-
- [6.3.1 应用安装时持久化](#6.3.1 应用安装时持久化)
- [6.3.2 应用卸载时持久化](#6.3.2 应用卸载时持久化)
- [6.3.3 应用更新时持久化](#6.3.3 应用更新时持久化)
- 主要类
- 总结
前言
在 Android 系统中,PackageManagerService(简称 PMS)是负责管理应用程序的核心服务。无论是应用安装、卸载,还是权限分配,PMS 都扮演着至关重要的角色。
本篇文章将带你深入探讨 PMS 的幕后机制,特别是 APK 安装与解析的详细流程。
1. PMS 的初始化
步骤 | 完成任务 | 影响范围 |
---|---|---|
启动 PMS 服务 | 注册 PMS 到 ServiceManager,成为系统管理的核心服务。 | 提供应用管理能力,供系统其他组件调用。 |
初始化组件 | 构建数据结构、组件解析器,启动后台线程。 | 提供运行时支持,准备后续任务。 |
加载持久化数据 | 恢复设备重启前的已安装应用信息。 | 确保已安装应用在系统重启后仍被识别和管理。 |
扫描并解析 APK | 扫描系统和用户目录,解析 APK 元数据,并注册到内存。 | 让系统能够识别和管理设备上的所有 APK。 |
初始化权限系统 | 加载默认权限配置文件,为系统应用分配权限。 | 保障系统安全,防止权限滥用。 |
提供对外接口 | 通过 API 提供安装、卸载、查询等功能,供系统和开发者调用。 | 提供应用安装和运行管理能力。 |
1.1 SystemServer 启动 PMS
源码路径:
frameworks/base/services/java/com/android/server/SystemServer.java
PMS 是 Android 启动服务的关键部分,在 startOtherServices() 方法中初始化:
kotlin
private void startOtherServices() {
PackageManagerService pms = PackageManagerService.main(mSystemContext);
ServiceManager.addService("package", pms);
}
- 主要工作:
1、调用 PackageManagerService.main() 方法,启动 PMS。
2、注册 PMS 服务到 ServiceManager,供系统其他服务访问。
1.2 PMS 的入口方法 main
代码路径: frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
kotlin
public static PackageManagerService main(Context context) {
Installer installer = new Installer();
return new PackageManagerService(context, installer, ...);
}
- 主要工作:
创建 Installer 实例,用于管理 APK 文件的安装和删除。
构造 PackageManagerService 对象。
1.3 PMS 构造函数
代码路径: frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
kotlin
public PackageManagerService(Context context, Installer installer, ...) {
mContext = context;
mInstaller = installer;
// 初始化数据结构
mPackages = new ArrayMap<>();
mComponentResolver = new ComponentResolver();
// 创建并启动后台线程
mBackgroundThread = BackgroundThread.get();
mHandler = new PackageHandler(mBackgroundThread.getLooper());
// 初始化权限管理服务
mPermissionManager = new PermissionManagerService(context, ...);
// 恢复持久化存储的数据
mSettings = new Settings(...);
mSettings.readPackageRestrictionsLPr();
// 启动扫描逻辑
scanPackages();
}
初始化内容:
- 核心数据结构 :
mPackages: 存储已安装包的信息。
mComponentResolver: 解析组件(Activity、Service 等)依赖关系。 - 后台线程 :
创建 BackgroundThread 处理异步任务,如 APK 扫描和安装。 - 权限管理 :
初始化 PermissionManagerService 管理动态权限和默认权限。 - 持久化存储 :
从 packages.xml 和 packages.list 加载已安装应用的状态。 - 启动扫描 :
调用 scanPackages(),扫描系统目录和用户目录。
1.4 扫描 APK 文件
代码路径: frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
kotlin
private void scanPackages() {
scanDirLI(new File("/system/app"), ...); // 扫描系统目录
scanDirLI(new File("/data/app"), ...); // 扫描用户目录
}
private void scanDirLI(File dir, ...) {
File[] files = dir.listFiles();
for (File file : files) {
if (file.isFile() && file.getName().endsWith(".apk")) {
try {
// 解析 APK
PackageParser.Package pkg = parsePackage(file);
// 注册包信息
scanPackageLI(pkg, ...);
} catch (PackageParserException e) {
Slog.e(TAG, "Failed to parse package: " + file, e);
}
}
}
}
解析逻辑:
- 调用 PackageParser 解析 APK 的 AndroidManifest.xml。
- 验证签名、权限、依赖等信息。
- 将解析后的包注册到内存数据结构。
1.5 权限初始化
代码路径: frameworks/base/services/core/java/com/android/server/pm/PermissionManagerService.java
kotlin
public void initializeDefaultPermissions() {
// 加载默认权限配置
loadDefaultPermissions();
for (String permission : defaultPermissions) {
grantPermission(permission);
}
}
主要工作:
- 加载 /etc/permissions 目录下的权限定义文件。
- 为系统应用自动授予默认权限。
1.6 提供对外服务
代码路径: frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
注册到 ServiceManager 后,PMS 提供以下对外服务:
1、获取已安装应用列表:
kotlin
public List<PackageInfo> getInstalledPackages() {...}
2、安装应用:
kotlin
public void installPackage(String packageName, ...) {...}
3、卸载应用:
kotlin
public void deletePackage(String packageName, ...) {...}
2. APK 安装机制
2.1. 安装请求的触发
入口:
- 命令行触发:通过 ADB 命令安装 APK
- 系统触发:例如应用商店或系统应用发起安装请求。
核心代码路径:
PackageManagerService 类:
方法 : installPackageAsUser()
路径 : frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
功能:接收安装请求,校验请求来源并创建安装任务。
kotlin
public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer, int installFlags, int userId) {
...
// 校验权限,检查安装来源合法性
// 创建安装任务,提交到后台处理
}
2.2 APK 文件解析与验证
在接收到安装请求后,PMS 通过 PackageParser 类解析 APK 文件,提取其元数据并进行签名校验。
PackageManagerService 调用 PackageParser 来解析 APK 文件,并将解析得到的包信息传递给后续处理模块。
关键类 :
PackageParser:
路径 : frameworks/base/core/java/android/content/pm/PackageParser.java
方法 : parsePackage()
功能: 解析 APK 文件中的 AndroidManifest.xml,提取包名、版本、权限等信息,并封装到 Package 对象中。
kotlin
public static Package parsePackage(File apkFile, int flags) throws PackageParserException {
...
// 解析 APK 的 manifest 文件,获取包信息
return new Package(pkgName);
}
2.3 签名校验
PackageManagerService 需要确保新安装的 APK 是合法的,尤其是对于系统应用或需要特定权限的应用,签名校验是一个关键步骤。PackageUtils 提供了签名校验的功能。
在解析 APK 后,PackageManagerService 会调用 PackageUtils.compareSignatures() 方法,校验安装包的签名
关键类:
PackageUtils:
源码路径 :frameworks/base/core/java/android/content/pm/PackageUtils.java
入口 :PackageUtils(compareSignatures() 方法)
作用:校验 APK 签名,确保其合法性。
kotlin
public boolean compareSignatures(Signature[] newSig, Signature[] oldSig) {
return SignatureUtils.areSignaturesMatching(newSig, oldSig); // 校验签名是否匹配
}
2.4 权限管理
2.4.1 权限声明
在 AndroidManifest.xml 文件中,应用声明所需的权限,例如 INTERNET、CAMERA 等。这些权限会在应用安装时进行解析。
示例:
kotlin
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.CAMERA"/>
2.4.2 权限校验与分配
- 在应用安装时,PackageManagerService 会调用 grantPermissions() 方法来根据 AndroidManifest.xml 中声明的权限,为应用授予相应的权限。
- 对于 危险权限,Android 会在运行时请求用户授权(在应用首次访问权限时)。
关键类:
PackageManagerService:
源码路径 :frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
入口 :PackageManagerService(grantPermissions() 方法)
作用:分配权限,确保安装的应用可以正常使用系统资源。
kotlin
public void grantPermissions(Package pkg) {
if (pkg != null) {
for (Permission perm : pkg.permissions) {
grantPermission(perm); // 授予应用声明的权限
}
}
}
2.5 持久化存储
在 APK 安装过程中,系统需要将应用的相关信息持久化存储,以确保在系统重启后仍然能访问到应用信息,并且保证应用的权限和其他元数据不会丢失。
-
安装包信息存储
在应用安装时,PackageManagerService 会将应用的信息(如包名、版本、权限 等)写入系统的持久化存储中。这个过程通常通过 writePackagesLocked() 方法完成。
-
系统数据更新
系统维护一个应用数据库,存储有关已安装应用的所有信息(包括应用包名、路径、签名、权限等)。当应用安装或卸载时,PackageManagerService 会更新这些数据,确保系统的一致性。
关键类:
PackageManagerService:
源码路径 :frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
入口 :PackageManagerService(writePackagesLocked() 方法)
作用:将已安装的包信息存储在系统持久化存储中,确保系统重启后信息不会丢失。
kotlin
public void writePackagesLocked() {
...
writePackageListLPr(); // 将包信息写入存储
}
2.6 广播通知
在 APK 安装过程中,系统需要向其他组件(如 Launcher、系统 UI 等)广播应用安装成功的消息。这使得其他系统服务能够及时获知应用安装并进行相应操作,如更新图标或设置默认应用等。
关键类:
PackageManagerService:
源码路径 :frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
入口 :PackageManagerService(sendPackageAddedBroadcast() 方法)
作用:安装完成后,发送广播通知系统其他组件应用安装成功。
kotlin
public void sendPackageAddedBroadcast(String packageName, boolean replacing) {
Intent intent = new Intent(Intent.ACTION_PACKAGE_ADDED);
intent.setData(Uri.parse("package:" + packageName));
sendBroadcast(intent); // 发送广播
}
3. APK 解析流程
3.1 APK 文件读取
APK 文件通常是一个 .apk 格式的压缩包,里面包含了应用的所有资源、代码(DEX 文件)、清单文件(AndroidManifest.xml)、签名信息等。
- 读取文件 :系统首先需要通过文件路径或 URI 来读取 APK 文件。
在 PackageManagerService 中,安装包的处理通常是通过 installPackage 或 installPackageWithExistingManifest 方法进行的。PackageManagerService 是处理与安装相关的主要服务。
源码路径:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
kotlin
public void installPackage(final Uri packageUri, final IPackageInstallObserver2 observer,
final int flags, final int userId) {
// 调用 PackageParser 解析 APK
PackageParser.Package pkg = parsePackage(packageUri);
}
通过 PackageParser 类的 parsePackage 方法解析 APK。
3.2 APK 校验
-
签名校验:
ApkFile.getSignatures() 提取签名。
checkSignatures() 方法校验签名是否有效。
-
格式校验:
通过 ApkFile.contains() 检查 APK 文件结构是否完整。
使用 validateFileStructure() 校验 AndroidManifest.xml 和 classes.dex 文件是否存在。
-
权限校验:
PackageManagerService.resolvePermissions() 方法检查 AndroidManifest.xml 中声明的权限是否合法。
3.2.1 签名校验
在 Android 中,每个 APK 都必须进行签名校验,确保 APK 文件没有被篡改。签名校验是通过 PackageParser 类和 PackageManagerService 服务来进行的。
源码路径:frameworks/base/core/java/android/content/pm/PackageParser.java
在 PackageParser 中,parsePackage() 方法会解析 APK 文件并校验签名。签名校验的主要工作是通过 ApkFile 类来获取 APK 的签名,然后通过 checkSignatures() 方法校验签名的有效性。
kotlin
public static Package parsePackage(File packageFile, int flags) throws PackageParser.PackageParserException {
// 创建 ApkFile 对象,用于解析 APK
ApkFile apkFile = new ApkFile(packageFile);
// 获取签名
Signatures signatures = apkFile.getSignatures();
// 校验签名
checkSignatures(signatures);
}
ApkFile.getSignatures() 会从 APK 中提取签名数据。接下来通过 checkSignatures() 来验证签名。
checkSignatures() 方法
在 checkSignatures() 方法中,系统会验证签名是否为空或者无效,如果签名校验失败,APK 的安装会被终止。
kotlin
private static void checkSignatures(Signatures signatures) {
if (signatures == null || signatures.length == 0) {
throw new PackageParser.PackageParserException("Missing signatures for package");
}
// 更多签名验证逻辑,比如签名一致性检查
// 如果签名不匹配或无效,将会抛出异常
}
逻辑:
- 如果签名为空,抛出 PackageParserException 异常。
- 如果签名无效或与系统要求不匹配,APK 安装会失败。
3.2.2 APK 格式校验
APK 文件需要遵循特定的结构:包含 AndroidManifest.xml 文件,至少一个 classes.dex 文件等。PackageParser 会在解析 APK 时进行格式校验,确保 APK 的结构完整且符合要求。
文件结构校验
源码路径:frameworks/base/core/java/android/content/pm/PackageParser.java
validateFileStructure() 方法负责检查 APK 文件的结构是否完整,特别是是否包含 AndroidManifest.xml 和 classes.dex 等必要文件。
kotlin
private static void validateFileStructure(ApkFile apkFile) throws PackageParser.PackageParserException {
// 检查是否包含 AndroidManifest.xml 文件
if (!apkFile.contains("AndroidManifest.xml")) {
throw new PackageParser.PackageParserException("Missing AndroidManifest.xml in APK");
}
// 检查是否包含 classes.dex 文件
if (!apkFile.contains("classes.dex")) {
throw new PackageParser.PackageParserException("Missing classes.dex in APK");
}
}
逻辑:
- 使用 apkFile.contains() 方法检查 AndroidManifest.xml 和 classes.dex 文件是否存在。
- 如果缺少其中任何一个文件,抛出 PackageParserException 异常,终止安装。
3.2.3 权限检查
确认应用声明的权限是否符合系统要求
源码路径:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
在安装过程中,PackageManagerService 会调用 resolvePermissions() 方法来检查应用所声明的权限。
kotlin
public void resolvePermissions(Package pkg) {
// 解析权限
for (String perm : pkg.requestedPermissions) {
// 检查权限是否合法
if (!isPermissionValid(perm)) {
throw new PackageManagerException("Invalid permission: " + perm);
}
}
}
逻辑:
- 遍历 AndroidManifest.xml 中声明的权限。
- 调用 isPermissionValid() 方法检查权限是否合法。
- 如果权限无效,抛出异常,安装被中止。
3.2.4 其他格式校验
除了签名和权限校验外,还会进行以下校验:
Manifest 校验:系统会检查 AndroidManifest.xml 文件的内容是否符合系统要求,例如检查包名是否重复,组件是否声明完整等。
PackageParser 中的 parseManifest() 方法负责解析 AndroidManifest.xml 文件并进行格式校验。
kotlin
private static void parseManifest(File packageFile, Package pkg) throws PackageParser.PackageParserException {
// 解析 AndroidManifest.xml
try {
// 解析逻辑
} catch (Exception e) {
throw new PackageParser.PackageParserException("Manifest parsing failed: " + e.getMessage());
}
}
哈希校验 :为了确保文件没有被篡改,系统会对 classes.dex、资源文件等进行哈希校验。
校验 classes.dex 文件是否存在并有效。classes.dex 文件是应用的核心字节码文件,必须存在并符合格式。
3.3 安装信息持久化
APK 解析完成后,PackageManagerService 会将解析得到的信息持久化到系统数据库中,例如应用的包名、版本、权限等。
源码路径:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
在 PackageManagerService 中,installPackage() 方法会将解析后的应用信息持久化。
kotlin
public int installPackage(Package pkg) {
// 保存应用信息
savePackageToDatabase(pkg);
// 其他安装过程
}
逻辑:
将 Package 对象保存到数据库中,确保应用信息能够在后续访问中使用。
4. Intent 和组件管理
Intent 和组件管理是系统的重要组成部分,负责管理和调度应用程序的各个组件(如 Activity、Service、BroadcastReceiver 和 ContentProvider)。Intent 作为通信的媒介,允许不同的组件之间进行交互和数据传递。PackageManagerService(PMS)是管理和调度这些组件的核心服务,负责根据 Intent 定位和启动对应的组件。
4.1 Intent 的作用
Intent 是 Android 中用于描述一个应用组件执行某个动作的对象。它不仅用于启动活动(Activity),服务(Service)和广播(BroadcastReceiver),还用于传递数据和指示操作。可以通过 Intent 向系统请求某些操作,例如启动另一个应用的组件、发送广播等。
Intent 主要有以下几种类型:
显式 Intent:明确指定目标组件(如包名和类名)。通常用于在同一应用内启动组件。
隐式 Intent:没有指定具体的目标组件,而是通过指定操作(如 ACTION_VIEW)让系统根据 Intent 的内容查找合适的组件来响应。
4.2 组件的管理
在 Android 中,每个应用包含若干个组件(如 Activity、Service、BroadcastReceiver 和 ContentProvider)。PMS 负责管理这些组件,并根据 Intent 选择合适的组件进行调度。
组件管理的核心操作包括 :
注册组件 :当应用安装或更新时,PMS 会解析应用的 AndroidManifest.xml 文件,获取应用的组件信息,并将其注册到系统中。
查找组件 :当收到一个 Intent 时,PMS 会根据该 Intent 的操作、数据、类别等信息,在所有已安装的应用中查找能够响应该 Intent 的组件。
启动组件:当 PMS 找到匹配的组件时,它会启动相应的 Activity、Service 或 BroadcastReceiver。
源码解析
PackageManagerService(PMS)是管理和调度组件的核心服务。当通过 Intent 启动一个组件时,系统会首先通过 PMS 查找符合条件的组件,并通过 ActivityManagerService 启动对应的组件。以下是相关的关键代码和流程。
resolveIntent() 方法
路径: frameworks/base/core/java/android/content/pm/ResolveInfo.java
kotlin
PackageManagerService 中的 resolveIntent() 方法负责根据 Intent 查找符合条件的组件。
public ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags) {
// 查找能够处理 Intent 的所有组件
List<ResolveInfo> queryIntentActivities = queryIntentActivities(intent, resolvedType, flags);
for (ResolveInfo resolveInfo : queryIntentActivities) {
if (resolveInfo.activityInfo.packageName.equals("com.example.target")) {
return resolveInfo;
}
}
return null;
}
作用:该方法根据传入的 Intent,在已安装的应用中查找能够处理该 Intent 的 Activity。在返回的 ResolveInfo 中包含了与 Intent 匹配的组件信息。
参数 :
intent:目标 Intent 对象。
resolvedType:意图的类型。
flags:标记,用于决定匹配策略。
queryIntentActivities() 方法
路径frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
queryIntentActivities() 方法用于查找系统中能够响应指定 Intent 的所有组件,返回一个 ResolveInfo 列表。它主要会遍历系统中已安装应用的组件列表,找到符合条件的组件。
kotlin
public List<ResolveInfo> queryIntentActivities(Intent intent, String resolvedType, int flags) {
List<ResolveInfo> result = new ArrayList<>();
// 遍历所有已安装的应用,寻找匹配的组件
for (ApplicationInfo appInfo : mAppDirs) {
List<ResolveInfo> appResult = queryIntentActivitiesForPackage(intent, appInfo.packageName, resolvedType, flags);
result.addAll(appResult);
}
return result;
}
作用 :查找所有能够响应给定 Intent 的 Activity,返回一个包含 ResolveInfo 对象的列表。
流程 :
遍历系统中所有已安装的应用。
对每个应用执行匹配检查,查看是否有 Activity 能够处理传入的 Intent。
Intent 启动组件的流程
当 Intent 发起组件调用时,PMS 负责解析和匹配相关组件,然后通过 ActivityManagerService 来启动对应的组件。具体流程如下:
1、用户或系统发起 Intent:一个 Intent 请求组件(如启动一个 Activity)。
2、PMS 解析 Intent:PackageManagerService 会调用 resolveIntent() 或 queryIntentActivities() 等方法,解析 Intent 并查找合适的组件。
3、返回匹配的组件:如果找到匹配的组件,PMS 会返回该组件的详细信息(如 ResolveInfo)。
4、ActivityManagerService 启动组件:ActivityManagerService 会通过 startActivity() 或 startService() 启动对应的组件。
权限和安全性管理
在进行组件管理时,系统还会进行权限校验,确保只有具有相应权限的应用才能启动某些组件。PackageManagerService 会根据 Intent 中的请求权限以及目标组件的权限声明进行权限检查,确保安全性。
5. 卸载流程
5.1 卸载请求的触发
卸载请求通常是由用户通过系统设置或者其他应用程序发起的。调用的 API 是 PackageManager.deletePackage()。
路径
frameworks/base/core/java/android/content/pm/PackageManager.java。这个方法是对外提供的卸载接口。
kotlin
public int deletePackage(String packageName, IPackageDeleteObserver observer, int flags) {
// 调用 PackageManagerService 的删除方法
mPackageManagerService.deletePackage(packageName, observer, flags);
}
作用:
- deletePackage() 方法接收包名、卸载观察者和标志位。该方法会通过 PackageManagerService 调用实际的卸载逻辑。
- 会根据 flags 参数确定是否保留应用的数据或是强制卸载
5.2 PackageManagerService 接收到卸载请求
卸载请求被 PackageManagerService(PMS)接收后,首先会进行一系列的初步验证和检查。PMS 是处理所有包管理事务的核心组件。
路径:frameworks/base/core/java/android/content/pm/PackageManager.java
kotlin
public void deletePackage(String packageName, IPackageDeleteObserver observer, int flags) {
// 权限检查
enforceSystemOrRoot();
// 获取要卸载的 PackageSetting 对象
PackageSetting ps = mSettings.getPackageSetting(packageName);
// 如果没有找到对应包名,则返回错误
if (ps == null) {
throw new IllegalArgumentException("Package " + packageName + " not found");
}
// 删除包裹的 APK 文件
deletePackageFile(packageName);
// 清除与该应用相关的数据
deleteDataDirsLIL(packageName);
// 更新系统数据库状态,移除包记录
removePackageData(ps);
// 发送卸载完成的广播
sendPackageRemovedBroadcast(packageName);
}
卸载过程中,系统会检查是否有权限执行该操作。
路径:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
kotlin
private void enforceSystemOrRoot() {
if (Binder.getCallingUid() != Process.SYSTEM_UID && Binder.getCallingUid() != Process.ROOT_UID) {
throw new SecurityException("Permission Denial: cannot delete package");
}
}
作用:
如果请求不是来自系统 UID 或 ROOT UID,则抛出 SecurityException,拒绝卸载请求。
作用 :
1、调用 enforceSystemOrRoot() 来检查请求是否来自系统或有适当权限的进程。
2、通过 mSettings.getPackageSetting(packageName) 获取与包名相关的设置对象。
3、删除包裹的 APK 文件和与该应用相关的数据。
5.3 停用应用
在卸载之前,需要停止正在运行的应用进程和服务,防止应用在卸载过程中仍然活动。
路径:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
kotlin
public void forceStopPackage(String packageName) {
// 终止正在运行的进程和服务
mActivityManager.forceStopPackage(packageName);
}
作用:
停止与卸载应用相关的进程和服务,确保不会在卸载过程中影响系统。
5.4 删除应用数据
卸载时需要删除应用的数据文件,包括缓存、数据库等。
路径:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
kotlin
private void deleteDataDirsLIL(String packageName) {
// 删除应用的私有数据目录
File dataDir = new File("/data/data/" + packageName);
if (dataDir.exists()) {
FileUtils.deleteContents(dataDir);
}
// 删除缓存目录
File cacheDir = new File("/data/cache/" + packageName);
if (cacheDir.exists()) {
FileUtils.deleteContents(cacheDir);
}
}
5.5 更新系统数据库
卸载过程中,系统数据库(如包管理数据库)需要移除该应用的相关信息。
路径:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
kotlin
private void removePackageData(PackageSetting packageSetting) {
// 从数据库中删除该应用相关信息
mSettings.removePackage(packageSetting);
// 更新数据库以确保删除记录
mSettings.writeLPr();
}
作用 :
删除与该应用相关的所有系统记录,包括应用的权限、签名、路径等信息。
使用 mSettings.writeLPr() 方法将更新后的数据库写入磁盘。
5.6 广播通知
卸载完成后,系统需要广播卸载信息,通知其他应用或系统组件。
路径:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
kotlin
private void sendPackageRemovedBroadcast(String packageName) {
// 发送卸载广播
Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED, Uri.parse("package:" + packageName));
intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
mContext.sendBroadcast(intent);
}
作用:
向系统广播卸载事件,通知其他系统组件(如 Launcher、应用管理等)更新其状态。
5.7 删除 APK 文件
卸载过程中,系统会删除与应用相关的 APK 文件。
路径:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
kotlin
private void deletePackageFile(String packageName) {
// 删除 APK 文件
File apkFile = new File("/data/app/" + packageName);
if (apkFile.exists()) {
apkFile.delete();
}
}
作用:
删除存储在 /data/app/ 或 /system/app/ 下的 APK 文件,释放存储空间。
5.8 更新 UI
卸载后,系统 UI(如 Launcher)需要更新,移除已卸载应用的图标。
路径:frameworks/base/core/java/android/app/ActivityManager.java
kotlin
public void removePackageFromLauncher(String packageName) {
// 从 Launcher 中移除应用图标
Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED, Uri.parse("package:" + packageName));
mContext.sendBroadcast(intent);
}
作用:
通知 Launcher 更新其 UI,删除已卸载应用的图标。
6. 持久化机制
为了保持应用程序数据的一致性和持久化,PMS 需要使用持久化机制来存储和管理已安装应用的相关信息
6.1 持久化数据存储位置
PMS 主要通过 PackageManagerService 内部的 Settings 对象来管理应用的持久化数据。Settings 类封装了应用安装和卸载所需的所有信息,并提供了将这些信息持久化到磁盘的能力。具体来说,持久化的应用信息存储在系统的数据库文件中,通常位于 /data/system/ 目录下。
6.2 数据存储格式
PMS 使用的是基于 XML 文件的数据存储格式,所有关于已安装应用的信息(如包名、路径、权限等)都被保存在一个 XML 文件中。这个 XML 文件被称为 packages.xml,它包含了所有已安装应用的元数据。
路径:/data/system/packages.xml
该文件存储着系统中所有安装应用的配置,包括每个应用的包名、路径、安装时间、签名、权限等。
6.3 持久化流程
6.3.1 应用安装时持久化
当新应用被安装时,PMS 会更新 packages.xml 文件,新增该应用的相关信息。安装时,PMS 会将应用的包名、路径、签名等信息存储在该文件中,确保这些信息可以在系统重启后依然存在。
路径:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
kotlin
private void addPackageSetting(PackageSetting ps) {
// 将 PackageSetting 对象持久化到 Settings 数据中
mSettings.addPackageSetting(ps);
// 更新 packages.xml 文件
mSettings.writeLPr();
}
作用:
addPackageSetting() 方法将新安装应用的 PackageSetting 对象添加到 Settings 数据中,并调用 mSettings.writeLPr() 更新 packages.xml 文件。
6.3.2 应用卸载时持久化
当应用被卸载时,PMS 会从 packages.xml 文件中删除该应用的记录,并更新文件。
路径:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
kotlin
private void removePackageData(PackageSetting ps) {
// 从 settings 数据中移除应用的记录
mSettings.removePackage(ps);
// 更新 packages.xml 文件
mSettings.writeLPr();
}
作用:
removePackageData() 方法会删除卸载应用的所有记录,并通过 mSettings.writeLPr() 更新 packages.xml 文件,确保卸载后的数据一致性。
6.3.3 应用更新时持久化
当应用更新时,PMS 会修改原有记录并更新 packages.xml 文件,以反映应用的新状态(如新版本号、安装路径等)。
路径:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
kotlin
private void updatePackageSetting(PackageSetting oldPs, PackageSetting newPs) {
// 更新旧的 PackageSetting 记录为新的记录
mSettings.replacePackageSetting(oldPs, newPs);
// 更新 packages.xml 文件
mSettings.writeLPr();
}
作用:
updatePackageSetting() 方法用于更新应用的持久化数据,确保新版本的应用信息被正确保存,并通过 mSettings.writeLPr() 更新 packages.xml 文件。
主要类
Settings 类的作用
Settings 类会将应用的配置信息存储为一系列 PackageSetting 对象。
路径:frameworks/base/services/core/java/com/android/server/pm/Settings.java
kotlin
public class Settings {
private final File mPackagesFile;
private Map<String, PackageSetting> mPackages = new HashMap<>();
public void readLPr() {
// 读取 packages.xml 文件并解析成 Map 数据
FileInputStream fis = null;
try {
fis = new FileInputStream(mPackagesFile);
// 解析 XML 文件,将数据加载到 mPackages 中
XmlUtils.readMapXml(fis, mPackages);
} catch (IOException e) {
Slog.e(TAG, "Error reading packages.xml", e);
} finally {
IoUtils.closeQuietly(fis);
}
}
public void writeLPr() {
// 将 mPackages 中的数据写入 packages.xml 文件
FileOutputStream fos = null;
try {
fos = new FileOutputStream(mPackagesFile);
// 将 Map 数据以 XML 格式写入文件
XmlUtils.writeMapXml(fos, mPackages);
} catch (IOException e) {
Slog.e(TAG, "Error writing packages.xml", e);
} finally {
IoUtils.closeQuietly(fos);
}
}
public void addPackageSetting(PackageSetting ps) {
mPackages.put(ps.name, ps);
}
public void removePackage(PackageSetting ps) {
mPackages.remove(ps.name);
}
}
作用:
readLPr ():从 packages.xml 文件读取数据并解析为 mPackages 映射。
writeLPr ():将 mPackages 中的数据写入到 packages.xml 文件。
addPackageSetting ():将一个 PackageSetting 对象添加到 mPackages 中,表示添加一个新的应用。
removePackage():从 mPackages 中删除一个 PackageSetting 对象,表示卸载一个应用。
PackageSetting 类的作用
PackageSetting 类表示一个应用程序的持久化数据。它包含了应用的各种元信息,如包名、签名、权限、安装路径、版本号等。
路径:frameworks/base/services/core/java/com/android/server/pm/PackageSetting.java
kotlin
public class PackageSetting {
public String name;
public String codePath;
public String resourcePath;
public String dataDir;
public int versionCode;
public String[] signatures;
public boolean isSystemApp;
}
作用 :
PackageSetting 用于存储每个应用的核心信息,包含应用的路径、签名、版本号、系统应用标识 等。
它是 Settings 类和 PackageManagerService 中用于持久化存储和管理应用元数据的主要对象。
总结
这篇文章的内容概述了 Android 系统中包管理服务(PMS)的工作原理,涉及从应用安装到卸载以及相关的持久化机制。以下是各章节的总结:
1、PMS 的初始化:
- 介绍了系统启动时,SystemServer 启动 PMS 的过程,并通过 main 方法初始化 PMS。
- PMS 构造函数负责初始化必要的资源,并扫描 APK 文件以便安装。
- 权限初始化过程和对外服务的提供是 PMS 的重要功能。
2、APK 安装机制:
描述了安装请求的触发、APK 文件的解析和验证过程,包括签名校验和权限管理。
权限声明与校验、持久化存储和广播通知等过程确保 APK 安全和正确安装。
3、APK 解析流程:
介绍了 APK 文件的读取、校验、权限检查等细节。
通过签名校验、格式校验等多项验证确保 APK 的有效性,并将安装信息持久化。
4、Intent 和组件管理:
讲解了 Intent 的作用以及如何通过 resolveIntent() 和 queryIntentActivities() 方法进行组件管理,确保应用间的通信和组件调用正确。
5、卸载流程:
描述了卸载请求的触发和处理过程,包括停用应用、删除数据、更新系统数据库、广播通知等步骤。
最终删除 APK 文件并更新 UI。
6、持久化机制:
讨论了持久化数据存储的位置、格式以及持久化流程。
重点描述了在应用安装、卸载和更新时的数据存储和管理方式。
总的来说,文章从 PMS 初始化到 APK 安装、解析、卸载及持久化机制,详细介绍了 Android 系统中包管理服务的运作流程。