# 包管理器

包管理器 (Package Manager) 是实际管理应用安装、移除和更新的 API。

## APK 安装过程

Android 应用安装过程如下：

![](/files/B5rvW7GHfiObfPca6qnc)

1. 当您安装 APK 文件时，包管理器解析 APK 并显示确认信息。当您按下"确定"按钮时，包管理器调用 `PackageInstaller` 进行交互式安装包。
2. PackageInstaller 提供用户界面来管理包。PackageInstaller 调用 `InstallAppProgress` 活动来接收来自用户的指令。
3. InstallAppProgress 请求 `包管理器服务` 安装包。
4. 包管理器服务使用 `installd` 守护进程安装包，该进程通过 `/dev/socket/installd` 套接字接收请求，并以 root 权限执行一系列步骤来安装 APK。整个安装过程如下：
   * 将包添加到安装队列。
   * 确定包安装的适当位置（系统应用为 `/system/app/`，第三方应用为 `/data/app/`）。
   * 将 APK 复制到给定目录。
   * 确定应用的 UID。
   * 请求 `installd` 守护进程。
   * 在 `/data/data/` 中创建应用目录并设置权限。
   * 将 DEX 代码提取到缓存目录 `/data/dalvik-cache/`。
   * 更新 `packages.list` 和 `packages.xml` 文件。
   * 使用 `Intent.ACTION_PACKAGE_ADDED` 或 `Intent.ACTION_PACKAGE_REPLACED` 向系统广播
5. 应用安装成功。

## 数据存储

包管理器将应用信息存储在位于 `/data/system` 的三个文件中：

* `packages.xml` 包含权限和包列表：
* `packages.list` 是一个简单的文本文件，包含包名、用户 ID、标志和数据目录。
* `packages-stoped.xml` 包含具有停止状态的包列表（停止状态的应用无法接收任何广播）。

### packages.list

`packages.list` 文件为系统上安装的所有应用包含类似这样的内容：

```
com.android.packageinstaller 10025 0 /data/data/com.android.packageinstaller platform 1028,3003,2001
```

该行分为 6 列，包含关于应用的信息：

* `com.android.packageinstaller` 是应用的包名，它是 `AndroidManifest.xml` 中 `<manifest>` 元素的 `package` 属性的内容。
* `10025` 是应用使用的 `userId`；如果在 `AndroidManifext.xml` 中未指定 `android:sharedUserId` 属性，系统将自动为应用分配一个 `userId`。
* `0` 表示应用是否处于调试模式，由 `AndroidManifext.xml` 中的 `android:debuggable` 指定。
* `/data/data/com.android.packageinstaller` 是应用的数据存储路径，通常是一个像 `/data/data/<package_name>` 的文件夹。
* `platform` 是应用的 `seinfo` 信息；这应该与 SEAndroid 机制相关（不太清楚，其值似乎有 `platform` 和 `default`）。
* `1028,3003,2001` 是应用所属的用户组；如果应用不属于任何组，这里的值是 `None`。

### packages.xml

`packages.xml` 文件具有以下结构：

```xml
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<packages>
    <version ... />
    <version ... />
 
    <permissions>
        <item name="xxxx" package="xxx" protection="xx" />
        ... ...
    </permissions>
 
    <package xxx>
        ...
    </package>
    ...
 
    <shared-user xxx>
        ...
    </shared-user>
    ...
 
    <keyset-settings version="1">
        ...
    </keyset-settings>
</packages>
```

`packages.xml` 文件中的主要信息分为以下部分：

* **权限块**包含系统中所有定义的权限信息。
* **包块**包含系统中所有已安装应用的详细信息。
* **共享用户块**包含所有系统定义的共享用户信息。
* **密钥集设置块**包含已安装应用签名的公钥信息。

#### 权限块

```xml
<permissions>
    <item name="android.permission.REAL_GET_TASKS" package="android" protection="18" />
    <item name="android.permission.REMOTE_AUDIO_PLAYBACK" package="android" protection="2" />
    ...
</permissions>
```

它定义了系统中所有声明的权限信息，每个项目块代表一个权限：

* **Name** 表示权限的名称。
* **Package** 表示声明权限的包
* **Protection** 表示权限的级别，如 normal、dangerous 等。

#### 包块

包块包含每个应用的详细信息。

```xml
<package name="com.tencent.qqmusictv" codePath="/data/app/qqmusictv" nativeLibraryPath="/data/app/qqmusictv/lib" primaryCpuAbi="armeabi" publicFlags="941112933" privateFlags="0" ft="15f00a383c8" it="15f00a383c8" ut="15f00a383c8" version="134" userId="10044">
    <sigs count="1">
        <cert index="6" key="30820247308201b0a003020..." />
    </sigs>
    <perms>
        <item name="android.permission.WRITE_SETTINGS" granted="true" flags="0" />
        ...
    </perms>
    <proper-signing-keyset identifier="7" />
</package>
```

`<package>` 元素的属性：

* **name** 表示包名。
* **codePath** 表示此 APK 文件存储的位置。
* **nativeLibraryPath** 表示应用使用的 `xxx.so` 库的位置。
* **primaryCpuAbi** 表示应用运行在哪个 abi 架构上。
* **publicFlags 和 privateFlags** 是根据 `AndroidManifest.xml` 中的设置生成的，例如：`android:multiarch`。
* **ft** 表示 APK 文件最后更改的时间。
* **it** 表示应用首次安装的时间。
* **ut** 表示应用最后更新的时间。
* **version** 是关于应用版本号的信息，是 `AndroidManifest.xml` 中配置的 `android:versionCode`。
* **userId** 是分配给应用的用户 ID；如果有 `shareUserId`，则 `SharedUserId` 出现在这里。

`<sigs>` 元素的属性：

* **count** 表示应用有多少个签名（某些应用可能由多个证书签名）。

`<cert>` 元素的属性：

* **index** 表示应用使用的证书的序列号；当系统发现新证书时，数字将增加 1。
* **key** 是应用使用的证书内容的 ASCII 码值；如果包管理器服务在扫描 APK 时检测到使用了先前已知的证书，则 `key` 值将不存在；具有相同索引的包，表示它们使用相同的签名。

`<perms>` 块是应用拥有的权限。对于普通应用，这些权限写在 `AndroidManifest.xml` 中。对于那些使用相同 `userId` 的应用，这里的权限是所有使用相同 `userId` 的应用的权限总和。`granted` 表示是否已允许此权限。

`<proper-signing-keyset>` 的 `identifier` 属性是[密钥集](#keyset-settings-block)中标识符的值。它用于指示应用使用哪个公钥。

#### 共享用户块

```xml
<shared-user name="android.uid.system" userId="1000">
    <sigs count="1">
        <cert index="1" />
    </sigs>
    <perms>
        <item name="android.permission.REAL_GET_TASKS" granted="true" flags="0" />
        <item name="android.permission.ACCESS_CACHE_FILESYSTEM" granted="true" flags="0" />
        ... ...
        <item name="android.permission.DELETE_PACKAGES" granted="true" flags="0" />
    </perms>
</shared-user>
```

`<shared-user>` 元素的属性：

* **name** 表示此共享用户的名称。
* **userId** 表示系统中用户的编号。

`<sigs>` 元素的含义在这里和包块中相同。

`<perms>` 表示此用户拥有的权限。在启动时扫描 APK 文件时，所有使用相同 `userId` 的应用的权限被收集并放置在这里。最后，这些权限将被发送到那些使用相同 `userId` 的应用。最终结果是系统中具有相同 `userId` 的应用具有相同的权限。

#### 密钥集设置块

```xml
<keyset-settings version="1">
    <keys>
        <public-key identifier="1" value="MIIBIjANBgkqhki..." />
        ...
    </keys>
    <keysets>
        <keyset identifier="1">
            <key-id identifier="1" />
        </keyset>
        ...
    </keysets>
    <lastIssuedKeyId value="9" />
    <lastIssuedKeySetId value="9" />
</keyset-settings>
```

* `<keyset-settings>` 块收集所有应用签名的公钥信息，并与包块中的信息相关联。
* `<keys>` 块中 `<public-key>` 的 `value` 属性的值是从 APK 包中的签名文件中提取的公钥。
* `<keysets>` 块包含许多密钥集。每个密钥集都有一个带标识符的数字。`<key-id>` 的 `identifier` 属性对应于上面 `<keys>` 中 `<public-key>` 标识符的值。
* `lastIssuedKeyId` 和 `lastIssuedKeySetId` 表示最新公钥被取到的集合编号。

## 参考资料

* [深入：Android 包管理器和包安装程序](https://dzone.com/articles/depth-android-package-manager)
* [Android 开发系统包文件解析](https://www.programmersought.com/article/3817615344/)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://gitbook.cdxiaodong.life/android-ying-yong-an-quan/gai-shu/package-manager.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
