PMS与APK安装
# 12.PMS与APK安装
# 目录介绍
- 01.为什么需要PMS
- 1.1 包管理的核心问题
- 1.2 PMS的核心职责
- 1.3 PMS在系统中的位置
- 02.PMS的整体架构设计
- 2.1 核心类关系
- 2.2 核心数据结构
- 2.3 PMS与installd的协作
- 03.PMS的启动与包扫描
- 3.1 包扫描的五个阶段
- 3.2 扫描核心代码
- 3.3 并行扫描优化
- 04.APK文件结构详解
- 4.1 APK的本质
- 4.2 DEX文件格式
- 4.3 resources.arsc结构
- 05.APK的签名与验证机制
- 5.1 签名方案演进
- 5.2 V2签名的结构
- 5.3 PMS中的签名验证
- 06.APK安装的完整流程
- 6.1 安装入口
- 6.2 安装的完整流程
- 6.3 安装关键代码
- 07.PackageParser解析原理
- 7.1 解析流程
- 7.2 Manifest解析详情
- 7.3 Intent Filter解析
- 08.dex优化与编译
- 8.1 dex编译的演进
- 8.2 dex2oat编译过程
- 8.3 编译过滤器
- 09.权限管理机制
- 9.1 权限的分类
- 9.2 权限检查流程
- 9.3 运行时权限的授予
- 10.Intent解析与组件匹配
- 10.1 Intent解析的核心
- 10.2 Intent Filter匹配算法
- 10.3 多匹配结果的处理
- 11.多用户包管理
- 11.1 多用户模型
- 11.2 应用的禁用与隐藏
- 12.应用更新与覆盖安装
- 12.1 覆盖安装的判断
- 12.2 覆盖安装的处理
- 12.3 Split APK(分包安装)
- 13.应用卸载流程
- 13.1 卸载的完整流程
- 13.2 系统应用的特殊处理
- 14.PMS的数据持久化
- 14.1 packages.xml文件
- 14.2 持久化时机
- 14.3 运行时权限持久化
- 15.PMS的安全机制
- 15.1 安装来源验证
- 15.2 UID隔离
- 15.3 Verified Boot与APK完整性
- 16.总结与技术思考
- 16.1 核心要点回顾
- 16.2 面试高频问题
- 16.3 调试命令
# 01.为什么需要PMS
# 1.1 包管理的核心问题
Android系统中可能安装了数百个应用,每个应用声明了多个组件。当一个应用需要启动另一个应用的组件时,系统需要知道:这个组件存在吗?它在哪个APK里?需要什么权限?
如果每次使用都去解析APK文件,效率太低。所以需要PMS在系统启动时扫描所有APK,建立全局的包信息索引。
# 1.2 PMS的核心职责
PMS的职责:
├── 包信息管理:解析Manifest,维护包名→组件的映射
├── 安装管理:APK安装、卸载、更新
├── 权限管理:权限声明、授予、检查
├── Intent解析:组件匹配、Intent Filter匹配
├── 签名验证:APK签名校验、一致性检查
└── 数据持久化:packages.xml、权限状态
2
3
4
5
6
7
# 1.3 PMS在系统中的位置
PMS运行在SystemServer进程中,是最先启动的核心服务之一。应用通过PackageManager接口访问PMS,实际安装工作由installd守护进程完成。installd运行在Native层,具有root权限,负责文件操作和dex编译。
# 02.PMS的整体架构设计
# 2.1 核心类关系
PMS核心类关系图:
┌──────────────────────────────────────────┐
│ PackageManagerService │
│ ┌─────────────────────────────────┐ │
│ │ Settings │ │
│ │ ├── mPackages (已安装包信息) │ │
│ │ ├── mPermissions (权限信息) │ │
│ │ └── mSharedUsers (共享用户) │ │
│ └─────────────────────────────────┘ │
│ ┌─────────────────────────────────┐ │
│ │ ComponentResolver │ │
│ │ ├── mActivities (Activity解析) │ │
│ │ ├── mReceivers (Receiver解析) │ │
│ │ ├── mServices (Service解析) │ │
│ │ └── mProviders (Provider解析) │ │
│ └─────────────────────────────────┘ │
│ ┌─────────────────────────────────┐ │
│ │ PackageInstallerService │ │
│ │ └── 处理APK安装/卸载请求 │ │
│ └─────────────────────────────────┘ │
│ ┌─────────────────────────────────┐ │
│ │ PermissionManagerService │ │
│ │ └── 权限授予/检查/撤销 │ │
│ └─────────────────────────────────┘ │
└──────────────────────────────────────────┘
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 2.2 核心数据结构
// PackageSetting - 已安装包的核心描述
class PackageSetting extends PackageSettingBase {
String name; // 包名
File codePath; // APK文件位置
File resourcePath; // 资源路径
PackageSignatures signatures; // 签名信息
int appId; // 应用ID
long firstInstallTime; // 首次安装时间
long lastUpdateTime; // 最后更新时间
InstallSource installSource; // 安装来源
SparseArray<PackageUserState> mUserState; // 每个用户的状态
SharedUserSetting sharedUser; // SharedUser信息
}
// AndroidPackage - 解析后的完整包信息
interface AndroidPackage {
String getPackageName();
long getLongVersionCode();
List<ParsedActivity> getActivities();
List<ParsedService> getServices();
List<ParsedProvider> getProviders();
List<ParsedReceiver> getReceivers();
List<ParsedPermission> getPermissions();
List<ParsedUsesPermission> getUsesPermissions();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 2.3 PMS与installd的协作
PMS与installd的分工:
SystemServer进程 installd进程(Native)
┌──────────────────┐ ┌──────────────────┐
│ PMS │ Binder │ installd │
│ 逻辑决策: │◄────────────►│ 执行操作: │
│ ├── 解析APK │ │ ├── dex2oat │
│ ├── 权限检查 │ │ ├── mkdir │
│ ├── 签名验证 │ │ ├── 文件复制 │
│ └── 信息维护 │ │ ├── 权限设置 │
│ │ │ └── 数据清理 │
└──────────────────┘ └──────────────────┘
分离原因:
1. 文件操作需要root权限
2. dex编译是CPU密集型操作
3. 安全隔离
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 03.PMS的启动与包扫描
# 3.1 包扫描的五个阶段
PMS启动时的包扫描是系统启动中最耗时的步骤之一:
PMS包扫描的五个阶段:
阶段1:扫描系统包
├── /system/framework/*.apk (框架包)
├── /system/app/*.apk (系统预装应用)
├── /system/priv-app/*.apk (系统特权应用)
├── /vendor/app/*.apk (厂商应用)
└── /product/app/*.apk (产品应用)
阶段2:扫描覆盖包
└── /vendor/overlay/*.apk (资源覆盖包)
阶段3:扫描用户安装包
└── /data/app/*.apk (用户安装的应用)
阶段4:准备数据
├── 更新共享库
├── 更新权限信息
└── 清理残留数据
阶段5:dex优化
└── 对需要优化的包执行dex2oat编译
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 3.2 扫描核心代码
// PackageManagerService.java
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
// 1.初始化Settings,读取已有包信息
mSettings = new Settings(Environment.getDataDirectory());
mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false));
long startTime = SystemClock.uptimeMillis();
// 2.扫描系统包
scanDirTracedLI(frameworkDir,
mDefParseFlags | PackageParser.PARSE_IS_SYSTEM,
scanFlags | SCAN_NO_DEX | SCAN_AS_SYSTEM, 0);
scanDirTracedLI(systemAppDir,
mDefParseFlags | PackageParser.PARSE_IS_SYSTEM,
scanFlags | SCAN_AS_SYSTEM, 0);
scanDirTracedLI(systemPrivAppDir,
mDefParseFlags | PackageParser.PARSE_IS_SYSTEM,
scanFlags | SCAN_AS_SYSTEM | SCAN_AS_PRIVILEGED, 0);
// 3.扫描用户安装包
scanDirTracedLI(dataAppDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
// 4.更新权限
mPermissionManager.updateAllPermissions(
StorageManager.UUID_PRIVATE_INTERNAL, sdkUpdated);
// 5.持久化
mSettings.writeLPr();
Log.i(TAG, "Package scanning took " +
(SystemClock.uptimeMillis() - startTime) + "ms");
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 3.3 并行扫描优化
// ParallelPackageParser.java (Android 10+)
class ParallelPackageParser {
private final ExecutorService mService;
ParallelPackageParser(int maxThreads) {
mService = Executors.newFixedThreadPool(maxThreads);
}
void submit(File scanFile, int parseFlags) {
mService.submit(() -> {
ParseResult result = new ParseResult();
try {
result.parsedPackage = parsePackage(scanFile, parseFlags);
} catch (Exception e) {
result.throwable = e;
}
mQueue.put(result);
});
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
其他优化策略:增量扫描(只扫描变化的包)、缓存机制(/data/system/package_cache/)、延迟dex优化。
# 04.APK文件结构详解
# 4.1 APK的本质
APK本质是ZIP压缩包:
example.apk (ZIP格式)
├── AndroidManifest.xml // 二进制XML,声明组件和权限
├── classes.dex // 主Dex文件
├── classes2.dex // MultiDex
├── resources.arsc // 编译后的资源映射表
├── res/ // 资源文件
│ ├── layout/
│ ├── drawable-xxhdpi/
│ └── values/
├── lib/ // Native库
│ ├── armeabi-v7a/
│ │ └── libnative.so
│ └── arm64-v8a/
│ └── libnative.so
├── assets/ // 原始资源文件
├── META-INF/ // 签名信息(V1签名)
│ ├── MANIFEST.MF
│ ├── CERT.SF
│ └── CERT.RSA
└── kotlin/ // Kotlin元数据
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 4.2 DEX文件格式
DEX文件是Android的可执行格式:
DEX文件内部结构:
┌─────────────────┐
│ dex_header │ magic, checksum, SHA1签名
│ │ 文件大小,各区偏移量
├─────────────────┤
│ string_ids │ 所有字符串的索引
├─────────────────┤
│ type_ids │ 所有类型的索引
├─────────────────┤
│ proto_ids │ 方法原型索引
├─────────────────┤
│ field_ids │ 字段索引
├─────────────────┤
│ method_ids │ 方法索引
├─────────────────┤
│ class_defs │ 类定义
├─────────────────┤
│ call_site_ids │ 调用站点(Java 8+)
├─────────────────┤
│ method_handles │ 方法句柄(Java 8+)
├─────────────────┤
│ data │ 实际数据区
│ ├── string_data │
│ ├── type_lists │
│ ├── annotations │
│ ├── class_data │
│ └── code_items │ 字节码指令
├─────────────────┤
│ link_data │ 链接数据
└─────────────────┘
DEX vs JAR的区别:
- JAR: 每个类一个.class文件,字符串表独立
- DEX: 所有类合并到一个文件,共享字符串常量池
- DEX的优势:减少冗余、减小文件体积、加快加载速度
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 4.3 resources.arsc结构
resources.arsc资源映射表:
┌────────────────────────────────────┐
│ ResourceTable Header │
├────────────────────────────────────┤
│ Package Group 1 (com.example.app) │
│ ├── Type: layout │
│ │ ├── 0x7f040001 → res/layout/activity_main.xml
│ │ └── 0x7f040002 → res/layout/fragment_detail.xml
│ ├── Type: drawable │
│ │ ├── 0x7f020001 → res/drawable-xxhdpi/ic_launcher.png
│ │ └── 0x7f020002 → res/drawable-xhdpi/ic_launcher.png
│ ├── Type: string │
│ │ ├── 0x7f0b0001 → "Hello World" (default)
│ │ └── 0x7f0b0001 → "你好世界" (zh-rCN)
│ └── Type: color │
│ └── 0x7f050001 → #FF6200EE │
└────────────────────────────────────┘
资源ID格式:0xPPTTEEEE
PP: Package ID (应用=0x7f, 系统=0x01)
TT: Type ID (layout=04, drawable=02等)
EEEE: Entry ID (资源序号)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 05.APK的签名与验证机制
# 5.1 签名方案演进
APK签名方案的演进:
V1签名(JAR签名):
└── 基于JAR签名机制
└── 对每个文件单独签名
└── 缺点:验证慢(需要解压)、不保护非Manifest文件
└── META-INF/MANIFEST.MF + CERT.SF + CERT.RSA
V2签名(Android 7.0+):
└── 对整个APK进行签名(作为二进制块)
└── 签名信息插入ZIP文件的Central Directory之前
└── 优点:验证更快、保护整个APK完整性
└── 不需要解压即可验证
V3签名(Android 9.0+):
└── 在V2基础上支持密钥轮替
└── 包含证书链信息
└── 允许更换签名密钥而保持更新兼容
V4签名(Android 11+):
└── 增量安装支持
└── 基于fs-verity的Merkle树签名
└── 允许流式安装,边下载边安装
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 5.2 V2签名的结构
V2签名在APK中的位置:
┌─────────────────────────┐
│ ZIP Entry内容 │ ← 文件内容区域
│ (classes.dex等文件) │
├─────────────────────────┤
│ APK Signing Block │ ← V2签名数据(V1没有这个块)
│ ├── 大小(8字节) │
│ ├── ID-Value对 │
│ │ ├── V2签名数据 │
│ │ ├── V3签名数据 │
│ │ └── 其他数据 │
│ ├── 大小(8字节) │
│ └── Magic ("APK Sig Block 42")
├─────────────────────────┤
│ Central Directory │ ← ZIP中央目录
├─────────────────────────┤
│ EOCD │ ← ZIP结束记录
└─────────────────────────┘
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 5.3 PMS中的签名验证
// ApkSignatureVerifier.java
class ApkSignatureVerifier {
static PackageParser.SigningDetails verify(String apkPath, int minSignatureScheme) {
// 优先尝试最高版本的签名验证
// 1.尝试V4验证(如果需要)
// V4主要用于增量安装场景
// 2.尝试V3验证
SigningDetails v3Details = verifyV3Signature(apkPath);
if (v3Details != null) {
return v3Details;
}
// 3.尝试V2验证
SigningDetails v2Details = verifyV2Signature(apkPath);
if (v2Details != null) {
return v2Details;
}
// 4.回退到V1验证
if (minSignatureScheme <= SigningDetails.SignatureSchemeVersion.JAR) {
return verifyV1Signature(apkPath);
}
throw new PackageParserException("No valid signature found");
}
}
// 覆盖安装时的签名一致性检查
void verifySignaturesMatch(PackageSetting existingPkg, AndroidPackage newPkg) {
// 新包的签名必须与已安装包的签名匹配
if (!existingPkg.signatures.mSigningDetails
.checkCapability(newPkg.getSigningDetails(),
SigningDetails.CertCapabilities.INSTALLED_DATA)) {
throw new PackageManagerException(
"Package " + newPkg.getPackageName()
+ " signatures do not match previously installed version");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 06.APK安装的完整流程
# 6.1 安装入口
APK安装可以通过多种方式触发:
APK安装的入口方式:
├── 应用商店安装
│ └── PackageInstaller.Session.commit()
├── adb install
│ └── pm install → PackageManagerShellCommand
├── 文件管理器安装
│ └── Intent(ACTION_INSTALL_PACKAGE)
├── 系统启动时扫描安装
│ └── PMS.scanDirTracedLI()
└── 应用内安装(需要REQUEST_INSTALL_PACKAGES权限)
└── PackageInstaller API
2
3
4
5
6
7
8
9
10
11
# 6.2 安装的完整流程
APK安装的完整流程(以应用商店安装为例):
1. 创建安装会话
PackageInstaller.createSession(params)
└── PMS.allocateSessionId()
└── 返回sessionId
2. 写入APK数据
session.openWrite("base.apk", 0, -1)
└── 将APK数据写入staging目录
└── /data/app/vmdl{sessionId}.tmp/
3. 提交安装
session.commit(statusReceiver)
└── PackageInstallerSession.commitLocked()
│
├── 4.验证APK
│ ├── 签名验证(V2/V3签名校验)
│ ├── Manifest解析验证
│ └── 版本兼容性检查
│
├── 5.解析APK
│ └── PackageParser2.parsePackage()
│ ├── 解析AndroidManifest.xml
│ ├── 提取组件信息
│ └── 提取权限信息
│
├── 6.权限和策略检查
│ ├── 检查安装权限
│ ├── 检查设备策略限制
│ └── 如果是覆盖安装,检查签名一致性
│
├── 7.准备安装
│ ├── 创建目标目录 /data/app/包名-随机后缀/
│ ├── 复制APK到目标目录
│ ├── 提取Native库
│ │ └── /data/app/包名-xxx/lib/arm64-v8a/
│ └── 设置文件权限
│
├── 8.dex优化
│ └── installd.dexopt()
│ └── dex2oat将dex编译为oat
│ └── 输出到/data/app/包名-xxx/oat/
│
├── 9.提交变更
│ ├── 更新PMS中的包信息
│ ├── 更新权限状态
│ ├── 创建应用数据目录
│ │ └── /data/data/包名/ (CE存储)
│ │ └── /data/user_de/0/包名/ (DE存储)
│ └── 写入packages.xml
│
└── 10.通知
├── 发送ACTION_PACKAGE_ADDED广播
├── 通知Launcher更新图标
└── 回调安装结果
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# 6.3 安装关键代码
// InstallPackageHelper.java
void installStage(InstallParams params) {
// 1.复制APK
int ret = copyApk(params);
if (ret != PackageManager.INSTALL_SUCCEEDED) {
return;
}
// 2.解析APK
ParsedPackage parsedPackage = parsePackage(params.origin.file, parseFlags);
// 3.验证签名
collectCertificates(parsedPackage, false);
// 4.处理覆盖安装
if (isReplace) {
replacePackageLIF(parsedPackage, parseFlags, scanFlags,
args.user, installerPackageName, res);
} else {
installNewPackageLIF(parsedPackage, parseFlags, scanFlags,
args.user, installerPackageName, res);
}
// 5.dex优化
performDexOptTraced(parsedPackage.getPackageName(),
getCompilerFilterForReason(REASON_INSTALL));
// 6.发送安装完成广播
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
parsedPackage.getPackageName(), null, 0, null, null, null);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 07.PackageParser解析原理
# 7.1 解析流程
PackageParser(Android 11+为PackageParser2)负责将APK文件解析为内存中的数据结构:
// PackageParser2.java
public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches) {
if (useCaches) {
// 1.尝试从缓存加载
ParsedPackage cached = getCachedResult(packageFile, flags);
if (cached != null) return cached;
}
// 2.判断是单个APK还是Split APK目录
if (packageFile.isDirectory()) {
return parseClusterPackage(packageFile, flags);
} else {
return parseMonolithicPackage(packageFile, flags);
}
}
// 解析单个APK
private ParsedPackage parseMonolithicPackage(File apkFile, int flags) {
// 使用AssetManager打开APK
AssetManager assets = AssetManager.createForPackage();
assets.addAssetPath(apkFile.getAbsolutePath());
// 读取AndroidManifest.xml
XmlResourceParser parser = assets.openXmlResourceParser("AndroidManifest.xml");
// 解析Manifest的各个节点
ParsedPackage pkg = parseBaseApk(parser, assets, flags);
return pkg;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 7.2 Manifest解析详情
// ParsingPackageUtils.java
ParseResult<ParsingPackage> parseBaseApk(XmlResourceParser parser, ...) {
// 解析<manifest>标签
String packageName = parser.getAttributeValue(null, "package");
int versionCode = parser.getAttributeIntValue(null, "versionCode", 0);
ParsingPackage pkg = PackageImpl.forParsing(packageName);
while (parser.next() != XmlPullParser.END_DOCUMENT) {
String tagName = parser.getName();
switch (tagName) {
case "application":
parseBaseApplication(pkg, parser, ...);
break;
case "uses-permission":
parseUsesPermission(pkg, parser);
break;
case "permission":
parsePermission(pkg, parser);
break;
case "uses-feature":
parseUsesFeature(pkg, parser);
break;
case "uses-sdk":
parseUsesSdk(pkg, parser);
break;
}
}
return ParseResult.success(pkg);
}
// 解析<application>中的组件
void parseBaseApplication(ParsingPackage pkg, XmlResourceParser parser, ...) {
while (parser.next() != XmlPullParser.END_TAG) {
String tagName = parser.getName();
switch (tagName) {
case "activity":
case "activity-alias":
ParsedActivity activity = parseActivity(parser, ...);
pkg.addActivity(activity);
break;
case "service":
ParsedService service = parseService(parser, ...);
pkg.addService(service);
break;
case "receiver":
ParsedActivity receiver = parseActivity(parser, ...);
pkg.addReceiver(receiver);
break;
case "provider":
ParsedProvider provider = parseProvider(parser, ...);
pkg.addProvider(provider);
break;
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# 7.3 Intent Filter解析
// 解析Intent Filter
void parseIntentFilter(ParsedIntentInfo intentInfo, XmlResourceParser parser) {
while (parser.next() != XmlPullParser.END_TAG) {
String nodeName = parser.getName();
switch (nodeName) {
case "action":
String action = parser.getAttributeValue(NAMESPACE, "name");
intentInfo.addAction(action);
break;
case "category":
String category = parser.getAttributeValue(NAMESPACE, "name");
intentInfo.addCategory(category);
break;
case "data":
String scheme = parser.getAttributeValue(NAMESPACE, "scheme");
String host = parser.getAttributeValue(NAMESPACE, "host");
String path = parser.getAttributeValue(NAMESPACE, "path");
String mimeType = parser.getAttributeValue(NAMESPACE, "mimeType");
intentInfo.addDataScheme(scheme);
intentInfo.addDataAuthority(host, port);
intentInfo.addDataPath(path, PatternMatcher.PATTERN_LITERAL);
intentInfo.addDataType(mimeType);
break;
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 08.dex优化与编译
# 8.1 dex编译的演进
疑惑:为什么Android要对DEX文件进行额外的编译优化?
答疑:DEX字节码是平台无关的中间码,直接解释执行效率太低。Android需要将其编译为设备的本地机器码以提高运行效率。但编译策略经历了多次变化:
DEX编译策略的演进:
Android 2.2 (Froyo):JIT编译
└── 运行时即时编译热点代码
└── 优点:安装快,占用空间小
└── 缺点:运行时有编译开销
Android 5.0 (Lollipop):AOT编译
└── 安装时通过dex2oat将所有DEX编译为OAT文件
└── 优点:运行时无编译开销,执行效率高
└── 缺点:安装慢(大应用可能需要数分钟),占用大量存储
Android 7.0 (Nougat):混合编译
└── 安装时不编译,首次运行使用解释器
└── 运行时JIT编译热点代码,记录Profile
└── 空闲时根据Profile进行AOT编译(profile-guided compilation)
└── 优点:安装快 + 运行快 + 存储占用合理
Android 12+:Cloud Profile
└── 从Play Store下载聚合的Profile
└── 安装时就用Cloud Profile进行AOT编译
└── 首次运行即有接近完全编译的性能
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 8.2 dex2oat编译过程
dex2oat的工作过程:
输入:classes.dex (Dalvik字节码)
│
├── 1.加载DEX文件
│ └── 验证DEX文件格式
│
├── 2.类验证
│ └── 验证字节码的合法性
│ └── 检查类型安全
│
├── 3.编译
│ ├── 选择编译器后端
│ │ ├── Quick(旧版快速编译器)
│ │ └── Optimizing(优化编译器,默认)
│ ├── 构建中间表示(HIR/LIR)
│ ├── 执行优化 Pass
│ │ ├── 常量折叠
│ │ ├── 死代码消除
│ │ ├── 内联
│ │ ├── 寄存器分配
│ │ └── 指令调度
│ └── 生成目标机器码(ARM/x86)
│
└── 4.输出
├── .oat文件(ELF格式,包含编译后的机器码)
├── .art文件(预初始化的类和对象)
└── .vdex文件(验证后的DEX,用于快速重编译)
输出文件存放位置:
/data/app/包名-xxx/oat/arm64/
├── base.odex (编译后的机器码)
├── base.vdex (验证后的DEX)
└── base.art (预初始化数据)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 8.3 编译过滤器
PMS根据不同场景选择不同的编译级别:
// 编译过滤器(Compiler Filter)
public static final String COMPILER_FILTER_VERIFY = "verify";
// 只验证,不编译(最快安装,最慢运行)
public static final String COMPILER_FILTER_QUICKEN = "quicken";
// 快速优化(deprecated in Android 12)
public static final String COMPILER_FILTER_SPEED_PROFILE = "speed-profile";
// 根据Profile编译热点方法(推荐,空间和性能的平衡)
public static final String COMPILER_FILTER_SPEED = "speed";
// 编译所有方法(最慢安装,最快运行,最大空间)
// 不同安装场景的编译策略
String getCompilerFilterForReason(int reason) {
switch (reason) {
case REASON_FIRST_BOOT: return "verify"; // 首次开机
case REASON_BOOT_AFTER_OTA: return "verify"; // OTA后开机
case REASON_INSTALL: return "speed-profile"; // 应用安装
case REASON_BACKGROUND_DEXOPT: return "speed-profile"; // 后台优化
case REASON_AB_OTA: return "speed-profile"; // A/B OTA
case REASON_CMDLINE: return "speed"; // 命令行指定
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 09.权限管理机制
# 9.1 权限的分类
Android权限分类:
1. 普通权限(Normal)
└── 安装时自动授予,无需用户确认
└── 例:INTERNET, VIBRATE, SET_WALLPAPER
2. 危险权限(Dangerous)
└── 需要用户运行时授权
└── 按权限组管理
└── 例:CAMERA, LOCATION, READ_CONTACTS
3. 签名权限(Signature)
└── 只有与声明权限的应用签名相同时才能获得
└── 例:MANAGE_ACTIVITY_STACKS
4. 特殊权限(Special)
└── 需要引导用户到设置页面授予
└── 例:SYSTEM_ALERT_WINDOW, WRITE_SETTINGS
权限组(Android 12+变化):
├── LOCATION: ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION
├── CAMERA: CAMERA
├── STORAGE: READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE
├── CONTACTS: READ_CONTACTS, WRITE_CONTACTS
└── ...
注意:Android 12+单独授权,不再按组自动授予
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 9.2 权限检查流程
// PermissionManagerService.java
int checkPermission(String permName, String packageName, int userId) {
// 1.获取包信息
AndroidPackage pkg = mPackageManagerInt.getPackage(packageName);
if (pkg == null) return PERMISSION_DENIED;
// 2.检查UID级别的权限
int uid = UserHandle.getUid(userId, pkg.getUid());
// 3.对于安装时权限
if (isNormalPermission(permName)) {
// 检查Manifest中是否声明了uses-permission
if (pkg.getRequestedPermissions().contains(permName)) {
return PERMISSION_GRANTED;
}
}
// 4.对于运行时权限
if (isRuntimePermission(permName)) {
// 检查是否已授权
PermissionState permState = getPermissionState(permName, packageName, userId);
if (permState != null && permState.isGranted()) {
return PERMISSION_GRANTED;
}
}
// 5.对于签名权限
if (isSignaturePermission(permName)) {
// 检查签名是否匹配
if (isSignatureMatch(declaringPkg, requestingPkg)) {
return PERMISSION_GRANTED;
}
}
return PERMISSION_DENIED;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 9.3 运行时权限的授予
// PermissionManagerService.java
void grantRuntimePermission(String packageName, String permName, int userId) {
// 1.获取包和权限信息
AndroidPackage pkg = mPackageManagerInt.getPackage(packageName);
Permission bp = mRegistry.getPermission(permName);
// 2.验证权限类型
if (!bp.isRuntime() && !bp.isDevelopment()) {
throw new SecurityException("Permission " + permName
+ " is not a runtime permission");
}
// 3.验证包声明了uses-permission
if (!pkg.getRequestedPermissions().contains(permName)) {
throw new SecurityException("Package " + packageName
+ " has not requested permission " + permName);
}
// 4.设置权限状态为已授权
int uid = UserHandle.getUid(userId, pkg.getUid());
UidPermissionState uidState = getUidPermissionState(uid);
uidState.grantPermission(bp);
// 5.持久化权限状态
mPermissionManager.writeRuntimePermissionsForUserLPr(userId, false);
// 6.通知权限变化
int[] changedUids = new int[]{uid};
mOnPermissionChangeListeners.onPermissionsChanged(uid);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 10.Intent解析与组件匹配
# 10.1 Intent解析的核心
当AMS需要启动一个Activity或Service时,如果使用隐式Intent,需要PMS来解析出目标组件:
// PackageManagerService.java
public ResolveInfo resolveIntent(Intent intent, String resolvedType,
int flags, int userId) {
// 1.查询所有匹配的Activity
List<ResolveInfo> query = queryIntentActivitiesInternal(
intent, resolvedType, flags, userId);
// 2.选择最佳匹配
ResolveInfo bestMatch = chooseBestActivity(intent, resolvedType, flags, query);
return bestMatch;
}
2
3
4
5
6
7
8
9
10
11
12
# 10.2 Intent Filter匹配算法
// IntentFilter.java
// Intent Filter匹配需要同时满足action、category、data三个维度
int match(String action, String type, String scheme, Uri data,
Set<String> categories, String callingPackage) {
// 1.Action匹配
if (action != null) {
if (!mActions.contains(action)) {
return NO_MATCH_ACTION; // Action不匹配
}
}
// 2.Data匹配(scheme + authority + path + mimeType)
int dataMatch = matchData(type, scheme, data);
if (dataMatch < 0) {
return NO_MATCH_DATA; // Data不匹配
}
// 3.Category匹配
if (categories != null) {
for (String category : categories) {
if (!mCategories.contains(category)) {
return NO_MATCH_CATEGORY; // Category不匹配
}
}
}
// 全部匹配成功,返回匹配度
return dataMatch + MATCH_CATEGORY_SCHEME;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 10.3 多匹配结果的处理
当多个组件都匹配时,PMS的处理策略:
多匹配结果处理策略:
1. 优先级排序
└── 按IntentFilter的priority排序(-1000 ~ 1000)
└── 系统应用可以设置更高优先级
2. 默认应用选择
└── 如果用户设置了默认应用(如默认浏览器)
└── 直接返回默认应用
3. 弹出选择器
└── 如果没有默认应用且有多个匹配
└── 系统弹出选择对话框让用户选择
└── 用户可以选择"始终"设为默认
4. 验证域名(App Links, Android 12+)
└── 对于HTTP/HTTPS Intent
└── 只有通过了域名验证的应用才能自动处理
└── 未验证的应用需要在选择器中选择
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 11.多用户包管理
# 11.1 多用户模型
Android支持多用户,PMS需要为每个用户维护独立的包状态:
多用户包管理模型:
APK文件(所有用户共享):
/data/app/com.example.app-xxx/base.apk ← 只有一份
用户数据(每个用户独立):
/data/user/0/com.example.app/ ← 用户0的数据
/data/user/10/com.example.app/ ← 用户10的数据
PackageUserState(每个用户独立):
┌─────────────────────────┐
│ 用户0 (Owner) │
│ installed = true │
│ enabled = true │
│ hidden = false │
│ stopped = false │
│ runtimePermissions = {│
│ CAMERA: granted, │
│ LOCATION: denied │
│ } │
├─────────────────────────┤
│ 用户10 (Work Profile) │
│ installed = true │
│ enabled = true │
│ hidden = false │
│ stopped = true │
│ runtimePermissions = {│
│ CAMERA: denied, │
│ LOCATION: granted │
│ } │
└─────────────────────────┘
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 11.2 应用的禁用与隐藏
// PackageManagerService.java
void setApplicationEnabledSetting(String packageName, int newState,
int flags, int userId, String callingPackage) {
// newState可以是:
// COMPONENT_ENABLED_STATE_DEFAULT (0) - 默认
// COMPONENT_ENABLED_STATE_ENABLED (1) - 启用
// COMPONENT_ENABLED_STATE_DISABLED (2) - 禁用
// COMPONENT_ENABLED_STATE_DISABLED_USER (3) - 用户禁用
// COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED (4) - 直到使用前禁用
synchronized (mLock) {
PackageSetting pkgSetting = mSettings.getPackageLPr(packageName);
// 更新用户状态
pkgSetting.setEnabled(newState, userId, callingPackage);
// 持久化
mSettings.writePackageRestrictionsLPr(userId);
}
// 通知组件状态变化
sendPackageChangedBroadcast(packageName, false, components, userId);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 12.应用更新与覆盖安装
# 12.1 覆盖安装的判断
// 判断是否为覆盖安装
void installStage(InstallParams params) {
PackageSetting existingPkg = mSettings.getPackageLPr(packageName);
if (existingPkg != null) {
// 覆盖安装
// 检查条件:
// 1.版本号必须大于等于已安装版本
if (newPkg.getLongVersionCode() < existingPkg.getLongVersionCode()) {
// 默认拒绝降级安装
if ((installFlags & INSTALL_ALLOW_DOWNGRADE) == 0) {
throw new PackageManagerException("version downgrade rejected");
}
}
// 2.签名必须一致
verifySignaturesMatch(existingPkg, newPkg);
// 执行覆盖安装
replacePackageLIF(newPkg, ...);
} else {
// 新安装
installNewPackageLIF(newPkg, ...);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 12.2 覆盖安装的处理
// 覆盖安装流程
void replacePackageLIF(ParsedPackage parsedPackage, int parseFlags,
int scanFlags, UserHandle user, String installerPackageName,
InstallResult res) {
PackageSetting oldPkgSetting = mSettings.getPackageLPr(packageName);
// 1.备份旧包信息
AndroidPackage oldPackage = oldPkgSetting.getPkg();
// 2.处理权限变化
// 如果新版本移除了某些权限声明
for (String perm : oldPackage.getPermissions()) {
if (!newPackage.getPermissions().contains(perm)) {
// 从系统中移除该权限
mPermissionManager.removePermission(perm);
}
}
// 3.替换包
if (isSystemApp(oldPkgSetting)) {
replaceSystemPackageLIF(parsedPackage, ...);
} else {
replaceNonSystemPackageLIF(parsedPackage, ...);
}
// 4.更新包信息
commitReconciledScanResultLocked(reconciledPkg);
// 5.保留用户数据(/data/data/包名/不会被删除)
// 只替换APK和native库
// 6.重新进行dex优化
performDexOptTraced(packageName, "speed-profile");
// 7.发送广播
sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName, ...);
// 也会发送ACTION_MY_PACKAGE_REPLACED给被更新的应用自己
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 12.3 Split APK(分包安装)
Android 5.0+支持Split APK机制:
Split APK的结构:
/data/app/com.example.app-xxx/
├── base.apk // 基础包(必须有)
├── split_config.arm64_v8a.apk // ABI分包
├── split_config.xxhdpi.apk // 密度分包
├── split_config.zh.apk // 语言分包
└── split_feature1.apk // 功能模块分包(Dynamic Feature)
优势:
1. 用户只下载设备需要的资源(App Bundle)
2. 动态功能模块按需下载
3. 减小APK下载大小
2
3
4
5
6
7
8
9
10
11
12
# 13.应用卸载流程
# 13.1 卸载的完整流程
应用卸载流程:
1. 发起卸载请求
└── PackageManager.deletePackage() 或 adb uninstall
2. 权限检查
├── 系统应用不能被普通方式卸载
└── 检查DELETE_PACKAGES权限
3. 冻结应用
└── 停止应用的所有组件
└── 杀死应用进程
4. 清理数据
├── 删除应用数据 /data/data/包名/
├── 删除缓存 /data/cache/
├── 删除外部存储数据
└── 通过installd执行
5. 移除APK
└── 删除 /data/app/包名-xxx/ 目录
6. 更新PMS数据
├── 从mPackages中移除
├── 从ComponentResolver中移除组件
├── 移除权限信息
└── 更新packages.xml
7. 通知
├── 发送ACTION_PACKAGE_REMOVED广播
├── 发送ACTION_PACKAGE_FULLY_REMOVED广播
├── 通知Launcher移除图标
└── 通知其他系统服务
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 13.2 系统应用的特殊处理
// 系统应用卸载更新
void deleteSystemPackageLIF(AndroidPackage deletedPkg,
PackageSetting deletedPs, int flags, InstallResult res) {
// 系统应用不能真正卸载,只能"卸载更新"
// 恢复到出厂版本
// 1.删除/data/app/下的更新版本
removePackageDataLIF(deletedPs, null, null, flags);
// 2.重新启用/system/app/下的出厂版本
File systemApk = new File(deletedPs.codePathString); // /system/app/xxx
// 3.重新扫描出厂版本
scanPackageTracedLI(systemApk, parseFlags, scanFlags, 0);
// 4.更新包信息为出厂版本
// 用户数据保留(因为出厂版本仍然是同一个应用)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 14.PMS的数据持久化
# 14.1 packages.xml文件
PMS将所有包信息持久化到/data/system/packages.xml:
<!-- packages.xml 关键内容 -->
<packages>
<!-- 权限声明 -->
<permissions>
<item name="android.permission.INTERNET" package="android"
protection="0" />
<item name="android.permission.CAMERA" package="android"
protection="1" />
</permissions>
<!-- 已安装包 -->
<package name="com.example.app"
codePath="/data/app/com.example.app-abc123"
nativeLibraryPath="/data/app/com.example.app-abc123/lib/arm64"
primaryCpuAbi="arm64-v8a"
publicFlags="0x84000" privateFlags="0x0"
ft="18c6f3a0000" it="18c6f3a0000" ut="18c6f3a0000"
version="10" userId="10125">
<sigs count="1">
<cert index="0" key="308202..." />
</sigs>
<perms>
<item name="android.permission.INTERNET" granted="true" flags="0" />
<item name="android.permission.CAMERA" granted="true" flags="0x32" />
</perms>
</package>
<!-- 共享UID -->
<shared-user name="android.uid.system" userId="1000">
<sigs count="1">
<cert index="1" />
</sigs>
</shared-user>
</packages>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 14.2 持久化时机
packages.xml写入时机:
├── 系统启动扫描完成后
├── 安装新应用后
├── 更新应用后
├── 卸载应用后
├── 权限变更后
└── 应用启用/禁用状态变更后
写入采用安全写入策略:
1. 先写入packages.xml.tmp
2. 成功后重命名为packages.xml
3. 保留packages-backup.xml作为备份
4. 如果写入失败,从备份恢复
2
3
4
5
6
7
8
9
10
11
12
13
# 14.3 运行时权限持久化
运行时权限单独持久化:
/data/system/users/0/runtime-permissions.xml (用户0)
/data/system/users/10/runtime-permissions.xml (用户10)
每个用户独立的权限状态文件。
2
3
4
5
# 15.PMS的安全机制
# 15.1 安装来源验证
// PMS的安装安全检查
void verifyInstallSource(InstallParams params) {
// 1.检查安装权限
// 普通应用需要REQUEST_INSTALL_PACKAGES权限(Android 8.0+)
// 或者通过"允许安装未知来源"设置
// 2.检查安装来源
// 应用商店(有INSTALL_PACKAGES权限)
// ADB安装(需要USB调试开启)
// 三方应用安装(需要用户授权)
// 3.Package Verifier验证
// Google Play Protect或其他验证器会扫描APK
Intent verification = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
broadcastIntentInPackage("android", verification, ...);
// 等待验证结果
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 15.2 UID隔离
Android的UID隔离机制(PMS负责分配UID):
每个应用安装时PMS分配一个唯一的Linux UID:
├── 系统核心:UID 1000 (system)
├── 系统服务:UID 1001-1999
├── 普通应用:UID 10000-19999 (u0_a0 ~ u0_a9999)
├── 多用户:UID = userId * 100000 + appId
│ ├── 用户0的应用A:UID 10125
│ └── 用户10的应用A:UID 1010125
└── 共享UID:多个应用可以共享同一个UID
└── 需要在Manifest中声明sharedUserId
└── 且签名必须相同
UID隔离的效果:
1. 每个应用运行在独立的Linux进程中
2. 文件权限基于UID,应用无法访问其他应用的文件
3. 网络访问、进程通信等都可以基于UID进行控制
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 15.3 Verified Boot与APK完整性
从系统启动到APK运行的完整性链:
1. Verified Boot
└── 验证boot分区和system分区的完整性
└── 确保系统文件(包括系统APK)未被篡改
2. dm-verity
└── 运行时验证system分区的每个块
└── 防止系统应用被运行时修改
3. APK签名验证
└── PMS在安装时验证APK签名
└── 确保APK来自可信发布者
4. ART验证
└── dex2oat在编译时验证DEX字节码
└── 确保代码合法性
5. SELinux
└── 限制应用进程的系统调用和文件访问
└── 即使获得root也受限
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 16.总结与技术思考
# 16.1 核心要点回顾
PMS核心要点:
1. PMS是包信息的"数据库"
└── 系统启动时扫描所有APK建立索引
└── 提供查询、安装、卸载、权限等服务
2. APK安装是一个复杂的多步骤过程
└── 复制→验证→解析→优化→注册→通知
└── 安全检查贯穿始终
3. 签名是APK安全的基石
└── V1→V2→V3→V4的演进
└── 覆盖安装必须签名一致
4. dex编译策略持续优化
└── 从纯JIT到纯AOT再到混合编译
└── Profile-guided compilation是当前最佳实践
5. 权限管理越来越精细
└── 从安装时权限到运行时权限
└── 从权限组到单独权限
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 16.2 面试高频问题
问题:APK安装时发生了什么?
- 复制APK到/data/app/、签名验证、Manifest解析、dex2oat编译、创建数据目录、更新packages.xml、发送广播
问题:为什么签名不一致不能覆盖安装?
- 防止恶意应用冒充合法应用进行更新
- 签名代表发布者身份,不一致说明不是同一个发布者
问题:Android 7.0的混合编译是怎么工作的?
- 安装时只验证DEX不编译
- 运行时JIT编译热点代码并记录Profile
- 空闲时根据Profile做选择性AOT编译
- 兼顾了安装速度、运行性能和存储空间
# 16.3 调试命令
# 查看已安装包信息
adb shell pm list packages
adb shell pm dump com.example.app
# 查看包路径
adb shell pm path com.example.app
# 查看权限信息
adb shell pm list permissions -d -g
adb shell dumpsys package permissions
# 查看PMS详细信息
adb shell dumpsys package com.example.app
# 查看dex优化状态
adb shell pm art dump com.example.app
# 手动触发dex优化
adb shell cmd package compile -m speed com.example.app
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19