深入解析 Android PMS —— APK 安装与解析全流程

文章目录

  • 前言
  • [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. 安装请求的触发

入口:

  1. 命令行触发:通过 ADB 命令安装 APK
  2. 系统触发:例如应用商店或系统应用发起安装请求。

核心代码路径:

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 安装过程中,系统需要将应用的相关信息持久化存储,以确保在系统重启后仍然能访问到应用信息,并且保证应用的权限和其他元数据不会丢失。

  1. 安装包信息存储

    在应用安装时,PackageManagerService 会将应用的信息(如包名、版本、权限 等)写入系统的持久化存储中。这个过程通常通过 writePackagesLocked() 方法完成。

  2. 系统数据更新

    系统维护一个应用数据库,存储有关已安装应用的所有信息(包括应用包名、路径、签名、权限等)。当应用安装或卸载时,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 校验

  1. 签名校验:

    ApkFile.getSignatures() 提取签名。

    checkSignatures() 方法校验签名是否有效。

  2. 格式校验:

    通过 ApkFile.contains() 检查 APK 文件结构是否完整。

    使用 validateFileStructure() 校验 AndroidManifest.xml 和 classes.dex 文件是否存在。

  3. 权限校验:

    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 系统中包管理服务的运作流程。

相关推荐
Hi-Dison44 分钟前
OpenHarmony系统中实现Android虚拟化、模拟器相关的功能,包括桌面显示,详细解决方案
android
事业运财运爆棚2 小时前
http 502 和 504 的区别
android
峥嵘life4 小时前
Android Studio新版本的一个资源id无法找到的bug解决
android·bug·android studio
编程乐学4 小时前
网络资源模板--Android Studio 实现绿豆通讯录
android·前端·毕业设计·android studio·大作业·安卓课设·绿豆通讯录
朴拙数科7 小时前
mysql报错解决 `1525 - Incorrect DATETIME value: ‘0000-00-00 00:00:00‘`
android·数据库·mysql
1登峰造极9 小时前
scroll-view 实现滑动显示,确保超出正常显示,小程序app,h5兼容
android·小程序
刘争Stanley9 小时前
Android 15(V)新功能适配,雕琢移动细节之美
android·kotlin·android 15·android v
小狗爱世界9 小时前
深入解析Binder源码
android·binder
qq_214670359 小时前
android 聊天界面键盘、表情切换丝滑
android·gitee
韩家老大9 小时前
MTK Android12 隐藏顶部状态栏
android