29. 实战:自定义 PackageManager 模拟器:实现一个轻量级 PMS 核心逻辑
讲到这里,我们已经把 PMS 的源码翻了个底朝天。但说实话,光看代码不动手,总觉得隔着一层纱。
今天我们就来干点实在的——自己写一个轻量级的 PMS 模拟器。别怕,不是让你去复刻 AOSP 那几万行代码。我们只抓核心逻辑:应用安装、卸载、查询、权限管理。
我当年刚啃完 PMS 源码时,也试过自己撸一个简化版。写完之后,很多之前模糊的概念一下子就通了。你想想看,自己亲手搭一遍骨架,比看十遍源码都管用。
29.1 设计思路:我们要模拟什么?
真正的 PMS 太庞大了。我们得做减法。我建议只保留这几个核心能力:
- 应用注册:模拟安装,把 APK 信息存起来
- 应用查询:按包名、按 Intent 找应用
- 应用卸载:从注册表中移除
- 权限声明与检查:应用声明权限,系统检查权限
说白了,就是一个内存里的应用信息数据库,加上一点权限校验逻辑。
核心原则:我们只模拟逻辑层,不碰底层文件解析、不碰 installd、不碰 zygote。聚焦在 PMS 的「大脑」部分。
29.2 数据结构设计
先定义几个核心类。嗯,这里我踩过坑——一开始我把所有信息塞进一个大 HashMap,后来发现查询效率惨不忍睹。
// 应用信息
data class AppInfo(
val packageName: String,
val versionCode: Int,
val versionName: String,
val label: String,
val icon: String?, // 模拟图标路径
val activities: List<ActivityInfo>,
val services: List<ServiceInfo>,
val receivers: List<ReceiverInfo>,
val requestedPermissions: List<String>,
val grantedPermissions: MutableSet<String> = mutableSetOf()
)
// Activity 信息
data class ActivityInfo(
val name: String,
val action: String?,
val category: String?,
val exported: Boolean = false
)
// 权限定义
data class PermissionDef(
val name: String,
val protectionLevel: Int, // 0: normal, 1: dangerous, 2: signature
val description: String
)
为什么要把权限单独拎出来?因为权限管理是 PMS 最头疼的部分之一。我在项目中遇到过好几次权限混乱导致的诡异 bug,后来发现是权限定义和检查逻辑没对齐。
29.3 核心引擎:LightPMS
这就是我们的主角了。一个单例,管理所有应用和权限。
class LightPMS private constructor() {
// 应用注册表:包名 -> AppInfo
private val appRegistry = ConcurrentHashMap<String, AppInfo>()
// 权限定义表:权限名 -> PermissionDef
private val permissionRegistry = ConcurrentHashMap<String, PermissionDef>()
// Intent 过滤器索引:action -> 匹配的 Activity 列表
private val intentIndex = ConcurrentHashMap<String, MutableList<Pair<String, ActivityInfo>>>()
companion object {
@Volatile
private var instance: LightPMS? = null
fun getInstance(): LightPMS {
return instance ?: synchronized(this) {
instance ?: LightPMS().also { instance = it }
}
}
}
// ---------- 应用安装 ----------
fun installApp(app: AppInfo): Boolean {
if (appRegistry.containsKey(app.packageName)) {
// 模拟覆盖安装
uninstallApp(app.packageName)
}
appRegistry[app.packageName] = app
// 建立 Intent 索引
for (activity in app.activities) {
if (activity.action != null) {
intentIndex.getOrPut(activity.action) { mutableListOf() }
.add(Pair(app.packageName, activity))
}
}
// 自动授予 normal 级别权限
for (perm in app.requestedPermissions) {
val def = permissionRegistry[perm]
if (def != null && def.protectionLevel == 0) {
app.grantedPermissions.add(perm)
}
}
return true
}
// ---------- 应用卸载 ----------
fun uninstallApp(packageName: String): Boolean {
val app = appRegistry.remove(packageName) ?: return false
// 清理 Intent 索引
for (activity in app.activities) {
if (activity.action != null) {
intentIndex[activity.action]?.removeAll { it.first == packageName }
}
}
return true
}
// ---------- 应用查询 ----------
fun getAppInfo(packageName: String): AppInfo? = appRegistry[packageName]
fun queryActivitiesByAction(action: String): List<Pair<String, ActivityInfo>> {
return intentIndex[action]?.filter { it.second.exported } ?: emptyList()
}
fun getAllApps(): List<AppInfo> = appRegistry.values.toList()
// ---------- 权限管理 ----------
fun registerPermission(def: PermissionDef) {
permissionRegistry[def.name] = def
}
fun checkPermission(packageName: String, permission: String): Boolean {
val app = appRegistry[packageName] ?: return false
return app.grantedPermissions.contains(permission)
}
fun grantPermission(packageName: String, permission: String): Boolean {
val app = appRegistry[packageName] ?: return false
val def = permissionRegistry[permission] ?: return false
// dangerous 权限需要用户确认,这里简化处理
if (def.protectionLevel == 1) {
app.grantedPermissions.add(permission)
return true
}
return false
}
fun revokePermission(packageName: String, permission: String): Boolean {
val app = appRegistry[packageName] ?: return false
return app.grantedPermissions.remove(permission)
}
}
设计要点:
- 用 ConcurrentHashMap 保证线程安全——真实 PMS 可是要应对多线程调用的
- Intent 索引单独维护,避免每次查询都遍历所有应用
- 权限自动授予只针对 normal 级别,dangerous 需要显式调用 grantPermission
29.4 权限检查流程
权限检查是 PMS 最频繁的操作之一。每次 startActivity、bindService、sendBroadcast 都要过一遍。我画了个流程图,帮你理清逻辑:
这个流程看着简单,但真实 PMS 里还有 uid 匹配、签名校验、权限组管理等复杂逻辑。我们这里只抓主干。
29.5 实战:模拟一个完整场景
光有引擎不行,得跑起来看看效果。我们来模拟一个完整的应用安装→权限检查→卸载流程。
fun main() {
val pms = LightPMS.getInstance()
// 1. 注册系统权限
pms.registerPermission(PermissionDef(
name = "android.permission.CAMERA",
protectionLevel = 1, // dangerous
description = "访问相机"
))
pms.registerPermission(PermissionDef(
name = "android.permission.INTERNET",
protectionLevel = 0, // normal
description = "访问网络"
))
// 2. 创建一个应用
val cameraApp = AppInfo(
packageName = "com.example.camera",
versionCode = 1,
versionName = "1.0.0",
label = "我的相机",
icon = null,
activities = listOf(
ActivityInfo(
name = ".MainActivity",
action = "android.intent.action.MAIN",
category = "android.intent.category.LAUNCHER",
exported = true
),
ActivityInfo(
name = ".CaptureActivity",
action = "com.example.action.CAPTURE",
exported = true
)
),
services = emptyList(),
receivers = emptyList(),
requestedPermissions = listOf(
"android.permission.CAMERA",
"android.permission.INTERNET"
)
)
// 3. 安装应用
pms.installApp(cameraApp)
println("安装完成: ${cameraApp.packageName}")
// 4. 检查权限状态
println("INTERNET 权限: ${pms.checkPermission("com.example.camera", "android.permission.INTERNET")}")
println("CAMERA 权限: ${pms.checkPermission("com.example.camera", "android.permission.CAMERA")}")
// 5. 手动授予危险权限
pms.grantPermission("com.example.camera", "android.permission.CAMERA")
println("授予 CAMERA 权限后: ${pms.checkPermission("com.example.camera", "android.permission.CAMERA")}")
// 6. 查询 Intent
val activities = pms.queryActivitiesByAction("com.example.action.CAPTURE")
println("匹配 CAPTURE 的 Activity: ${activities.map { it.second.name }}")
// 7. 卸载应用
pms.uninstallApp("com.example.camera")
println("卸载后查询: ${pms.getAppInfo("com.example.camera")}")
}
输出结果:
安装完成: com.example.camera
INTERNET 权限: true
CAMERA 权限: false
授予 CAMERA 权限后: true
匹配 CAPTURE 的 Activity: [.CaptureActivity]
卸载后查询: null
注意:INTERNET 权限自动授予了,因为它是 normal 级别。CAMERA 是 dangerous 级别,需要用户手动确认。这个设计跟 Android 真实行为一致——normal 权限安装时自动授予,dangerous 权限运行时弹窗询问。
29.6 避坑指南
写这个模拟器的时候,我踩了几个坑,分享给你:
- 并发问题:一开始我用 HashMap,结果多线程测试时数据全乱了。后来换成 ConcurrentHashMap 才稳住。
- Intent 索引更新:卸载应用时忘了清理索引,导致已卸载的应用还能被查询到。这个 bug 我查了一下午。
- 权限继承:真实 PMS 中,如果应用 A 调用应用 B 的组件,权限检查是基于 A 的 uid 还是 B 的?答案是 B。这个细节很容易搞错。
扩展思考:如果你想继续深入,可以尝试加入这些功能:
- 签名校验:只有相同签名的应用才能互相调用 signature 级别的权限
- 权限组管理:同组权限一次授权全部生效
- 运行时权限回调:模拟弹窗和用户选择
- 多用户支持:每个用户有自己的应用列表和权限状态
好了,这个轻量级 PMS 的核心逻辑就这些。别看代码不多,但骨架已经搭起来了。你完全可以基于这个框架,往里面填充更多真实 PMS 的特性。
动手试试吧,自己跑一遍代码,比看十遍文章都管用。
公众号:蓝海资料掘金营,微信deep3321