本文介绍PackageManagerService启动流程。
相关源码:
一、system_server启动PMS
Android的所有Java服务都是通过system_server
进程启动的,并且驻留在system_server
进程中。SystemServer进程在启动时,通过创建一个ServerThread
线程来启动所有服务,现在先来看看Android服务中PackageManagerService
服务启动过程。
/frameworks/base/services/java/com/android/server/SystemServer.java
1.1 startBootstrapServices()
system_server的startBootstrapServices()
函数会启动一些引导服务,该方法所创建的服务:
- ActivityManagerService,
- PowerManagerService,
- LightsService,
- DisplayManagerService,
PackageManagerService
,- UserManagerService,
- SensorService服务。
其中我们需要的PackageManagerService就在这里启动,我们来看看startBootstrapServices()是如何启动PMS的:
|
|
1.2 startOtherServices()
另外,system_server的startOtherServices()
方法会启动其他服务,这个函数也会对PMS作一些操作:
|
|
从上面的代码可以看出,PMS启动后将参与一些系统优化的工作,然后调用SystemReady函数通知系统进入就绪状态。
整个system_server进程启动过程,涉及PMS服务的主要几个动作如下:
- PMS.main()
- PMS.performDexOpt()
- PMS.systemReady()
本文主要介绍PMS.main()流程,即PackageManagerService启动流程。
二、PMS.main入口
PackageManagerService.main过程主要是创建PMS服务,并注册到ServiceManager大管家:
|
|
三、PMS构造函数 - 分析
new PackageManagerService(context, installer, factoryTest, onlyCore);
创建PMS对象的过程,就是执行PMS的构造函数,PMS构造函数比较长,我们把这个过程分成几个阶段:
- BOOT_PROGRESS_PMS_START,
- BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
- BOOT_PROGRESS_PMS_DATA_SCAN_START,
- BOOT_PROGRESS_PMS_SCAN_END,
- BOOT_PROGRESS_PMS_READY,
PMS构造函数里面,在每个阶段开始的时候,都会往Eventlog
里面打Tag,比如像这样:
|
|
接下来分别说说这几个阶段。
3.1 PMS_START
BOOT_PROGRESS_PMS_START阶段:
|
|
我们再来总结PMS_START阶段都做了什么:
总结阶段一主要工作如下:
- 构造DisplayMetrics类:描述界面显示,尺寸,分辨率,密度。构造完后并获取默认的信息保存到变量mMetrics中。
- 构造Settings类:这个是Android的全局管理者,用于协助PMS保存所有的安装包信息
- 保存Installer对象
- 获取系统配置信息:SystemConfig构造函数中会通过
readPermissions()
解析指定目录下的所有xml文件,然后把这些信息保存到systemConfig中,涉及的目录有如下:- /system/etc/sysconfig
- /system/etc/permissions
- /oem/etc/sysconfig
- /oem/etc/permissions
- 创建名为PackageManager的handler线程,建立PackageHandler消息循环,用于处理外部的安装请求等消息
- 创建data下的各种目录,比如data/app, data/app-private等。
- 创建用户管理服务UserManagerService
- 把systemConfig关于xml中的
标签所指的动态库保存到mSharedLibraries - Settings.readLPw扫描解析packages.xml和packages-backup.xml
补充说明下debug.separate_processes这个属性:
这个属性你可以使用强制应用程序组件在自己的进程中运行,有两种方法可以使用这个:
|
|
这个属性一般不会用到。
3.2 PMS_SYSTEM_SCAN_START
接下来是BOOT_PROGRESS_PMS_SYSTEM_SCAN_START阶段:
|
|
PMS_SYSTEM_SCAN_START阶段主要做了如下工作:
- 首先将BOOTCLASSPATH,SYSTEMSERVERCLASSPATH这两个环境变量下的路径加入到不需要dex优化集合alreadyDexOpted中
- SYSTEMSERVERCLASSPATH:主要包括/system/framework目录下services.jar,ethernet-service.jar,wifi-service.jar这3个文件。
- BOOTCLASSPATH:该环境变量内容较多,不同ROM可能有所不同,常见内容包含/system/framework目录下的framework.jar,ext.jar,core-libart.jar,telephony-common.jar,ims-common.jar,core-junit.jar等文件。
- 获取共享库mSharedLibraries,判断是否需要dex优化,如果需要则进行dex优化,并加入到alreadyDexOpted列表中
- 添加framework-res.apk、core-libart.jar两个文件添加到已优化集合alreadyDexOpted中
- 将framework目录下,其他的apk或者jar,进行dex优化并加入已优化集合alreadyDexOpted中
- scanDirLI(): 扫描指定目录下的apk文件,最终调用PackageParser.parseBaseApk来完成AndroidManifest.xml文件的解析,生成Application, activity,service,broadcast, provider等信息
- 删除系统不存在的包 removePackageLI
- 清理安装失败的包 cleanupInstallFailedPackage
- 删除临时文件 deleteTempPackageFiles
- 移除不相干包中的所有共享userID
3.3 PMS_DATA_SCAN_START
BOOT_PROGRESS_PMS_DATA_SCAN_START阶段:
|
|
- 当mOnlyCore = false时,则scanDirLI()还会收集如下目录中的apk
- /data/app
- /data/app-private
3.4 PMS_SCAN_END
BOOT_PROGRESS_PMS_SCAN_END阶段:
|
|
- 当sdk版本不一致时,需要更新权限
- 当这是ota后的首次启动,正常启动则需要清除目录的缓存代码
- 当权限和其他默认项都完成更新,则清理相关信息
- 信息写回packages.xml文件
3.5 PMS_READY
BOOT_PROGRESS_PMS_READY阶段:
|
|
- 初始化PackageInstallerService
- GC回收下内存
四、PMS构造函数 - 总结
PMS初始化过程,分为5个阶段:
1. PMS_START阶段:
- 创建Settings对象;
- 将6类shareUserId到mSettings;
- 初始化SystemConfig;
- 创建名为“PackageManager”的handler线程mHandlerThread;
- 创建UserManagerService多用户管理服务;
- 通过解析4大目录中的xmL文件构造共享mSharedLibraries;
2. PMS_SYSTEM_SCAN_START阶段:
- mSharedLibraries共享库中的文件执行dexopt操作;
- system/framework目录中满足条件的apk或jar文件执行dexopt操作;
- 扫描系统apk;
3. PMS_DATA_SCAN_START阶段:
- 扫描/data/app目录下的apk;
- 扫描/data/app-private目录下的apk;
4. PMS_SCAN_END阶段:
- 将上述信息写回/data/system/packages.xml;
5. PMS_READY阶段:
- 创建服务PackageInstallerService;
到这里,大致介绍完了整个PMS构造函数的流程,基本上PMS_SCAN_END阶段我们apk就算安装完成了,那么接下来我们单独看看其中几个比较重要的模块:
- Settings
- SystemConfig - readPermissions
- scanPackageLI
五、Settings
在BOOT_PROGRESS_PMS_START阶段,我们会创建Setting对象,以及一堆的addSharedUserLPw调用:
5.1 创建Settings
frameworks/base/services/core/java/com/android/server/pm/Settings.java
|
|
Settings的构造函数主要用于创建”data/system”目录和一些xml文件,并配置相应的权限,其中:
- packages.xml 记录所有安装app的信息,当系统进行程序安装、卸载和更新等操作时,均会更新该文件。
- packages-backup.xml 备份文件
- packages-stopped.xml 记录被用户强行停止的应用的Package信息
- packages-stopped-backup.xml 备份文件
- packages.list 记录非系统自带的APK的数据信息,这些APK有变化时会更新该文件
5.2 Setings.readLPw
readLPw()函数,从/data/system/packages.xml或packages-backup.xml文件中获得packages、permissions相关信息,添加到相关内存列表中。packages.xml文件记录了系统的permisssions以及每个APK的name、codePath、flags、version等信息这些信息主要通过APK的AndroidManifest.xml解析获取,解析完APK后将更新信息写入这个文件,下次开机直接从里面读取相关信息添加到内存相关结构中。当有APK升级、安装或删除时会更新这个文件。
5.3 Settings.writeLPr
writeLPr函数,将解析出的每个APK的信息(mSetting.mPackages)保存到packages.xml和packages.list文件。packages.list记录了如下数据:pkgName, userId, debugFlag, dataPath(包的数据路径)。
六、SystemConfig - readPermissions
同样是在BOOT_PROGRESS_PMS_START阶段,我们会初始化SystemConfig去获取系统配置信息:
|
|
6.1 创建SystemConfig
frameworks/base/services/core/java/com/android/server/SystemConfig.java
|
|
从上面的代码可以看出,SystemConfig是单例模式,会通过readPermissions解析指定目录下的xml文件:
- /system/etc/sysconfig
- /system/etc/permissions
- /odm/etc/sysconfig
- /odm/etc/permissions
- /oem/etc/sysconfig
- /oem/etc/permissions
其中比较重要的是system/etc/permissions目录,该目录文件大多来源于代码中的framworks/(base or native)/data/etc
,这些文件的作用是表明系统支持的feature有哪些,例如是否支持蓝牙、wifi、P2P等。
6.2 readPermissions
readPermissions会循环去读取目录下的xml文件,但是它会跳过platform.xml文件,最后再去读取platform.xml文件。
|
|
我们发现读取函数最后都调用了readPermissionsFromXml(),函数readPermissionsFromXml最终会使用XMLPullParser的方式解析这些XML文件,然后把解析出来的数据结构保存到PMS中。
6.2.1 android.hardware.bluetooth.xml
最终会解析并且保存到PMS的final ArrayMap<String, FeatureInfo> mAvailableFeatures
中。
|
|
6.2.2 com.android.location.provider.xml
指明了运行一些library时,还需要加载一些java库。
这个最终会解析并保存到PMS的final ArrayMap<String, SharedLibraryEntry> mSharedLibraries
中。
|
|
6.2.3 platform.xml
这个文件中定义了底层GID和app层权限名字之间的对应关系,或者直接给某一个uid赋予对应的权限:
|
|
解析<permission>
标签的时候,会创建一个PermissionEntry类,他关联了gids和permission name:
最终PermissionEntry会放入SystemConfig的final ArrayMap<String, PermissionEntry> mPermissions
变量中。
|
|
解析<assign-permission>
的时候表示把属性name中的字符串表示的权限赋予属性uid中的用户。uid和name则存入SystemConfig中的SparseArray> 类型的mSystemPermissions
变量中。
七、scanPackageLI
scanPackageLI是比较重要的安装apk的方法,下面具体分析。
7.1 scanDirLI
scanDirLI函数会处理目录下每一个package文件:(当然不止scanDirLI最后会调用到scanPackageLI)
|
|
scanPackageTracedLI函数最终会调用到scanPackageLI函数:
|
|
7.2 scanPackageLI安装apk
PackageManagerService的scanPackageLI过程scanPackageLI()有3个重载的方法,参数稍有不同:
|
|
(1)scanPackageLI(File scanFile, int parseFlags,…)函数
- 实例化一个PackageParser对象,接着调用该对象的parsePackage()对APK文件进行解析。
- 实例化一个Package对象,用于保存解析出的APK信息
- 从AndroidManifest.xml文件中解析出VersionCode、VersionName、installLocation等全局属性信息,然后根据标签循环解析XML文件包含的其它组成部分,将解析出的信息添加到Package对象的相关列表中。
|
|
最后会调用第(2)个scanPackageLI去继续解析。
(2)scanPackageLI(PackageParser.Package pkg, File scanFile,…)函数
|
|
(3)scanPackageLI(PackageParser.Package pkg, final int policyFlags,…)函数
最终会走到第三个scanPackageLI函数,这个函数最后会调用scanPackageDirtyLI函数,scanPackageDirtyLI是实际解析package的函数,这个才是真正干活的。
|
|
7.3 scanPackageDirtyLI
通过上述的扫描过程,我们得到了当前apk文件对应的Package信息。不过这部分信息是存储在PackageParser中的,我们必须将这部分信息传递到PMS中。毕竟最终的目的是:让PMS能得到所有目录下Package的信息。
scanPackageDirtyLI函数主要就是把签名解析应用程序得到的package、provider、service、receiver和activity等信息保存在PackageManagerService相关的成员列表里。
比如将每个APK的receivers列表里的元素,通过mReceivers.addActivity(a, “receiver”)添加到PMS成员列表mReceivers中:
|
|
由于实际解析函数太长,粗略看下有1000来行,读者有兴趣的可以自行研究。
八、开机时间分析
adb shell cat /proc/bootprof/
我们可以从上面的信息看到PMS在开机的时候做的动作和时间分布:(因为手上只有kk的平台,所以信息不太对应)
|
|
一般app装的越多,那么开机时间就会越长。