编程进阶网 编程进阶网
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • README
  • Android提升进阶

    • 库的解读

    • 专栏博客

      • 系统启动Zygote
      • Binder通信原理
      • Handler消息机制
      • Activity启动原理
      • 四大组件原理分析
      • AMS与组件管理
      • View绑制与渲染
      • 事件分发机制
      • Surface渲染原理
      • 自定义View设计
      • WMS窗口管理
      • PMS与APK安装
        • 虚拟机与类加载
        • 内存管理与GC
        • 线程与并发编程
        • 性能优化与监控
        • 序列化与数据存储
        • 组件化与路由设计
        • 插件化与热修复
        • NDK开发实践
        • WebView核心设计
        • ADB常见使用操作
      • 智能硬件

    • iOS开发和进阶

    • Web开发和进阶

    • Linux应用开发

    • Apps
    • Android提升进阶
    • 专栏博客
    杨充
    2026-04-14
    目录

    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、权限状态
    
    1
    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      │     │
    │  │  └── 权限授予/检查/撤销           │     │
    │  └─────────────────────────────────┘     │
    └──────────────────────────────────────────┘
    
    1
    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();
    }
    
    1
    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. 安全隔离
    
    1
    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编译
    
    1
    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");
    }
    
    1
    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);
            });
        }
    }
    
    1
    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元数据
    
    1
    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的优势:减少冗余、减小文件体积、加快加载速度
    
    1
    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 (资源序号)
    
    1
    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树签名
      └── 允许流式安装,边下载边安装
    
    1
    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结束记录
    └─────────────────────────┘
    
    1
    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");
        }
    }
    
    1
    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
    
    1
    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更新图标
               └── 回调安装结果
    
    1
    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);
    }
    
    1
    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;
    }
    
    1
    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;
            }
        }
    }
    
    1
    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;
            }
        }
    }
    
    1
    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编译
      └── 首次运行即有接近完全编译的性能
    
    1
    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    (预初始化数据)
    
    1
    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";         // 命令行指定
        }
    }
    
    1
    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+单独授权,不再按组自动授予
    
    1
    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;
    }
    
    1
    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);
    }
    
    1
    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;
    }
    
    1
    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;
    }
    
    1
    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
       └── 只有通过了域名验证的应用才能自动处理
       └── 未验证的应用需要在选择器中选择
    
    1
    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    │
    │   }                      │
    └─────────────────────────┘
    
    1
    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);
    }
    
    1
    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, ...);
        }
    }
    
    1
    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给被更新的应用自己
    }
    
    1
    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下载大小
    
    1
    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移除图标
       └── 通知其他系统服务
    
    1
    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.更新包信息为出厂版本
        // 用户数据保留(因为出厂版本仍然是同一个应用)
    }
    
    1
    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>
    
    1
    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. 如果写入失败,从备份恢复
    
    1
    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)
    
    每个用户独立的权限状态文件。
    
    1
    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, ...);
        // 等待验证结果
    }
    
    1
    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进行控制
    
    1
    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也受限
    
    1
    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. 权限管理越来越精细
       └── 从安装时权限到运行时权限
       └── 从权限组到单独权限
    
    1
    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
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    上次更新: 2026/06/10, 11:13:41
    WMS窗口管理
    虚拟机与类加载

    ← WMS窗口管理 虚拟机与类加载→

    最近更新
    01
    信号崩溃快速排查
    06-15
    02
    CoreDump破案
    06-15
    03
    perf火焰图实战
    06-15
    更多文章>
    Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式