PKMS 的分析从两个角度来看, 一个是Client进程另一个是Server进程:
在IPackageManager.aidl文件中, 申明了所有Client和Server之间有业务交流的接口, 在经过编译后会生成一个面向Server端的Stub 类,Stub 所实现的onTransact()函数就是用于响应来自Client端的远程调用; 同时还会生成一个Stub.Proxy 类是面向Client端的.
其中Stub.Proxy会实现所有的在AIDL文件中定义的接口, 但是实际的实现逻辑却不在Proxy内, 它实际的工作只是序列化了客户端传过来的参数, 然后通过mRemoteBinder对象的transact()函数把序列化后的参数发送出去;
通过Binder驱动, 把参数回调到了Stub的onTransact()函数, 进而真正的调用到在Server端实现的逻辑函数, 此时完成了从Client–>Server的函数调用. 上述的流程总结就是client通过获得一个server的代理接口,通过代理对server进行接口调用。
那么Server端拿到了结果, 如果把结果返回给Client的呢? 也就是Server–>Client的流程.
在client端调用代理接口的时候,不仅传入的是参数列表的序列化对象, 还传入了一个_result用于存储调用结果的序列化坑位对象, 而调用transact()是一个同步的过程, 只要阻塞结束, client就可以从_result中取出调用结果了.
所以从Client调用一次远程binder通信, 流程可以理解为如下:
Client需要持有一个AIDL文件生成的Proxy对象, 持有的方式一般是通过绑定远程Service获得的, 具体如下:
bindService()传入的connection对象在建立起连接后会返回一个IBinder对象, 可以通过调用IInterface.Stub.asInterface(Ibinder)函数把Ibinder对象转化成一个Stub.Proxy对象. 接着客户端就可以利用这个proxy对象调用远程接口. 客户端在通过proxy.callRemoteFunc(params)的时候,
在SystemServer的run()内, 会对一些服务执行初始化操作, 其中包括了PKMS, 直接调用PKMS的入口函数main(), 调用自己的构造函数并添加到系统服务中去, 接着SystemServer会调用PKMS的优化dex包的函数performBootDexOpt() 目的是为了提升运行的效率; 最后SystemServer会调用PKMS的systemReady()函数, 告知系统进入就绪状态。
PKMS的构造函数中执行一些比较重的work, 诸如启动了一个扫描指定文件夹的操作:
|
|
接着就是获取permissions的操作, 简述流程就是: 遍历…/etc/permissions文件夹, 解析此文件下的所有XML文件, 根据各个权限的标签(gid, library(对应的是一个个的jar文件路径), feature)进行了归类, 最终存储于map结构中.
|
|
接着就是扫描各个package的内容了, 优先处理的是系统级别的dex包了, 首先遍历上述的mSharedLibrarires结构, 它存储了系统级别的库文件. 通过调用mInstaller.dexopt(lib, Process.SYSTEM_UID, true) 函数执行优化系统库文件(实际上是通过执行 dexopt apkpath uid boolean 命令完成jar的优化的. ). 紧接着是framework目录下的所有jar包也会被执行一次dexopt操作.
紧接着就是把/system/frameworks(系统库) /system/app(系统apk) /vendor/app(第三方厂商apk) 三个系统包路径下调用scanDirLI()–> scanPackageLI() 方法了, 通过一个PackageParser对象来生成一个parsed的package对象, 完成了从物理文件(apk)到内存映射数据结构的转换过程, 这个过程主要是解析apk的root路径下的manifest.xml文件, parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME) 说明了解析的文件就是Manifest.xml, 读取出所有的manifest内的数据. 包括Provider/Service/Activity/Permission 都是把对应的标签内的信息映射到实际的数据结构中来.
|
|
以上的简化代码就是遍历每一个Manifest文件中的标签数据并转化成一个个的Service(Activity)IntentResolver对象,实现了物理文件转内存里的数据结构. 所以可见整个流程可以理解成, scanDirLI()遍历系统的所有apk/JAR文件夹路径, 然后找到每一个单独的文件通过scanPackageLI()来解析单个的apk文件(其实就是解析Manifest.xml), 取出Manifest.xml中所有有用的信息, 最终暴露出每一个package对象的数据。