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 都要过一遍。我画了个流程图,帮你理清逻辑:

LightPMS 权限检查流程 收到权限检查请求 步骤1:查找应用注册信息 appRegistry[packageName] 是否存在? 应用未安装 → 返回 false 步骤2:查找权限定义 permissionRegistry[perm] 是否存在? 权限未注册 → 返回 false 步骤3:检查已授予权限集 grantedPermissions.contains(perm)? 权限未授予 → 返回 false 权限已授予 → 返回 true

这个流程看着简单,但真实 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