包管理器

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

APK 安装过程

Android 应用安装过程如下:

  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.listpackages.xml 文件。

    • 使用 Intent.ACTION_PACKAGE_ADDEDIntent.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 机制相关(不太清楚,其值似乎有 platformdefault)。

  • 1028,3003,2001 是应用所属的用户组;如果应用不属于任何组,这里的值是 None

packages.xml

packages.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 文件中的主要信息分为以下部分:

  • 权限块包含系统中所有定义的权限信息。

  • 包块包含系统中所有已安装应用的详细信息。

  • 共享用户块包含所有系统定义的共享用户信息。

  • 密钥集设置块包含已安装应用签名的公钥信息。

权限块

<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 等。

包块

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

<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 属性是密钥集中标识符的值。它用于指示应用使用哪个公钥。

共享用户块

<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 的应用具有相同的权限。

密钥集设置块

<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> 标识符的值。

  • lastIssuedKeyIdlastIssuedKeySetId 表示最新公钥被取到的集合编号。

参考资料

最后更新于