导读
当
PMS
笔记大小写到106kb
时,就知大事不妙,导读叒叒叒来了
不得已,将原本计划一篇结束的文章分为了如下三部分:
前两篇主要梳理了PMS
初始化的流程和初始化的一些细节;最后一篇对应用的安装过程进行了简单梳理。
有没有注意梳理
字眼,关于想要深入全面学习PMS
的同学只能非常抱歉,本系列文章也仅仅是起到的主要流程的梳理作用。
我们先从整体上掌握,等后面遇到相关需求再来仔细研究吧(这波安慰很及时。。。。)
整个模块学习下来,简单对PackageManagerService
吐槽一下:
PMS
中涉及的类比较多,真正掌握需要一番时间和精力- 相关的类像:
Package
相关、Session
相关、Settings
等等
- 相关的类像:
- 阅读
PMS
的方法有点拆俄罗斯套娃
的感觉,一层又一层。。。。 - 业务逻辑复杂,几乎每个方法的执行会夹杂着各种
权限
和用户
的逻辑判断
有了上面的预期,欢迎来到PackageManagerService
的世界
了解PackageManagerService
类
PackageManagerService
代码行数在24000
行。。。。。
Android
的应用管理主要是通过PackageManagerService
来完成的。PackageManagerService
负责各种APK
包的安装、卸载、优化和查询。
Android
中的应用可以简单分为两大类:系统应用
和普通应用
:
系统应用
是指位于/system/app
和/system/priv-app
目录下的应用/system/priv-app
是从Android 4.4
开始出现的目录,存放的是一些系统底层的应用,如:Settings
、SystemUI
等/system/app
存放的则是一些系统级的应用,如Calendar
、Contacts
等
普通应用
是用户安装的应用,位于目录/data/app
下
PackageManagerService
在启动时会扫描所有APK
文件和Jar
包,然后把它们的信息读取到内存中,这样系统在运行时就能迅速找到各种应用和组件信息。
-
扫描过程如果遇到没有优化的文件,还要执行优化操作。
从
Android 5.0
开始引入了ART
,ART
使用预先 (AOT
) 编译,并且从Android 7.0
开始结合使用AOT
、即时 (JIT) 编译
和配置文件引导型编译
来优化应用的启动和执行效率
不着急直接分析源代码,我们先从使用的角度简单了解下
在应用中,如果要使用PackageManagerService
服务,通常是调用Context
的getPackageManager()
方法,内容如下:
public PackageManager getPackageManager() {
if (mPackageManager != null) {
return mPackageManager;
}
IPackageManager pm = ActivityThread.getPackageManager();
if (pm != null) {
// Doesn't matter if we make more than one instance.
return (mPackageManager = new ApplicationPackageManager(this, pm));
}
return null;
}
调用到了ActivityThread.getPackageManager()
的方法:
public static IPackageManager getPackageManager() {
if (sPackageManager != null) {
return sPackageManager;
}
IBinder b = ServiceManager.getService("package");
sPackageManager = IPackageManager.Stub.asInterface(b);
return sPackageManager;
}
从这两个getPackageManager()
方法的代码中可以看到:
-
返回的是
ApplicationPackageManager
对象,这个对象创建时使用了IPackageManager
对象作为参数IPackageManager
对象是PackageManagerService
的Binder
代理对象
-
ApplicationPackageManager
继承自PackageManager
,在PackageManager
中定义了应用可以访问PackageManagerService
的所有接口
PackageManager
关系图如下:
而对于PackageManagerService
类来说,有两个重要成员变量:
-
mInstallerService
:类PackageInstallerService
的实例对象。Android
通过PackageInstallerService
来管理应用的安装过程。PackageInstallerService
也是一个Binder
服务,对应的代理对象是PackageInstaller
-
mInstaller
:类Installer
的实例对象。类结构相对简单,有一个IInstalld mInstalld
变量,通过Binder调用
来和installd
进程通信- 实际上系统中进行
apk
文件格式转换、建立数据目录等工作最后都是由installd
进程来完成的
- 实际上系统中进行
上述这几个对象之间的关系图如下:
Settings
类和packages.xml
在开始分析PackageManagerService
前,我们要先看下Settings
类,这个类用来保存和PackageManagerService
相关的一些设置,它保存的内容在解析应用时会用到。
先看看com.android.server.pm.Settings
类的构造方法:
Settings(File dataDir, PermissionSettings permission, Object lock) {
// 省略权限相关的处理
......
// 在data目录下创建system目录
mSystemDir = new File(dataDir, "system");
mSystemDir.mkdirs();
// 设置目录的属性为0775
FileUtils.setPermissions(mSystemDir.toString(),
FileUtils.S_IRWXU|FileUtils.S_IRWXG
|FileUtils.S_IROTH|FileUtils.S_IXOTH,
-1, -1);
mSettingsFilename = new File(mSystemDir, "packages.xml");
mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");
mPackageListFilename = new File(mSystemDir, "packages.list");
FileUtils.setPermissions(mPackageListFilename, 0640, SYSTEM_UID, PACKAGE_INFO_GID);
......
// Deprecated: Needed for migration
mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
}
Settings
类的构造方法会在/data
目录下创建system
目录用来保存一些系统配置文件。创建了5个位于目录/data/system
的File
对象:
packages.xml
:记录系统中所有安装的应用信息,包括基本信息、签名和权限。packages-backup.xml
:packages.xml
文件的备份packages.list
:保存普通应用的数据目录和uid
等信息packages-stopped.xml
:记录系统中被强制停止运行的应用信息。系统在强制停止某个应用时,会将应用的信息记录到文件中packages-stopped-backup.xml
:packages-stopped.xml
文件的备份
packages-backup.xml
和packages-stopped-backup.xml
是备份文件。关于备份文件的逻辑是:
- 当Android对文件
packages.xml
和packages-stopped.xml
写之前,会把他们备份 - 如果文件写成功了,再把备份文件删除掉
- 如果写的时候系统出了问题,重启后再需要读取这两个文件时,如果发现备份文件存在,会使用备份文件的内容,因为原文件可能已经损坏了。
packages.xml
是PackageManagerService
启动时需要用到的文件,我们先看看文件的格式:
<package name="com.android.providers.telephony" codePath="/system/priv-app/TelephonyProvider" nativeLibraryPath="/system/priv-app/TelephonyProvider/lib" publicFlags="1007402501" privateFlags="8" ft="11e8dc5d800" it="11e8dc5d800" ut="11e8dc5d800" version="28" sharedUserId="1001" isOrphaned="true">
<sigs count="1" schemeVersion="3">
<cert index="0" />
</sigs>
<perms>
<item name="android.permission.USE_RESERVED_DISK" granted="true" flags="0" />
<item name="android.permission.INTERACT_ACROSS_USERS_FULL" granted="true" flags="0" />
<item name="android.permission.INTERACT_ACROSS_USERS" granted="true" flags="0" />
<item name="android.permission.MODIFY_PHONE_STATE" granted="true" flags="0" />
</perms>
<proper-signing-keyset identifier="1" />
</package>
上面是文件中的一个<package/>
标签,表示一个应用的基本信息、签名和声明的权限,从内容来看基本都是解析AndroidManifest.xml
的内容。
我们看下主要属性和标签:
name
表示应用的包名codePath
表示apk
文件的位置nativeLibraryPath
表示应用的native
库的储存路径it
表示应用安装时间ut
表示最后一次修改的时间version
表示应用的版本号sharedUserId
表示应用用来共享的用户IDuserId
表示应用所属的用户ID<sign/>
表示应用的签名- 属性
count
表示标签中包含有多少个证书 - 标签
<cert/>
表示具体证书的值
- 属性
<perms/>
表示应用声明使用的权限- 每个子标签
<item/>
代表一项权限
- 每个子标签
除了<package/>
标签,packages.xml
中还可能存在一下标签:
<updated-package/>
:记录系统应用升级的情况。当安装了一个包名相同、版本号更高的应用后,Android
会通过此标签记录被覆盖的系统应用的信息。<renamed-package/>
:记录变更应用包名的情况。- 应用的包名通常是在
AndroidManifest.xml
中使用属性package
来指定,同时在AndroidManifest.xml
中还可以用<original-package>
来指定原始包名,对于这种情况:- 当设备上存在低版本且包名与原始包名相同的应用时,会升级覆盖原始应用。
- 最后运行的是新安装的应用,但是运行时应用的包名还是原始的包名。
Android
会通过<renamed-package/>
标签来记录这种改名的情况。
- 应用的包名通常是在
<cleaning-package/>
:用来记录那些已经删除,但是数据目录还暂时保留的应用的信息
对于<package/>
和<updated-package/>
标签,解析后的数据将保存在PackageSetting
的对象中。PackageSetting
类的继承关系图如下:
PackageSetting
继承了PackageSettingBase
类,PackageSettingBase
又继承了SettingBase
类- 应用的基本信息保存在
PackageSettingBase
类的成员变量中 - 申明的权限保存在
SettingBase
的mPermissionState
中
- 应用的基本信息保存在
- 签名信息保存在
SharedUserSetting
类的成员变量signatures
- 类
SharedUserSetting
用来描述具有相同sharedUserId
的应用信息- 它的成员变量
packages
是用来保存所有具有相同sharedUserId
应用对象的集合 - 这些应用的签名是相同的,因此只需要通过一个成员变量
signatures
保存即可
- 它的成员变量
代表标签<package/>
的PackageSetting
对象都会保存在Settings
的成员变量mPackages
中,定义如下:
final ArrayMap<String, PackageSetting> mPackages = new ArrayMap<>();
代表标签<updated-package/>
的PackageSetting
对象都会保存在Settings
的成员变量mDisabledSysPackages
中,定义如下:
private final ArrayMap<String, PackageSetting> mDisabledSysPackages = new ArrayMap<String, PackageSetting>();
代表标签<cleaning-package/>
的PackageSetting
对象都会保存在Settings
的成员变量mPackagesToBeCleaned
中,定义如下:
final ArrayList<PackageCleanItem> mPackagesToBeCleaned = new ArrayList<PackageCleanItem>();
代表标签<renamed-package/>
的PackageSetting
对象都会保存在Settings
的成员变量mRenamedPackages
中,定义如下:
private final ArrayMap<String, String> mRenamedPackages = new ArrayMap<String, String>();
这四个对象在PackageManagerService
中会经常遇到,为了方便后面的理解,请记一下哈
PackageManagerService
的初始化
有了前面的铺垫,我们来看下PackageManagerService
的初始化过程。
PackageManagerService
是在SystemServer
中初始化的,分别在startBootstrapServices()
和startOtherServices()
中都进行了一些处理。相关代码如下:
private void startBootstrapServices() {
......
// 启动 installd,PMS依赖此服务
Installer installer = mSystemServiceManager.startService(Installer.class);
......
// 判断vold.decrypt是否被设定为加密状态
// mOnlyCore=true表示加密状态,只能处理系统应用
// 一般情况mOnlyCore为false
String cryptState = SystemProperties.get("vold.decrypt");
if (ENCRYPTING_STATE.equals(cryptState)) {
Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
mOnlyCore = true;
} else if (ENCRYPTED_STATE.equals(cryptState)) {
Slog.w(TAG, "Device encrypted - only parsing core apps");
mOnlyCore = true;
}
// 调用main()初始化PMS
mPackageManagerService = PackageManagerService.main(mSystemContext, installer, mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
mFirstBoot = mPackageManagerService.isFirstBoot();
......
// 如果设备没有加密,尝试启动 OtaDexoptService
// OtaDexoptService 是用于A/B更新的一个服务,会用到PMS
if (!mOnlyCore) {
boolean disableOtaDexopt = SystemProperties.getBoolean("config.disable_otadexopt",
false);
if (!disableOtaDexopt) {
OtaDexoptService.main(mSystemContext, mPackageManagerService);
}
}
......
}
private void startOtherServices() {
if (!mOnlyCore) {
......
// 进行dex优化
mPackageManagerService.updatePackagesIfNeeded();
}
......
// 进行磁盘维护
mPackageManagerService.performFstrimIfNeeded();
......
// PMS 准备就绪
mPackageManagerService.systemReady();
}
上面就是SystemServer
中和PackageManagerService
启动相关的操作,
我们先看下PackageManagerService
的main()
方法
PackageManagerService
的main()
方法
main()
方法内容如下:
public static PackageManagerService main(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
// Self-check for initial settings.
PackageManagerServiceCompilerMapping.checkProperties();
// 创建 PackageManagerService 对象
PackageManagerService m = new PackageManagerService(context, installer, factoryTest, onlyCore);
// 启用部分系统应用
m.enableSystemUserPackages();
// 将服务添加到ServiceManager
// 包括package和package_native
ServiceManager.addService("package", m);
final PackageManagerNative pmn = m.new PackageManagerNative();
ServiceManager.addService("package_native", pmn);
return m;
}
main()
方法比较简单,主要进行了:
- 创建
PackageManagerService
对象- 核心对象,需要详细分析
- 创建
PackageManagerNative
对象,只是简单提供了三个功能接口String[] getNamesForUids
String getInstallerForPackage
long getVersionCodeForPackage
- 然后将两个对象添加到
ServiceManager
我们来重点看下PackageManagerService
的创建过程,也就是构造方法。
PackageManagerService
的构造方法
PackageManagerService
的构造方法比较长,根据构造方法中的EventLogTags
我们可以划分为5
个阶段:
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
......
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,SystemClock.uptimeMillis());
......
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START, startTime);
......
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START, SystemClock.uptimeMillis());
......
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END, SystemClock.uptimeMillis());
......
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY, SystemClock.uptimeMillis());
......
}
BOOT_PROGRESS_PMS_START
BOOT_PROGRESS_PMS_START
阶段主要进行了:
- 创建
DisplayMetrics
对象、设置Installer
引用 - 创建
PermissionManager
对象 - 根据
permission
信息创建Settings
对象,并添加部分uid
信息 - 创建
DexManager
对象跟踪dex
使用情况 - 创建
SystemConfig
对象用来获取系统配置信息,如:共享库列表、权限等 - 创建
PackageHandler
对象,其实是一个HandlerThread
类,并添加到Watchdog
- 对于首次启动,将预编译文件拷贝到
data
分区
具体代码如下:
// ###### BOOT_PROGRESS_PMS_START 阶段 ######
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,SystemClock.uptimeMillis());
// 检测SDK版本
if (mSdkVersion <= 0) {
Slog.w(TAG, "**** ro.build.version.sdk not set!");
}
mContext = context;
// 是否为工厂模式,工厂模式主要用于系统功能检测
// 可以进行一些按键转义等操作,正式产品一般都为false
mFactoryTest = factoryTest;
// 是否只处理系统应用,一般为false
mOnlyCore = onlyCore;
// 创建DisplayMetrics对象储存屏幕显示信息
mMetrics = new DisplayMetrics();
// 设置installer
mInstaller = installer;
// Create sub-components that provide services / data. Order here is important.
synchronized (mInstallLock) {
synchronized (mPackages) {
// Expose private service for system components to use.
// 添加PackageManagerInternal到本地服务
LocalServices.addService(PackageManagerInternal.class, new PackageManagerInternalImpl());
// 初始化多用户管理服务
sUserManager = new UserManagerService(......);
// 初始化权限管理服务
mPermissionManager = PermissionManagerService.create(......);
mDefaultPermissionPolicy = mPermissionManager.getDefaultPermissionGrantPolicy();
// 创建Settings对象
mSettings = new Settings(mPermissionManager.getPermissionSettings(), mPackages);
}
}
// 通过addSharedUserLPw方法添加SharedUserSetting对象到Settings中
// sharedUserId 属性相同的包可以运行在同一个进程,或者可以相互读取资源
// 这里添加了一共是7种系统的 uid:system、phone、log、nfc、bluetooth、shell、se
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
......
// 创建dexopt命令的帮助类
mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,
"*dexopt*");
DexManager.Listener dexManagerListener = DexLogger.getListener(this,
installer, mInstallLock);
// 创建 DexManager 用来跟踪dex文件的使用情况
mDexManager = new DexManager(mContext, this, mPackageDexOptimizer, installer, mInstallLock, dexManagerListener);
// 创建ART管理服务
mArtManagerService = new ArtManagerService(mContext, this, installer, mInstallLock);
mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
......
// 获取默认屏幕参数
getDefaultDisplayMetrics(context, mMetrics);
// 创建 SystemConfig 对象,用来保存系统全局配置信息
SystemConfig systemConfig = SystemConfig.getInstance();
mAvailableFeatures = systemConfig.getAvailableFeatures();
......
synchronized (mInstallLock) {
// writer
synchronized (mPackages) {
// 创建用来处理消息的线程,并添加到Watchdog中监控
mHandlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
mHandlerThread.start();
mHandler = new PackageHandler(mHandlerThread.getLooper());
......
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
// 创建即时应用管理模块
mInstantAppRegistry = new InstantAppRegistry(this);
// 通过 systemConfig 获取系统中的共享库列表
ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries();
final int builtInLibCount = libConfig.size();
for (int i = 0; i < builtInLibCount; i++) {
String name = libConfig.keyAt(i);
String path = libConfig.valueAt(i);
addSharedLibraryLPw(path, null, name, SharedLibraryInfo.VERSION_UNDEFINED,
SharedLibraryInfo.TYPE_BUILTIN, PLATFORM_PACKAGE_NAME, 0);
}
// 打开 SELinux 的 Policy 文件:
// 1. /security/mac_permissions.xml
// 2. /etc/security/mac_permissions.xml
SELinuxMMAC.readInstallPolicy();
......
// 读取packages.xml文件,解析后将数据存放到mSettings中
mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false));
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
// Clean up orphaned packages for which the code path doesn't exist
......
if (mFirstBoot) {
// 如果是首次启动
// 向Init进程请求(通过setProp的方式)
// 将预编译dex拷贝到data分区
requestCopyPreoptedFiles();
}
}
}
BOOT_PROGRESS_PMS_SYSTEM_SCAN_START
BOOT_PROGRESS_PMS_SYSTEM_SCAN_START
阶段主要进行:
- 获取环境变量
BOOTCLASSPATH
和SYSTEMSERVERCLASSPATH
以及系统版本信息 - 根据版本信息确认升级策略
Android M
之前版本需调整逻辑为运行时获取Android N
之前版本需要调整为首次启动
- 扫描
vendor
,product
的overlay
目录,收集文件信息 - 扫描
vendor
,product
,system
,oem
等分区下的app
和priv-app
目录,收集应用信息,并将搜集到的信息保存到mPackages
中 - 将
PMS
中的mPackages
集合与mSettings
中的mPackages
集合进行比对,过滤掉无效的应用 - 删除临时文件和未使用到的
SharedUserSetting
对象
相关代码如下:
// ###### BOOT_PROGRESS_PMS_SYSTEM_SCAN_START 阶段 ######
long startTime = SystemClock.uptimeMillis();
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START, startTime);
final String bootClassPath = System.getenv("BOOTCLASSPATH");
final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");
......
File frameworkDir = new File(Environment.getRootDirectory(), "framework");
final VersionInfo ver = mSettings.getInternalVersion();
mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint);
......
// 对于Android M之前版本升级上来的情况,需将系统应用程序权限从安装升级到运行时
mPromoteSystemApps = mIsUpgrade && ver.sdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1;
// 对于Android N之前版本升级上来的情况,需像首次启动一样处理package
mIsPreNUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N;
mIsPreNMR1Upgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N_MR1;
// 扫描前保存已经存在的系统 packages,系统升级后需重新申请权限
// save off the names of pre-existing system packages prior to scanning; we don't
// want to automatically grant runtime permissions for new system apps
if (mPromoteSystemApps) {
Iterator<PackageSetting> pkgSettingIter = mSettings.mPackages.values().iterator();
while (pkgSettingIter.hasNext()) {
PackageSetting ps = pkgSettingIter.next();
if (isSystemApp(ps)) {
mExistingSystemPackages.add(ps.name);
}
}
}
// 准备解析package缓存
mCacheDir = preparePackageParserCache(mIsUpgrade);
......
// 通过scanDirTracedLI扫描指定目录的应用信息,包括:
// vendor/overlay、/product/overlay
// system/framework、system/priv-app、/system/app
// vendor/priv-app、/vendor/app
// odm/priv-app、/odm/app、
// product/priv-app、/product/app
// 并将相关的应用信息保存到mPackages中
scanDirTracedLI(new File(VENDOR_OVERLAY_DIR),...);
scanDirTracedLI(new File(PRODUCT_OVERLAY_DIR),...);
.....
// Prune any system packages that no longer exist.
final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
// Stub packages must either be replaced with full versions in the /data
// partition or be disabled.
final List<String> stubSystemApps = new ArrayList<>();
if (!mOnlyCore) {
// do this first before mucking with mPackages for the "expecting better" case
final Iterator<PackageParser.Package> pkgIterator = mPackages.values().iterator();
while (pkgIterator.hasNext()) {
final PackageParser.Package pkg = pkgIterator.next();
if (pkg.isStub) {
stubSystemApps.add(pkg.packageName);
}
}
// 循环处理mSettings.mPackages中的应用信息
final Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
while (psit.hasNext()) {
PackageSetting ps = psit.next();
if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
// 忽略普通应用
continue;
}
// 请注意,此处的mPackages是在PMS中的成员变量
// 里面存放的是scanDir扫描目录后得到的应用信息
final PackageParser.Package scannedPkg = mPackages.get(ps.name);
if (scannedPkg != null) {
// 如果mPackages的系统应用是待升级包的,把它从mPackages中移除
// disable说明是<update-package/>标签表示的应用
if (mSettings.isDisabledSystemPackageLPr(ps.name)) {
// 从扫描表mPackages中移除
removePackageLI(scannedPkg, true);
// 将其添加到mExpectingBetter集合中
// 在下一阶段会处理
mExpectingBetter.put(ps.name, ps.codePath);
}
continue;
}
// 执行到此处说明mPackages中没有ps应用信息
// 也就是系统中不存在该应用
// 检查是否属于<update-package/>标签
if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {
// 不属于<update-package/>标签,说明是残留在packages.xml中的
// 移除,后面还会进行数据清理
psit.remove();
} else {
// 应用属于<update-package/>标签
// 添加到 possiblyDeletedUpdatedSystemApps 集合中
......
possiblyDeletedUpdatedSystemApps.add(ps.name);
}
}
}
//delete tmp files
deleteTempPackageFiles();
final int cachedSystemApps = PackageParser.sCachedPackageReadCount.get();
// 删除mSettings中没有关联任何应用的SharedUserSetting对象
mSettings.pruneSharedUsersLPw();
final long systemScanTime = SystemClock.uptimeMillis() - startTime;
......
BOOT_PROGRESS_PMS_DATA_SCAN_START
BOOT_PROGRESS_PMS_DATA_SCAN_START
主要进行了:
- 扫描用户目录
/data/app
和/data/app-private
下的应用信息,并添加到mPackages
中 - 遍历
possiblyDeletedUpdatedSystemApps
集合,并逐一将其从Settings
的中disabledSystemPackage
集合中删除possiblyDeletedUpdatedSystemApps
集合中存放的是在packages.xml
文件中被标记成了<update-package/>
,但是应用文件还未找到的应用- 如果在用户目录下存在该应用升级文件,调整应用的权限为普通应用
- 如果不存在,清除应用相关数据
- 遍历
mExpectingBetter
集合。mExpectingBetter
集合中存放的是在packages.xml
文件中被标记成了<update-package/>
,资料数据齐全,不能直接启动,需要配合用户目录下的完整版应用才能启动- 此时根据集合中的应用名称,从
/data
、/vendor
、/system
等目录查找,找到后将其扫描到mPackages
集合中
- 读取
StorageManager
和SetupWizard
包名,更新所有应用的动态库路径
具体代码如下:
if (!mOnlyCore) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START, SystemClock.uptimeMillis());
// 扫描 /data/app 目录,收集应用信息
scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
// 扫描 /data/app-private 目录,收集应用信息
scanDirTracedLI(sDrmAppPrivateInstallDir, mDefParseFlags
| PackageParser.PARSE_FORWARD_LOCK,
scanFlags | SCAN_REQUIRE_KNOWN, 0);
// possiblyDeletedUpdatedSystemApps中存放的应用是在packages.xml文件中被标记成了已升级的系统应用,但是文件却不在了
// 因此这里检查用户目录下的升级文件是否存在,然后进行处理
for (String deletedAppName : possiblyDeletedUpdatedSystemApps) {
PackageParser.Package deletedPkg = mPackages.get(deletedAppName);
// 先将应用从<update-package/>集合中移除
mSettings.removeDisabledSystemPackageLPw(deletedAppName);
final String msg;
if (deletedPkg == null) {
// 用户安装目录也不存在该升级文件
msg = "Updated system package " + deletedAppName
+ " no longer exists; removing its data";
} else {
// 用户空间找到了升级文件,说明系统目录下的文件可能被删除了
// 因此,把应用的系统属性去掉,以普通应用的方式运行
msg = "Updated system package + " + deletedAppName
+ " no longer exists; revoking system privileges";
final PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName);
deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM;
}
......
}
// 放到 mExpectingBetter 列表中的应用是带有升级包的
// 在上一阶段解析时把它们从 mPackages 列表中已经移除并放到了 mExpectingBetter 列表中
for (int i = 0; i < mExpectingBetter.size(); i++) {
final String packageName = mExpectingBetter.keyAt(i);
if (!mPackages.containsKey(packageName)) {
final File scanFile = mExpectingBetter.valueAt(i);
final @ParseFlags int reparseFlags;
final @ScanFlags int rescanFlags;
// 从下面路径中查找应用
if (FileUtils.contains(privilegedAppDir, scanFile)) {
.....
} else if (FileUtils.contains(systemAppDir, scanFile)) {
......
} else if (FileUtils.contains(privilegedVendorAppDir, scanFile)
|| FileUtils.contains(privilegedOdmAppDir, scanFile)) {
......
} else if (FileUtils.contains(vendorAppDir, scanFile)
|| FileUtils.contains(odmAppDir, scanFile)) {
......
} else if (FileUtils.contains(oemAppDir, scanFile)) {
......
} else if (FileUtils.contains(privilegedProductAppDir, scanFile)) {
......
} else if (FileUtils.contains(productAppDir, scanFile)) {
......
} else {
// 如果没找到,不做任何处理
continue;
}
mSettings.enableSystemPackageLPw(packageName);
try {
// 扫描并处理该应用
scanPackageTracedLI(scanFile, reparseFlags, rescanFlags, 0, null);
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to parse original system package: " + e.getMessage());
}
}
}
// 此时用户空间的扫描基本都已经完成
// 解压安装 stub system app
decompressSystemApplications(stubSystemApps, scanFlags);
......
}
mExpectingBetter.clear();
// 获取StorageManager包名
mStorageManagerPackage = getStorageManagerPackageName();
// 获取SetupWizard 包名(开机引导)
mSetupWizardPackage = getSetupWizardPackageName();
......
// 更新所有应用的动态库路径
updateAllSharedLibrariesLPw(null);
......
// Now that we know all the packages we are keeping,
// read and update their last usage times.
mPackageUsage.read(mPackages);
mCompilerStats.read();
BOOT_PROGRESS_PMS_SCAN_END
BOOT_PROGRESS_PMS_SCAN_END
阶段的主要进行了:
- 检查
SDK
版本,更新应用权限 - 检查启动条件(首次正常启动、基于
Android M
升级后正常启动)设置默认首选应用 - 获取
UUID_DEFAULT
卷上的核心系统应用包名,并准备相关的应用数据 - 检查是否为OTA后首次启动,是:进行
cache
清理 - 最后,将
mSetting
中的数据更新到packages.xml
中
主要代码如下:
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END, SystemClock.uptimeMillis());
// 如果平台的SDK版本和上次启动时相比发生了变化,可能permission的定义也改变了
// 因此需要重新赋予应用权限
final boolean sdkUpdated = (ver.sdkVersion != mSdkVersion);
mPermissionManager.updateAllPermissions(
StorageManager.UUID_PRIVATE_INTERNAL, sdkUpdated, mPackages.values(),
mPermissionCallback);
ver.sdkVersion = mSdkVersion;
// If this is the first boot or an update from pre-M, and it is a normal
// boot, then we need to initialize the default preferred apps across
// all defined users.
// 根据条件设定默认的首选应用
if (!onlyCore && (mPromoteSystemApps || mFirstBoot)) {
for (UserInfo user : sUserManager.getUsers(true)) {
mSettings.applyDefaultPreferredAppsLPw(this, user.id);
applyFactoryDefaultBrowserLPw(user.id);
primeDomainVerificationsLPw(user.id);
}
}
......
// 获取 UUID_DEFAULT 上的核心系统应用包名集合
List<String> deferPackages = reconcileAppsDataLI(StorageManager.UUID_PRIVATE_INTERNAL, UserHandle.USER_SYSTEM, storageFlags, true /* migrateAppData */, true /* onlyCoreApps */);
// 为deferPackages集合中的应用准备应用数据
mPrepareAppDataFuture = SystemServerInitThreadPool.get().submit(...);
......
// 如果是执行OTA后的首次启动,需要清除cache
if (mIsUpgrade && !onlyCore) {
Slog.i(TAG, "Build fingerprint changed; clearing code caches");
for (int i = 0; i < mSettings.mPackages.size(); i++) {
final PackageSetting ps = mSettings.mPackages.valueAt(i);
if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, ps.volumeUuid)) {
// No apps are running this early, so no need to freeze
clearAppDataLIF(...);
}
}
ver.fingerprint = Build.FINGERPRINT;
}
......
// 把 mSettings 内容保存到 packages.xml
mSettings.writeLPr();
BOOT_PROGRESS_PMS_READY
BOOT_PROGRESS_PMS_READY
阶段内容比较简单,方法内容如下:
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY, SystemClock.uptimeMillis());
......
// 创建 PackageInstallerService
mInstallerService = new PackageInstallerService(context, this);
......
// 获取每个user对应的已安装应用信息,填充到userPackages中
final Map<Integer, List<PackageInfo>> userPackages = new HashMap<>();
final int[] currentUserIds = UserManagerService.getInstance().getUserIds();
for (int userId : currentUserIds) {
userPackages.put(userId, getInstalledPackages(/*flags*/ 0, userId).getList());
}
// 将userPackages添加到 mDexManager 中用来跟踪dex使用信息
mDexManager.load(userPackages);
......
// 触发垃圾回收
Runtime.getRuntime().gc();
......
PMS
构造方法总结
PackageManagerService
构造方法的执行过程就是:
- 先读取
packages.xml
文件内容,解析并保存在成员变量mSettings
中。- 这相当于加载设备上次运行时的应用状态信息
- 然后扫描设备中的几个分区目录下的应用文件,把扫描结果保存到
PMS
的mPackages
中。- 这记录的是当前系统中的应用状态信息
- 后面就是对两次的应用状态信息进行:比对、重新调整、扫描特定目录。
- 最后将应用信息重新写回
packages.xml
文件
调用时序图如下(为了简洁,移除了部分方法):
startOtherServices()
中的PMS
操作
前面已经介绍,在startOtherServices()
中,PMS
还进行了如下三个操作:
mPackageManagerService.updatePackagesIfNeeded()
:检查是否需要更新package
mPackageManagerService.performFstrimIfNeeded()
:执行磁盘清理,释放空间mPackageManagerService.systemReady()
:执行一些默认权限的检查和package
信息更新操作以及进行一些相关服务的状态通知(*.systemReady()
)
我们简单来看下
updatePackagesIfNeeded()
updatePackagesIfNeeded()
用来检查是否需要去更新packages
并进行dex
优化
方法内容如下,注释比较详细:
public void updatePackagesIfNeeded() {
enforceSystemOrRoot("Only the system can request package update");
// We need to re-extract after an OTA.
boolean causeUpgrade = isUpgrade();
// First boot or factory reset.
boolean causeFirstBoot = isFirstBoot() || mIsPreNUpgrade;
// We need to re-extract after a pruned cache, as AoT-ed files will be out of date.
boolean causePrunedCache = VMRuntime.didPruneDalvikCache();
// 如果不是:OTA、首次启动、升级到Android N、虚拟机缓存调整
// 直接返回
if (!causeUpgrade && !causeFirstBoot && !causePrunedCache) {
return;
}
List<PackageParser.Package> pkgs;
synchronized (mPackages) {
// 获取安装的Package集合
pkgs = PackageManagerServiceUtils.getPackagesForDexopt(mPackages.values(), this);
}
final long startTime = System.nanoTime();
// 执行升级操作
final int[] stats = performDexOptUpgrade(pkgs, mIsPreNUpgrade /* showDialog */,
causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT,
false /* bootComplete */);
......
}
真正执行升级的方法是performDexOptUpgrade()
,我们看下:
private int[] performDexOptUpgrade(List<PackageParser.Package> pkgs, ...) {
......
final int numberOfPackagesToDexopt = pkgs.size();
for (PackageParser.Package pkg : pkgs) {
numberOfPackagesVisited++;
boolean useProfileForDexopt = false;
if ((isFirstBoot() || isUpgrade()) && isSystemApp(pkg)) {
// 针对系统app,如果是首次启动或者升级的状态
// 先检测应用的initial preopt profiles是否存在
if (profileFile.exists()) {
......
// 文件存在的话,通过Installer拷贝文件
mInstaller.copySystemProfile(profileFile.getAbsolutePath(), ...);
} else {
// 文件不存在,检查<update-package/>标签中的应用
PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(pkg.packageName);
if (disabledPs != null && disabledPs.pkg.isStub) {
// 如果标签中存在,并且应用类型是Stub,查找特殊路径
String systemProfilePath = getPrebuildProfilePath(disabledPs.pkg).replace(STUB_SUFFIX, "");
profileFile = new File(systemProfilePath);
......
mInstaller.copySystemProfile(profileFile.getAbsolutePath(),...);
}
}
}
if (!PackageDexOptimizer.canOptimizePackage(pkg)) {
......
// 如果应用不支持优化,直接跳过
continue;
}
......
// performDexOptTraced的调用关系及其恶心
// 跟踪代码,最后调用的是
// PackageDexOptimizer 类 dexOptPath 函数
// 函数通过mInstaller.dexopt()函数来进行转化
// mInstaller.dexopt() 通过 Binder 远程调用installd服务
performDexOptTraced(new DexoptOptions(
pkg.packageName,
pkgCompilationReason,
dexoptFlags));
......
}
......
}
performFstrimIfNeeded()
performFstrimIfNeeded()
用来进行磁盘清理工作,相关代码如下:
public void performFstrimIfNeeded() {
enforceSystemOrRoot("Only the system can request fstrim");
// Before everything else, see whether we need to fstrim.
try {
// 获取StorageManagerService对象
IStorageManager sm = PackageHelper.getStorageManager();
if (sm != null) {
boolean doTrim = false;
// 读取fstrim的间隔,默认3天
final long interval = android.provider.Settings.Global.getLong(
mContext.getContentResolver(),
android.provider.Settings.Global.FSTRIM_MANDATORY_INTERVAL,
DEFAULT_MANDATORY_FSTRIM_INTERVAL);
if (interval > 0) {
// 判断间隔
final long timeSinceLast = System.currentTimeMillis() - sm.lastMaintenance();
if (timeSinceLast > interval) {
doTrim = true;
}
}
if (doTrim) {
......
// runMaintenance先通过sm的发送H_FSTRIM消息给Handler
// 然后mVold对象通过binder调用vold进程的fstrim函数
sm.runMaintenance();
}
} else {
Slog.e(TAG, "storageManager service unavailable!");
}
} catch (RemoteException e) {
// Can't happen; StorageManagerService is local
}
}
systemReady()
systemReady()
主要进行的是权限相关的更新操作,以及一些服务状态的变更。
相关代码如下:
@Override
public void systemReady() {
enforceSystemOrRoot("Only the system can claim the system is ready");
mSystemReady = true;
...... // 省略一个和InstantApp相关的 ContentObserver
CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(), this, mContext.getContentResolver(), UserHandle.USER_SYSTEM);
......
// 获取并设置 compatibilityModeEnabled 兼容模式
boolean compatibilityModeEnabled = android.provider.Settings.Global.getInt(
mContext.getContentResolver(),
android.provider.Settings.Global.COMPATIBILITY_MODE, 1) == 1;
PackageParser.setCompatibilityModeEnabled(compatibilityModeEnabled);
......
int[] grantPermissionsUserIds = EMPTY_INT_ARRAY;
synchronized (mPackages) {
// 检查PreferredActivity是否存在,及时清理不存在的
ArrayList<PreferredActivity> removed = new ArrayList<PreferredActivity>();
for (int i=0; i<mSettings.mPreferredActivities.size(); i++) {
......
}
// 检查用户权限,将不是默认权限的userID添加到 grantPermissionsUserIds 集合中
for (int userId : UserManagerService.getInstance().getUserIds()) {
if (!mSettings.areDefaultRuntimePermissionsGrantedLPr(userId)) {
grantPermissionsUserIds = ArrayUtils.appendInt(
grantPermissionsUserIds, userId);
}
}
}
sUserManager.systemReady();
// 设置为默认权限
for (int userId : grantPermissionsUserIds) {
mDefaultPermissionPolicy.grantDefaultPermissions(userId);
}
......
// 更新权限
synchronized (mPackages) {
mPermissionManager.updateAllPermissions(
StorageManager.UUID_PRIVATE_INTERNAL, false, mPackages.values(),
mPermissionCallback);
}
// 发送systemReady通知
if (mPostSystemReadyMessages != null) {
for (Message msg : mPostSystemReadyMessages) {
msg.sendToTarget();
}
mPostSystemReadyMessages = null;
}
// 注册外部储存设备监听
final StorageManager storage = mContext.getSystemService(StorageManager.class);
storage.registerListener(mStorageListener);
// 发送关联服务的 systemReady 通知
mInstallerService.systemReady();
mDexManager.systemReady();
mPackageDexOptimizer.systemReady();
// 获取 StorageManager 服务并添加相关的状态通知策略
StorageManagerInternal StorageManagerInternal = LocalServices.getService(
StorageManagerInternal.class);
StorageManagerInternal.addExternalStoragePolicy(...);
// Now that we're mostly running, clean up stale users and apps
sUserManager.reconcileUsers(StorageManager.UUID_PRIVATE_INTERNAL);
reconcileApps(StorageManager.UUID_PRIVATE_INTERNAL);
// 发送 PermissionManager 的 systemReady 通知
mPermissionManager.systemReady();
......
}
到这里,PMS
的初始化主要过程就梳理完了,整个流程还是比较长的,很多细节做了忽略,接下来我们从三个方面来看下PMS
初始化细节:
permission
文件处理- 应用目录的扫描过程
APK
文件的解析过程
下一篇深入Android系统(十)PMS-2-初始化的一些细节