如何应对Android面试官 -> PKMS 权限管理

静默安装与权限管理:PKMS中的签名、权限策略与AspectJ实现

前言


image.png

本章我们继续上一章节,讲解 PKMS 相关知识点;

静默安装


静默安装说的就是:在用户无感知的情况下,给用户的手机安装了某个 app,或者是用户触发安装之后,不需要额外的任何操作即可以安装目标 app 到手机上的行为;

手机内置的应用市场 app,其实现了静默安装的能力,三方 app 如果想实现这个能力是不可能的。因为需要系统的签名,这个签名厂商是不会开放的,所以我们无法实现想要的静默安装能力;

签名

那么,这个签名是什么呢?就是在 PKMS 的构造方法中,添加到系统 Settings 的 UID

mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);

mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);

mSettings.addSharedUserLPw("android.uid.log", LOG_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);

mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);

mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);

mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);

mSettings.addSharedUserLPw("android.uid.se", SE_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);

mSettings.addSharedUserLPw("android.uid.networkstack", NETWORKSTACK_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);

这里额外插入一个小的知识点,如果做系统应用开发,如何让系统应用在系统启动的时候被装载进来,需要使用 adb push 相关命令;

1. adb remount
2. adb shell
3. chmod777 system/app
4. exit
5. adb push xxx/yy.apk system/app
6. adb reboot

这里 xxx/yy.apk 是 apk 的绝对路径,system/app 是系统目录;这些操作的本质就是:拷贝 + 扫描,将你定制的系统应用拷贝到 system/app 中然后重启系统执行扫描操作;

厂商静默安装实现

如果想实现系统的『静默安装』能力,我们需要准备哪些能力呢?首先我们需要三个系统的 aidl 文件;

android.content.pm.IPackageDeleteObserver.aidl
android.content.pm.IPackageInstallObserver.aidl
android.content.pm.IPackageMoveObserver.aidl

/**
 * API for deletion callbacks from the Package Manager.
 *
 * {@hide}
 */
oneway interface IPackageDeleteObserver {
    void packageDeleted(in String packageName, in int returnCode);
}
/**
 * API for installation callbacks from the Package Manager.
 * @hide
 */
oneway interface IPackageInstallObserver {
    void packageInstalled(in String packageName, int returnCode
<think>我们正在讨论Android的PackageManagerService(PKMS)处理APK安装的流程。根据用户提供的引用和之前的问题,我们需要详细解释APK安装过程。注意,用户要求参考站内引用,因此我们在回答中需要适当引用这些内容。 安装过程主要分为几个阶段:准备阶段、扫描阶段、安装阶段和通知阶段。下面将详细说明。 ### 1. **安装触发阶段** 用户通过以下方式触发安装: - 系统设置中安装 - `adb install`命令 - 应用商店下载安装 PKMS首先检查调用者权限(需要`INSTALL_PACKAGES`权限)[^1]。如果是系统应用,则可能具有更高权限。 ### 2. **准备阶段** PKMS会进行以下准备工作: - **验证APK文件**:检查签名、包名、版本号等。 - **检查存储空间**:确保有足够的空间安装应用。 - **确定安装位置**:内部存储(`/data/app`)或外部存储(SD卡)[^4]。 在此阶段,PKMS会冻结当前包(如果存在更新安装),以防止在安装过程中被修改: ```java final PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags, "installPackageLI"); ``` [^4] ### 3. **扫描阶段** PKMS扫描APK文件,解析`AndroidManifest.xml`,提取应用信息(如包名、版本、权限、组件等)[^2]。扫描分为两步: - **系统应用扫描**:开机时扫描`/system`目录下的预装应用(如`/system/priv-app`、`/system/app`)[^2]。 - **用户应用扫描**:扫描`/data/app`目录下的用户安装的应用。 扫描过程中,PKMS会更新`packages.xml`文件,该文件存储所有应用的权限和包信息[^2]。 ### 4. **安装阶段** 根据扫描结果,PKMS决定是全新安装还是更新安装: - **全新安装**: 1. 创建应用数据目录:`/data/data/<package_name>` 2. 提取DEX文件到`/data/dalvik-cache`(如果是ART虚拟机,则编译为OAT文件) 3. 在`packages.xml`中新增记录 - **更新安装**: 1. 保留原有数据目录(但会备份旧版本) 2. 替换APK文件 3. 更新`packages.xml`中的版本信息 在安装阶段,PKMS还会处理应用依赖关系(如共享库)和权限授予[^1]。 ### 5. **优化阶段** 安装完成后,系统会对应用进行优化: - **DEX优化**:将DEX文件转换为OAT文件(ART虚拟机使用),提高执行效率。 - **资源优化**:生成编译后的资源文件(如`resources.arsc`)。 ### 6. **通知阶段** 安装完成后,PKMS发送广播通知系统: ```java Intent intent = new Intent(Intent.ACTION_PACKAGE_ADDED); intent.putExtra(Intent.EXTRA_UID, packageUid); sendBroadcast(intent); ``` 同时,更新启动器图标和应用列表。 ### 安装流程图 ```mermaid graph TD A[安装触发] --> B[权限检查] B --> C[准备阶段:验证APK] C --> D[扫描阶段:解析Manifest] D --> E{是否已安装?} E -->|是| F[更新安装] E -->|否| G[全新安装] F --> H[替换APK并保留数据] G --> I[创建数据目录] H --> J[优化阶段] I --> J J --> K[更新packages.xml] K --> L[发送安装完成广播] ``` ### 常见问题及解决 **问题1:INSTALL_FAILED_INSUFFICIENT_STORAGE** **原因**:存储空间不足 **解决**:清理存储空间后重试 **问题2:INSTALL_FAILED_UPDATE_INCOMPATIBLE** **原因**:新版本签名与旧版本不一致 **解决**:先卸载旧版本再安装 **问题3:INSTALL_FAILED_DUPLICATE_PERMISSION** **原因**:应用尝试定义已存在的权限 **解决**:修改自定义权限名称 ### 调试建议 - 查看安装日志:`adb logcat | grep "PackageManager"` - 检查包信息:`adb shell dumpsys package <package_name>` - 验证`packages.xml`:`adb shell cat /data/system/packages.xml` > 注意:Android 11+的`packages.xml`路径可能为`/data/system/packages.xml`,需要root权限访问[^2]。 §§相关问题§§ 1. 如何通过ADB命令安装系统应用? 2. PKMS在安装过程中如何处理权限冲突? 3. Android沙盒机制如何保证APK安装的安全性? 4. 应用更新安装时数据迁移的原理是什么? [^1]: PackageManagerService源码分析 [^2]: Android系统启动流程中的PKMS初始化 [^3]: PackageManager与PKMS的Binder通信机制 [^4]: PKMS安装过程中的冻结操作</think>### Android PackageManagerService (PKMS) APK安装流程详解 Android系统中APK安装的核心由**PackageManagerService (PKMS)** 处理,其流程分为四个关键阶段: --- #### 1. **安装触发阶段** - **入口方式**: - 用户操作(应用商店安装) - `adb install`命令 - 系统启动时扫描预装应用 - **权限校验**: - 调用者需具备`INSTALL_PACKAGES`权限 - 验证APK签名和证书链[^1] - **预处理**: - 冻结当前包(防止并发修改): ```java final PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags, "installPackageLI"); ``` [^4] --- #### 2. **扫描解析阶段 - **目录扫描**: - 系统应用:扫描`/system/priv-app`、`/system/app` - 用户应用:扫描`/data/app` - 开机时触发全量扫描(影响启动时间)[^2] - **解析Manifest**: - 提取包名、版本号、权限声明 - 解析四大组件(Activity/Service等) - **数据更新**: - 更新`/data/system/packages.xml`(存储所有应用权限和包信息)[^2] - 记录优化后的DEX路径 --- #### 3. **安装决策阶段 - **安装类型判断**: ```java if (replace) { // 替换安装 verifySignature(旧包, 新包); // 签名一致性检查 } else { // 全新安装 if (mSettings.getRenamedPackageLPr(pkgName) != null) { throw new PackageManagerException("重复包名"); } } ``` [^4] - **依赖检查**: - 验证共享库依赖(如`androidx.core`) - 检查minSdkVersion兼容性 - **存储分配**: - 内部存储:`/data/app/<包名>-RANDOM` - 外部存储:`/mnt/expand/<UUID>/app/<包名>` --- #### 4. **提交安装阶段 - **文件部署**: - APK复制到目标目录(如`/data/app/com.example.app-1/base.apk`) - 创建私有数据目录:`/data/data/<包名>` - **DEX优化**: - 生成ODEX/VDEX文件(ART虚拟机) - 存储路径:`/data/dalvik-cache/arm64/<base.apk@classes.dex>` - **广播通知**: - 发送安装完成广播: ```java Intent intent = new Intent(Intent.ACTION_PACKAGE_ADDED); intent.putExtra(Intent.EXTRA_UID, packageUid); sendBroadcast(intent); ``` - **权限处理**: - 自动授予`PROTECTION_NORMAL`权限 - 记录运行时权限状态到`packages.xml` --- ### 关键数据结构 | 文件路径 | 作用 | |----------|------| | `/data/system/packages.xml` | 存储所有包的权限、组件、签名信息[^2] | | `/data/system/packages.list` | 记录包名与UID映射关系 | | `/data/app/<包名>/base.apk` | APK主文件存储位置 | | `/data/data/<包名>` | 应用私有数据沙盒 | --- ### 安装流程图解 ```mermaid graph TD A[安装触发] --> B{权限校验} B -->|通过| C[APK扫描解析] C --> D{安装类型} D -->|新安装| E[创建数据目录] D -->|更新| F[签名验证] E --> G[DEX优化] F --> G G --> H[更新packages.xml] H --> I[发送广播] I --> J[启动器更新] ``` --- ### 常见问题解决 **安装失败:INSTALL_FAILED_UPDATE_INCOMPATIBLE** **原因**:新APK签名与旧版本不一致 **解决**: ```bash adb uninstall <包名> # 先卸载旧版 adb install new.apk ``` **安装卡顿** **原因**:DEX优化耗时(尤其Android 7+的混合编译) **调试**: ```bash adb logcat | grep "dex2oat" # 查看优化进度 ``` **权限未授予** **原因**:`packages.xml`未更新 **验证**: ```bash adb shell cat /data/system/packages.xml | grep <包名> ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值