Android M PackageManagerService 启动过程分析

前言

Android系统中,和用户关系最密切的service应该是PackageManager了。

一般来说,用户想要在Android设备上进行自己感兴趣的活动,都少不了apk的支持。

不论是打电话,上网,发短信还是玩一些自己喜欢的游戏,这些内容在android的世界里都是以apk的形式存在的。

所以,apk的安装,卸载是与每个用户是息息相关的。

我们今天的任务就是解析PackageManager的工作原理,apk的安装和卸载的过程。

PKMS服务启动过程

本篇涉及的代码及路径:

frameworks/base/services/java/com/android/server/SystemServer.java
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
frameworks/base/services/core/java/com/android/server/pm/Settings.java
frameworks/base/services/core/java/com/android/server/SystemConfig.java
frameworks/base/core/java/android/content/pm/PackageParser.java
frameworks/base/data/etc/ platform.xml

SystemServer中,我们知道启动Android的系统关键服务的函数首先就是startBootstrapServicespkms也就是在这个方法中启动的。


在启动PKMS之前,会先启动installer服务, Installer是pm路径下面的一个单独的类,主要用于通过InstallerConnection建立和installd的链接,然后Installd会进行创建一些系统关键目录的作用,所以我们要等待Installd的结束,才可以继续进行其它的创建,这里mOnlyCore 用于判断是否只扫描系统的目录,后面会用到。

启动PKMS是通过PackageManagerServicemain函数进入到PackageManager的实际工作中的。

PackageManagerServicemain函数其实很简单,创建一个Pkms对象,并将其注册到ServiceManager大管家中


这里主要new了一个PacnakeManagerService对象,所以,所有的工作都是在构造函数里做的,其中,PackageManager的构造函数的主要功能为:

1. 扫描Android系统中几个目标文件夹中的apk,并且建立相应的数据结构去管理Package的信息,四大组件的信息,权限信息等内容

2. 解析物理的apk的文件,生成符合自己需求的数据结构。

所以,学习PackageManager最重要的就是学习保存各种信息的数据结构和他们之间的关系,以及控制的策略。

PKMS构造函数

PKMS对象的构造方法很长,分为以下几个阶段,每个阶段会输出相应的EventLog,除了阶段1的开头部分代码,后续代码都是同时持有同步锁mPackages和mInstallLock的过程中执行的


下面分别从这五个阶段分析其构造过程。

BOOT_PROGRESS_PMS_START

第一阶段分两部分,先看前半部分。



说明:

  • 初始化DisplayMetrics(设备显示屏相关)Settings,将shareUserId加入到mSettings中保存;
  • 保存installer对象,该对象和Native进程installd交互;
  • 创建PackageDexOptimizer对象,后面用它进行dex优化;
  • 通过SystemConfig对象解析系统xml文件,获取系统配置信息(permission-gids以及featureslibrary)

参数mDexOptLRUThresholdInMills用于决定执行dex优化操作的时间阈,这个参数用于后续的PKMS.performBootDexOpt()过程。

  • 对于Eng版本,则只会对30分钟之内使用过的app执行dex优化;
  • 对于非Eng版本,则会将用户最近一周内使用过的app执行dex优化;

接下来,在看后半部分,后半部分已经进了mInstallLockmPackages的同步锁了。


说明:

创建一个ThreadHandler对象,实际就是创建一个带消息循环处理的线程,该线程的工作是:程序的安装和卸载等。

以ThreadHandler线程的消息循环(Looper对象)为参数创建一个PackageHandler,可知该Handler的handleMessage函数将运行在此线程上。

这个过程还涉及的几个重要变量:

变量

对应目录

说明

mAppDataDir

/data/data

App数据存放目录

mAppInstallDir

/data/app

第三方app安装路径

mAppLib32InstallDir

/data/app-lib

App库目录

mAsecInternalPath

/data/app-asec

付费app

mUserAppDataDir

/data/user

用户数据

mDrmAppPrivateInstallDir

/data/app-private

DRM保护的app

 1.  Settings的构造


说明:

Settings的构造函数在data目录下新建了一个/system目录,然后新建了packages.xml, packages-backup.xml, packages.list,packages-stopped.xml等文件。

这些文件都有一些具体的作用。

文件

功能说明

packages.xml

保存了系统所有的Package信息

packages-backup.xml

packages.xml的备份,防止在写packages.xml突然断电

packages.list

保存了系统中已经安装的apk,以及对应的data/data/下面的对应关系

packages-stopped.xml

用于记录系统中强制停止运行的Package信息

packages-stopped-backup.xml

packages-stopped.xml的备份,防止在写packages-stopped-backup的时候突然断电

2. SystemConfig的构造


说明:

通过readPermissions读取系统权限及配置,主要是解析目录下的所有xml文件。比如将标签<library>所指的动态库保存到 PKMS的成员变量mSharedLibraries。可见,SystemConfig创建过程是对以下这四个目录中的所有xml进行解析:

  • /system/etc/sysconfig
  • /system/etc/permissions
  • /oem/etc/sysconfig
  • /oem/etc/permissions

接着看readPermissions方法做了什么。


该方法是解析指定目录下所有的具有可读权限的xml文件。

可以看到platfrom.xml是最后才会解析的,解析这个文件建立android权限和gid的对应关系。将解析的结果mSystemPermissions,mSharedLibraries,mPermissions,mAvailableFeatures等几个集合中供系统查询和权限配置使用。 后面在扫描apk时,会由请求的权限找到对应的gid,并保存在Package类中。

3.  platfrom.xml部分内容

说明:

  • 权限名与gid的映射关系,比如WRITE_MEDIA_STORAGE权限对应的用户组是media_rw和sdcard_rw。
  • permission和group用于建立Linux层gid和Android层pemission之间的映射关系。
  • assign-permission用于向指定的uid赋予相应的权限。这个权限由Android定义,用字符串表示。当进程使用这个UID运行时,就具备了这个权限。
  • library用于指定系统扩展库。当应用程序运行时,系统会自动为这些进程加载这些库。

其他的文件如xxx-hardware.xx.xml则用于描述一个手持终端(包括手机、平板电脑等)应该支持的硬件特性,例如支持camera、支持蓝牙等。系统每增加一个硬件,都要添加相应的feature

注意:对于不同的硬件特性,还需要包含其他的xml文件。例如,要支持自动聚焦,还需要包含android.hardware.camera. autofocus.xml文件。这些文件内容大体一样,都通过feature标签表明自己的硬件特性。

1. mSettings.readLPw

判断 /data/system/packages.xm 文件是否存在,如果不存在则返回 false ,如果存在则进行解析,在系统第一次启动时 packages.xml 文件是不存在的,由 writeLP() 创建该文件,并将该文件写到设备里,下次开机会直接读取并解析这个文件。解析的过程即是按照 xml 定义的标签,将对应的属性和值添加到全局列表中。 packages.xml 文件中记录了系统安装的所有 apk 的属性权限的信息,当系统中的 apk 安装,删除或升级时,改文件就会被更新。

PMS_SYSTEM_SCAN_START

Scan Start阶段代码太多,我就不贴了,整理下具体所做的工作吧:

1.       alreadyDexOpted存放已经优化或不需要优化的文件

finalString bootClassPath = System.getenv("BOOTCLASSPATH");
finalString systemServerClassPath =System.getenv("SYSTEMSERVERCLASSPATH");
BOOTCLASSPATH=/system/framework/core-libart.jar:/system/framework/conscrypt.jar:/system/framework/okhttp.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/telephony-common.jar:/system/framework/voip-common.jar:/system/framework/ims-common.jar:/system/framework/apache-xml.jar:/system/framework/org.apache.http.legacy.boot.jar:/system/framework/com.askey.ktechandler.jar
SYSTEMSERVERCLASSPATH=/system/framework/services.jar:/system/framework/ethernet-service.jar:/system/framework/wifi-service.jar:/system/framework/realtek.jar

2.    对mSharedLibraries中的库调用mInstaller.dexopt进行优化,执行dex优化操作的文件有以下几类:

  • mSharedLibraries:该共享库下的所有文件,是由SystemConfig构造函数中赋值的;
  • /system/framework:该目录的所有apk和jar文件,去除位于alreadyDexOpted中的文件。 具体有哪些文件不包括呢?比如services.jar, framework-res.apk,core-libart.jar
3.    按照如下顺序调用ScanDirLI 扫描指定目录下的 apk 文件,最终调用 PackageParser.parseBaseApk 来完成 AndroidManifest.xml 文件的解析,生成 Application, activity,service,broadcast, provider 等信息。

4.      possiblyDeletedUpdatedSystemApps/ deletePkgsList删除不存在的系统包以及不完整的包,还有tmp文件。

接下来我们研究下PKMS的核心方法scanDirLI


在scanDirLI中,又继续调用的是scanPackageLI。scanPackageLI函数扫描一个特定的文件,返回值是PackageParser的内部类package。该类的实例代表一个apk文件,所以它就是和apk文件对应的数据结构。


方法说明:

  • 创建一个PackageParser对象pp,调用parsePackage解析manifest文件,解析完成后返回PackageParser.Package pkg;
  • 解析完后会分析pkg是否已经存在/更新/是否系统pkg等。
  • 对解析的pkg的证书进行verify;
  • 对解析的pkg的签名进行compare;
  • 对pkg的applicationInfo进行归档;
  • 最后在调用scanPackageLI的重载方法进行对pkg数据结构归档到PKMS的数据结构中以便统一管理;

parsePackage

parseClusterPackage用于解析文件夹,parseMonolithicPackage用于解析非文件夹,即普通apk文件。

方法说明:

  • 创建一个AssetManager,他是android的资源器;
  • 对apk文件调用parseBaseApk解析;

接着看parseBaseApk做了什么?

方法说明:

  • 获取apk文件绝对路径;
  • 将apk文件的资源加载到资源管理器中;
  • Resources类的成员函数updateConfiguration首先是根据参数config和metrics来更新设备的当前配置信息, 例如,屏幕大小和密码、国家地区和语言、键盘配置情况等等, 接着再调用成员变量mAssets所指向的一个Java层的AssetManager对象的成员函数setConfiguration来将这些配置信息设置到与之关联的C++层的AssetManager对象中去;
  • 构建XmlResourceParser对象;
  • 调用parseBaseApk的重载方法解析apk文件,主要解析manifest;
  • 解析完成归档数据结构到pkg中;

parseBaseApk重载方法

这个方法很长,分阶段讲解.


第一阶段:

解析manifest标签,得到pkgName,mVersionCode, shareUserId,installLocation,coreApp等属性,并将它们归档pkg数据结构。

比如Gallery3d的manifest标签:


Settings的manifest标签:


可以看到settings为coreApp,且共享Uid为system,权限较高。

第二阶段:

解析application标签,permission-group,permission,uses-permission,uses-feature,supports-screens一些列标签,其中,parseBaseApplication会去解析我们的四大组件。

在分析parseBaseApplication之前,先看PackageParser内部类package,后面解析出的四大组件包括前面解析的其他属性都会保存在这个pkg中。


到这里,apk的manifest就算是解析完了,最后,调用scanPackageLI的重载方法完成数据结构的归档,统一整理到PKMS的结构中,并通过install命令添加data/data/pkgName目录。

private PackageParser.PackagescanPackageLI(File scanFile, int parseFlags, int scanFlags, long currentTime,UserHandle user) ;
private PackageParser.Package scanPackageLI(PackageParser.Packagepkg, int parseFlags, int scanFlags, long currentTime, UserHandle user);

PKMS的成员对象如下:


一次将之前的pkg信息填充到PKMS的数据结构中。


主体流程图如下:


PMS_DATA_SCAN_START

到达Data Scan Start阶段时,开始处理非系统app。

mOnlyCore= false,则scanDirLI()收集如下目录中的apk

  • data/app
  •  /data/app-private

PMS_SCAN_END

说明:

  • 为app分配linux用户组ID,授予权限(第三部分会详细介绍);
  • 设置默认浏览器;
  •  通过Settings将数据写入packages.xml文件,以便下次开机后直接读取;

PMS_READY

到这里我们的两个同步锁中的内容就跑完了。PKMS初始化完成阶段,还会创建一个PackageInstaller服务。

启动流程小结

PKMS初始化过程,分为5个阶段:

  • PMS_START阶段:
  1. 创建Settings对象;
  2. 将6类shareUserId到mSettings;
  3. 初始化SystemConfig;
  4. 创建名为“PackageManager”的handler线程mHandlerThread;
  5. 创建UserManagerService多用户管理服务;
  6. 通过解析4大目录中的xmL文件构造共享mSharedLibraries;
  • PMS_SYSTEM_SCAN_START阶段:
  1. mSharedLibraries共享库中的文件执行dexopt操作;
  2. system/framework目录中满足条件的apk或jar文件执行dexopt操作;
  3. 扫描系统apk;
  • PMS_DATA_SCAN_START阶段:
  1. 扫描/data/app目录下的apk;
  2. 扫描/data/app-private目录下的apk;
  • PMS_SCAN_END阶段:
  1. 将上述信息写回/data/system/packages.xml;
  • PMS_READY阶段:
  1. 创建服务PackageInstallerService;
posted @ 2018-06-14 11:01  mail181  阅读(69)  评论(0编辑  收藏  举报