Android PackageManagerService系列博客目录:
PackageManagerService启动详解系列博客概要
PackageManagerService启动详解(一)之整体流程分析
终于要拉开PKMS启动详解系列博客的序幕了,当然读者最好能先阅读一下PackageManagerService启动详解系列博客概要对我们的整个系列博客有一个整体的了解。在今天的博客中我们将会从整体上来介绍PKMS的启动流程,所以我们只会重点关注PKMS的整个启动流程,让读者先从整体上有一个认识。至于在源码分析中注释的代码内容,读者可以先有一个概括,在后续的博客中我们会采取庖丁解牛的方式逐一分析。
万事开头难,我们只有从整体上对PKMS的启动有了了解,才能在后续对它涉及的相关流程逐一分解,各个突破取得最后的胜利。所以读者不要老想着一下子能吃撑胖子,慢慢来!
为了读者心里有底,我们来个提前亮,先来直接看下PKMS的整体启动图,如下:
注意:本篇的介绍是基于Android 7.xx平台为基础的(并且为了书写简便后续PackageManagerService统一简称为PKMS),其中涉及的代码路径如下:
--- frameworks/base/services/java/com/android/server/SystemServer.java
--- frameworks/base/services/core/java/com/android/server/pm/
Installer.java
PackageDexOptimizer.java
PackageInstallerService.java
PackageManagerService.java
PackageSetting.java
Settings.java
--- frameworks/base/core/java/com/android/server/SystemConfig.java
对Android系统启动流程有一定了解的读者应该知道,Android在启动过程中会主动创建一个system_server的核心进程,然后system_server进程启动之后会创建一系列的Java核心服务(对于这一块还不是很熟悉的读者,可以参见博客Android核心服务和关键进程启动),而这其中就包括我们的PKMS服务,PKMS服务创建成功之后就会常驻system_server进程,通过Binder远程调用的方式对外提供服务。而我们本文的重点就是介绍Android服务中PKMS服务启动过,正所谓知己知彼方能百战百胜,所以我们非常有必要先来看下PKMS服务的相关类图关系,如下:
好了,我们直接来看下SystemServer启动PKMS的代码,如下:
//【 SystemServer.java 】
public final class SystemServer {
private boolean mOnlyCore;//此变量标志是否只加载核心应用
...
private void startBootstrapServices() {
...
//此处的Installer用于和Native层的installd进行socket通信,传递命令
Installer installer = mSystemServiceManager.startService(Installer.class);
String cryptState = SystemProperties.get("vold.decrypt");//判断加密模式
/*
注意Android正常启动模式下,不会进入下面的分支,所以mOnlyCore为false
*/
if (ENCRYPTING_STATE.equals(cryptState)) {
mOnlyCore = true;
} else if (ENCRYPTED_STATE.equals(cryptState)) {
mOnlyCore = true;
} else if (mIsAlarmBoot) {
mOnlyCore = true;
}
...
/*
注意Android正常启动模式下,工厂模式为false
*/
mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
...
}
---
}
我们接着继续来看PMKS的main()静态方法,其源码实现逻辑如下:
//【 PackageManagerService.java 】
public class PackageManagerService extends IPackageManager.Stub {
...
public static PackageManagerService main(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
PackageManagerServiceCompilerMapping.checkProperties();
//构建一个PKMS实例
PackageManagerService m = new PackageManagerService(context, installer,
factoryTest, onlyCore);
m.enableSystemUserPackages();
//将PKMS实例添加到ServiceManager中进行管理
ServiceManager.addService("package", m);
return m;
}
...
}
在分析该方法之前,我们先来简单看下它的四个入参,如下:
入参参数的含义我们搞懂了,我们接着来看main方法,可以看到它的逻辑比较简单,主要干了如下两件事情:
可以看到这里的main方法非常简单,只有短短几行代码,但是它的执行时间却较长!我想对于从事Android系统开发的小伙伴来说,一定有想方设法的缩减这段代码执行的时间而奋战过(缩减开机时间!)。
至于为啥此处简简单单的几行代码要耗费如此多的时间,主要原因是 PKMS在其构造方法中做了很多“重体力活”(主要是扫描应用),这也是 Android 启动速度慢的主要原因之一。
下图是经过优化之后的PKMS开机执行耗时时间图,对于这快的时间通常是越短越好!
通过前面system_server进程的一番操作,PKMS被构建了出来,此时进入它的构造方法中,开始新的征程,在它的构造方法中,主要分为五个阶段,每个阶段通过EventLog打印出来,如下:
//【 PackageManagerService.java 】
public class PackageManagerService extends IPackageManager.Stub {
...
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
//写入EventLog,标志着PKMS启动第一阶段
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START, SystemClock.uptimeMillis());
...
synchronized (mInstallLock) {
synchronized (mPackages) {
...
//写入EventLog,标志着PKMS启动第二阶段
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START, startTime);
...
if (!mOnlyCore) {
//写入EventLog,标志着PKMS启动第三阶段
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START, SystemClock.uptimeMillis());
}
...
//写入EventLog,标志着PKMS启动第四阶段
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END, SystemClock.uptimeMillis());
...
//写入EventLog,标志着PKMS启动第五阶段
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY, SystemClock.uptimeMillis());
...
}
}
Runtime.getRuntime().gc();
LocalServices.addService(PackageManagerInternal.class, new PackageManagerInternalImpl());
}
...
}
可以看到Android系统根据 EventLog将PKMS的初始化分为以下几个阶段:
上面的几个阶段,也就我们前面通过logcat查看的标记信息。我们后续的章节也会以这个EventLog为划分标准,来说明在PKMS构造方法中每个阶段的"历史重任"!
啥也不多说了,翠花上酸菜!跑题了,直接上源码:
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
/************************* PKMS启动第一阶段 *************************/
//写入日志,标识PackageManagerService正式开始第一阶段的启动
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
SystemClock.uptimeMillis());
//SDK版本检测
if (mSdkVersion <= 0) {
Slog.w(TAG, "**** ro.build.version.sdk not set!");
}
mContext = context;
mPermissionReviewRequired = context.getResources().getBoolean(
R.bool.config_permissionReviewRequired);
//开机模式是否为工厂模式,默认为false
mFactoryTest = factoryTest;
mOnlyCore = onlyCore;//默认为false,标记是否只加载核心服务
// 用于存储与显示屏相关的一些属性,例如屏幕的宽 / 高尺寸,分辨率等信息
mMetrics = new DisplayMetrics();
/*
在Settings中,创建packages.xml、packages-backup.xml、packages.list 等文件对象
这个Settings对象非常重要,我们在后续的分析中会多次看到并使用到它
它是Android系统已经安装Package在内存中的数据映射,存储了譬如已安装应用的代码位置,数据位置,签名等信息
*/
mSettings = new Settings(mPackages);
/*
向Settings实例对象添加system, phone, log, nfc, bluetooth, shell这六种shareUserId到mSettings中,
后面扫描和安装有用!
其中的system对应的shareUserId也就是我们经常所说的对应的Android系统权限
*/
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);
mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
//此处具体用途不是很清晰,忽略
File setFile = new File(AlarmManager.POWER_OFF_ALARM_SET_FILE);
File handleFile = new File(AlarmManager.POWER_OFF_ALARM_HANDLE_FILE);
mIsAlarmBoot = SystemProperties.getBoolean("ro.alarm_boot", false);
if (mIsAlarmBoot) {
...
} else if (setFile.exists() && handleFile.exists()) {
...
}
//这块具体用途不是很清晰,应该是和调试进程隔离有关
String separateProcesses = SystemProperties.get("debug.separate_processes");
if (separateProcesses != null && separateProcesses.length() > 0) {
if ("*".equals(separateProcesses)) {
mDefParseFlags = PackageParser.PARSE_IGNORE_PROCESSES;
mSeparateProcesses = null;
Slog.w(TAG, "Running with debug.separate_processes: * (ALL)");
} else {
mDefParseFlags = 0;
mSeparateProcesses = separateProcesses.split(",");
Slog.w(TAG, "Running with debug.separate_processes: "
+ separateProcesses);
}
} else {
mDefParseFlags = 0;
mSeparateProcesses = null;
}
/*
installer在SystemServer中被构造,这里通过该对象与底层installd进行socket通信
进行具体安装与卸载的操作
*/
mInstaller = installer;
//创建PackageDexOptimizer,该类用于辅助进行dex优化
mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,
"*dexopt*");
mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
//创建OnPermissionChangeListeners对象,用于监听权限改变!
mOnPermissionChangeListeners = new OnPermissionChangeListeners(
FgThread.get().getLooper());
getDefaultDisplayMetrics(context, mMetrics);
/*
构建SystemConfig对象实例(单例模式)
它主要用于获取系统配置信息
譬如共享库,系统feather,权限等
*/
SystemConfig systemConfig = SystemConfig.getInstance();
mGlobalGids = systemConfig.getGlobalGids();
mSystemPermissions = systemConfig.getSystemPermissions();
mAvailableFeatures = systemConfig.getAvailableFeatures();
mProtectedPackages = new ProtectedPackages(mContext);
synchronized (mInstallLock) {
// writer
synchronized (mPackages) {
/*
构建并启动Handler处理线程,用于处理应用的安装和卸载相关事件的处理
这里为啥要单独开辟一个线程处理呢,这是因为第三方应用的安装和卸载是一个比较耗时的操作
*/
mHandlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
mHandlerThread.start();
//创建一个 PackageHandler 对象,绑定前面创建的HandlerThread,处理安装和卸载
mHandler = new PackageHandler(mHandlerThread.getLooper());
mProcessLoggingHandler = new ProcessLoggingHandler();
//使用看门狗检测当前消息处理线程,如果超时则触发看门狗机制
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
//创建默认权限管理对象,用于给某些预制的 apk 给予权限,也可以撤销!
mDefaultPermissionPolicy = new DefaultPermissionGrantPolicy(this);
/*
创建需要扫描检测的目录文件对象,为后续扫描做准备!
这里最最常见的就是/data/app/和/data/app-private/目录
*/
File dataDir = Environment.getDataDirectory();
mAppInstallDir = new File(dataDir, "app");
mAppLib32InstallDir = new File(dataDir, "app-lib");
mEphemeralInstallDir = new File(dataDir, "app-ephemeral");
mAsecInternalPath = new File(dataDir, "app-asec").getPath();
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
mRegionalizationAppInstallDir = new File(dataDir, "app-regional");
//构造UserManagerService对象,创建用户管理服务
sUserManager = new UserManagerService(context, this, mPackages);
//读取权限配置文件中的信息,保存到mPermissions这个ArrayMap中
ArrayMap<String, SystemConfig.PermissionEntry> permConfig
= systemConfig.getPermissions();
for (int i=0; i<permConfig.size(); i++) {
SystemConfig.PermissionEntry perm = permConfig.valueAt(i);
BasePermission bp = mSettings.mPermissions.get(perm.name);
if (bp == null) {
bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN);
mSettings.mPermissions.put(perm.name, bp);
}
if (perm.gids != null) {
bp.setGids(perm.gids, perm.perUser);
}
}
//获取并处理所有共享库信息
ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries();
for (int i=0; i<libConfig.size(); i++) {
mSharedLibraries.put(libConfig.keyAt(i),
new SharedLibraryEntry(libConfig.valueAt(i), null));
}
//尝试读取mac_permissions.xml中的selinux信息
mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();
/*
读取文件package.xml的内容,解析后存入mSettings的mPackages中
并且根据解析的结果判断是否是第一次启动,如果是第一次开机,那么是没有相关数据的
*/
mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false));
/*
移除哪些codePath无效的Package(该Pacakge是系统App升级过来的被安装在/data/app分区)
此时需要恢复处于system目录下的同名package
*/
final int packageSettingCount = mSettings.mPackages.size();
for (int i = packageSettingCount - 1; i >= 0; i--) {
PackageSetting ps = mSettings.mPackages.valueAt(i);
if (!isExternal(ps) && (ps.codePath == null || !ps.codePath.exists())
&& mSettings.getDisabledSystemPkgLPr(ps.name) != null) {
mSettings.mPackages.removeAt(i);
mSettings.enableSystemPackageLPw(ps.name);
}
}
if (mFirstBoot) {
/*
如果是第一次开机,从另外一个系统拷贝 odex 文件到当前系统的 data 分区
Android 7.1 引进了 AB 升级,这个是 AB 升级的特性,可以先不看!
*/
requestCopyPreoptedFiles();
}
//判断是否自定义的解析界面(存在多个满足添加的Activiyt,弹出的选择界面的那个)
String customResolverActivity = Resources.getSystem().getString(
R.string.config_customResolverActivity);
if (TextUtils.isEmpty(customResolverActivity)) {
customResolverActivity = null;
} else {
mCustomResolverComponentName = ComponentName.unflattenFromString(
customResolverActivity);
}
//获取扫描开始的时间
long startTime = SystemClock.uptimeMillis();
...
这里可以看到PKMS启动的第一阶段,源码洋洋洒洒就有上百行,不要害怕,不要灰心,更加不要放弃,我们先了解,后熟悉,再掌握,PKMS的知识点终会被我攻破的!
这里我们看到了在PKMS启动的PMS_START阶段,做了非常多的初始化工作,主要是为了后续扫描工作做准备。而这些初始化工作是非常重要也是必要的,这里我们挑出重点的一些初始化来先熟悉熟悉:
初始化阶段干的事情挺多的,这里就不细究了,后续会由专门的篇章来详细分析的。
经过前面阶段的准备,PKMS服务相关的初始化环境已经构建OK,现在要开始真正干活开始系统扫描阶段了:
...//第一阶段相关源码
/************************* PKMS启动第二阶段 *************************/
//写入开始扫描系统应用的日志
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
startTime);
/*
设置扫描的参数,这些参数比较重要,因为后续系统扫描阶段和data分区扫描阶段
调用的是同一套代码,其中的许多逻辑是通过扫描参数来区分调用的
*/
final int scanFlags = SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING | SCAN_INITIAL;
//获取Java启动类库的路径,可以通过echo $BOOTCLASSPATH查看
final String bootClassPath = System.getenv("BOOTCLASSPATH");
//获取systemServer的路径
final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");
if (bootClassPath == null) {
Slog.w(TAG, "No BOOTCLASSPATH found!");
}
if (systemServerClassPath == null) {
Slog.w(TAG, "No SYSTEMSERVERCLASSPATH found!");
}
/*
获得系统指令集合,这取决于当前Android终端是32位还是64位的
通常的指令集有如下几种arm64-v8a,armeabi-v7a,armeabi
*/
final List<String> allInstructionSets = InstructionSets.getAllInstructionSets();
final String[] dexCodeInstructionSets =
getDexCodeInstructionSets(
allInstructionSets.toArray(new String[allInstructionSets.size()]));
/*
确保所有的共享库都被dexopt优化
至于这个共享库有那些要具体以Android终端情况为准,在不同平台有不同情况
*/
if (mSharedLibraries.size() > 0) {
for (String dexCodeInstructionSet : dexCodeInstructionSets) {
for (SharedLibraryEntry libEntry : mSharedLibraries.values()) {
final String lib = libEntry.path;
if (lib == null) {
continue;
}
try {
//判断共享库是否需要执行odex操作
int dexoptNeeded = DexFile.getDexOptNeeded(
lib, dexCodeInstructionSet,
getCompilerFilterForReason(REASON_SHARED_APK),
false /* newProfile */);
// 如果需要odex操作,对共享库进行一次预编译(AOT)
if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
// 调用install的dexopt命令,优化后的文件放在/data/dalvik-cache/下面
mInstaller.dexopt(lib, Process.SYSTEM_UID, dexCodeInstructionSet,
dexoptNeeded, DEXOPT_PUBLIC /*dexFlags*/,
getCompilerFilterForReason(REASON_SHARED_APK),
StorageManager.UUID_PRIVATE_INTERNAL,
SKIP_SHARED_LIBRARY_CHECK);
}
} catch (FileNotFoundException e) {
Slog.w(TAG, "Library not found: " + lib);
} catch (IOException | InstallerException e) {
Slog.w(TAG, "Cannot dexopt " + lib + "; is it an APK or JAR? "
+ e.getMessage());
}
}
}
}
File frameworkDir = new File(Environment.getRootDirectory(), "framework");
//获取系统版本相关信息
final VersionInfo ver = mSettings.getInternalVersion();
/*
判断当前Android终端是否有进行版本OTA升级,如果当前版本的指纹与历史版本的指纹信息不一致
表示当前版本是一次OTA升级上来更新版本(此处指的是版本升级,而不是通常的固件OTA更新)
*/
mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint);
// when upgrading from pre-M, promote system app permissions from install to runtime
/*
对于旧版本升级的情况,将安装时获取权限变更为运行时申请权限
对于Android M之前版本升级上来的情况,需要将系统应用程序权限从安装升级到运行时
*/
mPromoteSystemApps =
mIsUpgrade && ver.sdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1;
/*
判断当前的Android终端的版本是不是从N升级上来的
对于Android N之前版本升级上来的情况,需像首次启动一样处理Package
*/
mIsPreNUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N;
//判断是否从Android 6.0升级上来额
mIsPreNMR1Upgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N_MR1;
/*
在扫描之前保存Android 6.0系统已经安装的软件包的信息,
我们不希望自动授予系统里新的应用程序的运行时权限
*/
if (mPromoteSystemApps) {
Iterator<PackageSetting> pkgSettingIter = mSettings.mPackages.values().iterator();
while (pkgSettingIter.hasNext()) {
PackageSetting ps = pkgSettingIter.next();
if (isSystemApp(ps)) {
mExistingSystemPackages.add(ps.name);
}
}
}
/*
对于包的扫描,执行的是先到先得,先被扫描到的文件,就是最终被用到的文件
当然前提是得经过一定的刷选合适的才可以
*/
// 扫描目录/vendor/overlay下的设备OEM厂商的应用包!
String overlayThemeDir = SystemProperties.get(VENDOR_OVERLAY_THEME_PROPERTY);
if (!overlayThemeDir.isEmpty()) {
scanDirTracedLI(new File(VENDOR_OVERLAY_DIR, overlayThemeDir), mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_TRUSTED_OVERLAY, scanFlags | SCAN_TRUSTED_OVERLAY, 0);
}
scanDirTracedLI(new File(VENDOR_OVERLAY_DIR), mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_TRUSTED_OVERLAY, scanFlags | SCAN_TRUSTED_OVERLAY, 0);
/*
扫描目录/system/framework下的应用包
通常该目录下只有一个被扫描的应用包framework-res.apk
*/
scanDirTracedLI(frameworkDir, mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED,
scanFlags | SCAN_NO_DEX, 0);
// 扫描/system/priv-app下的应用包
final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
scanDirTracedLI(privilegedAppDir, mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);
// 扫描/system/app下面的应用包
final File systemAppDir = new File(Environment.getRootDirectory(), "app");
scanDirTracedLI(systemAppDir, mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
// 扫描/vendor/app目录下的应用包
File vendorAppDir = new File("/vendor/app");
try {
vendorAppDir = vendorAppDir.getCanonicalFile();
} catch (IOException e) {
}
scanDirTracedLI(vendorAppDir, mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
// 扫描/oem/app目录下的应用包
final File oemAppDir = new File(Environment.getOemDirectory(), "app");
scanDirTracedLI(oemAppDir, mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
// 扫描运营商的资源包中收集所有区域化软件包,我们忽略
if (RegionalizationEnvironment.isSupported()) {
...
}
/*
删除任何已经不再存在的系统包
这类List表示的是有可能有升级包的系统应用,注意是可能,所以还需要判断一番
*/
final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<String>();
if (!mOnlyCore) {
//正常启动会走入该分支
// 遍历上一次安装的信息!
Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
while (psit.hasNext()) {
PackageSetting ps = psit.next();
//忽略掉非系统应用,即这个逻辑最对系统应用进行处理
if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
continue;
}
/*
注意这里的mPackages保存的是前面调用scanDirLI方法扫描目录得到的应用信息
不要和mSettings.mPackages弄混了。它保存的是前面一次启动扫描完成之后的应用安装信息
*/
final PackageParser.Package scannedPkg = mPackages.get(ps.name);
if (scannedPkg != null) {
/*
如果该被扫描的应用存在于Settings的实例的disable列表中,那么,
说明它是有通过OTA方式进行升级更新添加的,因此,清除相应数据以便后续覆盖安装在data分区中的
能有机会扫描到
说明一定是通过覆盖更新的,移除之前扫描的结果,保证之前用户安装的应用能够被扫描!
“disable” 列表是package.xml中标签标示的应用
*/
if (mSettings.isDisabledSystemPackageLPr(ps.name)) {
logCriticalInfo(Log.WARN, "Expecting better updated system app for "
+ ps.name + "; removing system app. Last known codePath="
+ ps.codePathString + ", installStatus=" + ps.installStatus
+ ", versionCode=" + ps.versionCode + "; scanned versionCode="
+ scannedPkg.mVersionCode);
// 从扫描列表mPackages中移除
removePackageLI(scannedPkg, true);
// 放入mExpectingBetter列表,后面会进行处理的。
mExpectingBetter.put(ps.name, ps.codePath);
}
//跳出循环,确保不会被删除
continue;
}
/*
注意此处被执行的前提是scannedPkg为null,即当前在系统中没有被扫描到
运行到这里说明ps表示的应用不在被扫描列表mPackages中,也就是在系统中不存在
出现这种情况很大可能是人为在root之后删除了,后者通过OTA升级把系统目录下某个应用干掉了
*/
if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {
psit.remove();
logCriticalInfo(Log.WARN, "System package " + ps.name
+ " no longer exists; it's data will be wiped");
} else {
/*
如果这个系统应用当前已经不在系统中,但是却在被标记为的不可用列表中
则将其加入到possiblyDeletedUpdatedSystemApps列表中待扫描完普通应用目录再进行处理
出现这种情况很大可能是人为在root之后删除了,后者通过OTA升级把系统目录下某个应用干掉了
*/
final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps.name);
if (disabledPs.codePath == null || !disabledPs.codePath.exists()) {
possiblyDeletedUpdatedSystemApps.add(ps.name);
}
}
}
}
/*
清理掉所有安装不完全的应用包
至于为啥安装不完全,这个可能的原因刘比较伛了
*/
ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackagesLPr();
for (int i = 0; i < deletePkgsList.size(); i++) {
final String packageName = deletePkgsList.get(i).name;
logCriticalInfo(Log.WARN, "Cleaning up incompletely installed app: " + packageName);
synchronized (mPackages) {
mSettings.removePackageLPw(packageName);
}
}
//delete tmp files
// 删除临时文件
deleteTempPackageFiles();
// Remove any shared userIDs that have no associated packages
// 删除掉Settings中的没有关联任何应用的SharedUserSetting对象
mSettings.pruneSharedUsersLPw();
到这里PKMS启动的第二阶段PMS_SYSTEM_SCAN_START到这里就告一段落了,对于最后对扫描完成之后的对扫描结果的一些处理,读者朋友不要强硬的去理解其中的逻辑处理,一定要搞清楚系统应用的升级方法,和对已安装应用数据的存储的packages.xml的构成。
此处没有搞明白没有关系,不要灰心,相信自己。并且后续会由专门章节来分析的。
PKMS启动的第二阶段PMS_SYSTEM_SCAN_START我们大致过了下,我们来简单总结一下该阶段的主要流程如下:
这里我们可以在Android终端中通过执行env或者echo $var进行查看相关的取值,并且我有写过专门的一篇博客来介绍可以详见博客Android获取和设置系统环境变量指南。
至于我们当前终端有那些共享库,我们也可以在终端下执行shell命令进行查看,这个每个平台,每个芯片厂商都会有所不同,根据具体情况而定。
被优化之后的相关jar包和共享库
判断当前Android版本是否是通过OTA升级来的,如果是从7.0之前版本升级过来的,需要对之前安装的应用做一些特殊处理,这一块主要是涉及到运行时权限有关
正式开始扫描系统级应用目录,这主要包括如下几个目录:
扫描指定目录下的apk文件,这个是PKMS的三大核心功能,它会通过调用PackageParser来完成对应用App中Apk的AndroidManifest.xml的文件的解析,生成 Application,activity,service,broadcast,provider等四大组件信息,并将上述解析得到的四大组件信息注册到PKMS中,供Android系统查询并使用。
这里可能存在如情况,就是升级完成的app被异常删除了,比如root之后手动删除了,或者其它的异常情况。网上有些说可能是OTA格式化了data分区,这个很明显不成立,如果都清除了data分区,Settings实例怎么可能从data分区中读取packages.xml的信息呢。
这里可能存在如下情况:
1.就是系统应用覆盖升级完成后,系统内最初的的app应用被删除了,比如root之后手动删除了
2.就是系统应用覆盖升级完成后,然后OTA升级固件,新的固件将该系统app删除了
前面战场清扫完毕,现在开始进入PKMS的PMS_DATA_SCAN_START阶段。马上投入战斗,战斗模式开启:
...//PKMS第二阶段启动相关代码
//开始处理非系统应用
if (!mOnlyCore) {
//Android正常启动模式下,会进入此分支
//写入相关阶段启动日志
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
SystemClock.uptimeMillis());
/*
扫描/data目录下相关的第三方应用安装目录
这里重点要注意扫描参数有发生了变化,在后续的扫描方法中会根据这个参数分贝进行不
同的处理逻辑
*/
scanDirTracedLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
scanDirTracedLI(mDrmAppPrivateInstallDir, mDefParseFlags
| PackageParser.PARSE_FORWARD_LOCK,
scanFlags | SCAN_REQUIRE_KNOWN, 0);
scanDirLI(mEphemeralInstallDir, mDefParseFlags
| PackageParser.PARSE_IS_EPHEMERAL,
scanFlags | SCAN_REQUIRE_KNOWN, 0);
// Collect all Regionalization 3rd packages.
if (RegionalizationEnvironment.isSupported()) {
Log.d(TAG, "Load Regionalization 3rd apks from res packages.");
final List<String> packages = RegionalizationEnvironment.getAllPackageNames();
for (String pack : packages) {
File appFolder = new File(mRegionalizationAppInstallDir, pack);
Log.d(TAG, "Load Regionalization 3rd apks of path " + appFolder.getPath());
scanDirLI(appFolder, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
}
}
/*
进行最后的data分区扫描的收尾工作
放在possiblyDeletedUpdatedSystemApps中的应用是在packge.xml中被标记成了禁用的已经升级了的系统应用
但是前面在扫描系统目录下却发现文件却不存在了,因此这里检查用户目录下升级文件是否还存在,然后进行处理
*/
for (String deletedAppName : possiblyDeletedUpdatedSystemApps) {
PackageParser.Package deletedPkg = mPackages.get(deletedAppName);
// 从mSettings.mDisabledSysPackages变量中移除去此应用
mSettings.removeDisabledSystemPackageLPw(deletedAppName);
String msg;
if (deletedPkg == null) {
/*
用户目录中也没有升级包,则肯定是残留的应用信息,则把它的数据目录删除掉
此时无任何扫描结果,表明这个系统中已经没有改apk了,那就删掉它
*/
msg = "Updated system package " + deletedAppName
+ " no longer exists; it's data will be wiped";
} else {
/*
此时表明系统App,覆盖安装的应用在data下面还存在,但是因为系统中的应用不存在了,所以
此时的data分区的应用应该要降级成为普通的第三方应用
*/
msg = "Updated system app + " + deletedAppName
+ " no longer present; removing system privileges for "
+ deletedAppName;
deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName);
deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM;
}
//报告系统发生了不一致的情况
logCriticalInfo(Log.WARN, msg);
}
/**
确保所有在用户data分区的应用都显示出来了,
如果data分区的无法显示,就显示system分区的
现在来处理mExpectingBetter列表,这个列表的应用是带有升级包的系统的应用,
前面把他们从mPackages列表中清除了并放到mExpectingBetter列表
最后也对它们进行扫描处理
*/
for (int i = 0; i < mExpectingBetter.size(); i++) {
final String packageName = mExpectingBetter.keyAt(i);
/*
如果PMS仍然没有扫描到mExpectingBetter列表中的apk,说明data分区的apk无法显示
出现这种情况的原因,可能是由于OTA或者异常导致data分区的覆盖安装的应用已经丢失了
那就要显示原来system分区的apk!
*/
if (!mPackages.containsKey(packageName)) {
final File scanFile = mExpectingBetter.valueAt(i);
logCriticalInfo(Log.WARN, "Expected better " + packageName
+ " but never showed up; reverting to system");
int reparseFlags = mDefParseFlags;
//确保应用位于下面几个系统应用目录,如果不在,则不需要处理
if (FileUtils.contains(privilegedAppDir, scanFile)) {
reparseFlags = PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED;
} else if (FileUtils.contains(systemAppDir, scanFile)) {
reparseFlags = PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR;
} else if (FileUtils.contains(vendorAppDir, scanFile)) {
reparseFlags = PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR;
} else if (FileUtils.contains(oemAppDir, scanFile)) {
reparseFlags = PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR;
} else {
Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);
continue;
}
/*
现在把这个apk标示为系统应用,从mSettings.mDisabledSysPackages中删除,
因为在scanDirLI->scanPackageLI中会执行mSettings.disableSystemPackageLPw
所以此时包名的标签是只有,执行到这步之后变成标签,
*/
mSettings.enableSystemPackageLPw(packageName);
try {
// 重新扫描下系统分区的该应用
scanPackageTracedLI(scanFile, reparseFlags, scanFlags, 0, null);
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to parse original system package: "
+ e.getMessage());
}
}
}
}
//清空mExpectingBetter列表
mExpectingBetter.clear();
// 获得存储管理对象!
mStorageManagerPackage = getStorageManagerPackageName();
// 获得开机向导应用
mSetupWizardPackage = getSetupWizardPackageName();
if (mProtectedFilters.size() > 0) {
if (DEBUG_FILTERS && mSetupWizardPackage == null) {
Slog.i(TAG, "No setup wizard;"
+ " All protected intents capped to priority 0");
}
for (ActivityIntentInfo filter : mProtectedFilters) {
if (filter.activity.info.packageName.equals(mSetupWizardPackage)) {
if (DEBUG_FILTERS) {
Slog.i(TAG, "Found setup wizard;"
+ " allow priority " + filter.getPriority() + ";"
+ " package: " + filter.activity.info.packageName
+ " activity: " + filter.activity.className
+ " priority: " + filter.getPriority());
}
// skip setup wizard; allow it to keep the high priority filter
continue;
}
Slog.w(TAG, "Protected action; cap priority to 0;"
+ " package: " + filter.activity.info.packageName
+ " activity: " + filter.activity.className
+ " origPrio: " + filter.getPriority());
filter.setPriority(0);
}
}
mDeferProtectedFilters = false;
mProtectedFilters.clear();
// 更新所有应用的动态库路径,保证他们有正确的共享库路径
updateAllSharedLibrariesLPw();
// 调整所有共享uid的package的指令集!
for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) {
adjustCpuAbisForSharedUserLPw(setting.packages, null /* scanned package */,
false /* boot complete */);
}
// 到这里,系统中所有的package都被扫描刀了,这里是更新他们上一次的扫描的相关信息
mPackageUsage.read(mPackages);
mCompilerStats.read();
到这里PKMS启动的第三阶段PMS_DATA_SCAN_START到这里就告一段落了,对于最后对扫描完成之后的对扫描结果的一些处理,读者朋友不要强硬的去理解其中的逻辑处理,一定要搞清楚系统应用的升级方法,和对已安装应用数据的存储的packages.xml的构成。可以看到对于系统升级应用的处理PKMS是特别有耐心的。
此处没有搞明白没有关系,不要灰心,相信自己。并且后续会由专门章节来分析的。
PKMS启动的第三阶段PMS_DATA_SCAN_START我们大致过了下,我们来简单总结一下该阶段的主要流程如下:
扫描非系统应用分区目录下的应用,主要包括如下几个目录:
在系统应用和非系统应用扫描完成之后,会最后来统一处理前面一次扫描的系统App不存在的情况,或者前面一次扫描系统App存在覆盖升级安装在data目录下的应用,但是这次扫描没有存在则需要重新解析system分区的package
这里可以看到对于系统应用被删除,或者被覆盖升级的情况,PKMS处理起来是非常谨慎和细致的,这个也是刚开始读者学习的时候让人有点迷糊的地方。
PKMS的PMS_DATA_SCAN_START阶段战役结束,我们一鼓作气,不做耽搁,还有三十秒抵达新的战场开启PKMS的BOOT_PROGRESS_PMS_SCAN_END阶段工作,啥也不多说了直接抄起家伙开干!
...//PKMS启动前面阶段相关源码
//写入关键日志,表明PKMS执行到了什么阶段
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
SystemClock.uptimeMillis());
/*
输出扫描总共消耗时间,很多Android人为了这个时间段呕心沥血
*/
Slog.i(TAG, "Time to scan packages: "
+ ((SystemClock.uptimeMillis()-startTime)/1000f)
+ " seconds");
/*
如果平台的SDK版本和上次启动时候发生了变化,可能permission的定义也发生了变化,因此需要重新赋予应用权限
这里会有一些安全问题,就是可能会有应用通过这种方式获取那些用户没有显式允许的权限
这里可能后续google会改善,说不定是Android的小哥哥是为了后续的API考核特意留下来的工作量呢
*/
int updateFlags = UPDATE_PERMISSIONS_ALL;
if (ver.sdkVersion != mSdkVersion) {
Slog.i(TAG, "Platform changed from " + ver.sdkVersion + " to "
+ mSdkVersion + "; regranting permissions for internal storage");
updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;
}
//更新系统中的权限和权限树,移除无效的权限和权限树,同时更新应用的权限授予情况
updatePermissionsLPw(null, null, StorageManager.UUID_PRIVATE_INTERNAL, updateFlags);
ver.sdkVersion = mSdkVersion;
/*
如果这是第一次开机或从Anroid M之前的版本升级上来的,然后我们需要初始化默认应用程序给所有的系统用户
*/
if (!onlyCore && (mPromoteSystemApps || mFirstBoot)) {
for (UserInfo user : sUserManager.getUsers(true)) {
mSettings.applyDefaultPreferredAppsLPw(this, user.id);
applyFactoryDefaultBrowserLPw(user.id);
primeDomainVerificationsLPw(user.id);
}
}
// 在启动完成前,为系统用户准备文件存储,因为很多核心的系统比如设置,系统界面等等会提前启动!
final int storageFlags;
/*
StorageManager.isFileEncryptedNativeOrEmulated()
方法用于判断系统是否运行在文件加密模式,
如果是文件加密模式的话,storageFlags只有StorageManager.FLAG_STORAGE_DE
*/
if (StorageManager.isFileEncryptedNativeOrEmulated()) {
storageFlags = StorageManager.FLAG_STORAGE_DE;
} else {
storageFlags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
}
/*
准备数据目录,这个地方也是有故事的
当我们的Android终端是第一次开机的时候,扫描完成系统应用之后,肯定系统应用是没有data数据目录的
所以此时该方法就派上用场了,会创建相关的data数据目录
*/
reconcileAppsDataLI(StorageManager.UUID_PRIVATE_INTERNAL, UserHandle.USER_SYSTEM,
storageFlags);
/*
如果是执行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(ps.pkg, UserHandle.USER_ALL,
StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE
| Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
}
}
ver.fingerprint = Build.FINGERPRINT;
}
//检查默认的浏览器
checkDefaultBrowser();
// 当权限和其他默认设置被更新后,执行清除操作
mExistingSystemPackages.clear();
mPromoteSystemApps = false;
// 更新系统数据库版本号!
ver.databaseVersion = Settings.CURRENT_DATABASE_VERSION;
// 把Settings的内容保存到packages.xml中去
mSettings.writeLPr();
// 如果是第一次开机,或者是系统升级,对核心的系统应用执行odex操作!
if ((isFirstBoot() || isUpgrade() || VMRuntime.didPruneDalvikCache()) && !onlyCore) {
long start = System.nanoTime();
List<PackageParser.Package> coreApps = new ArrayList<>();
for (PackageParser.Package pkg : mPackages.values()) {
if (pkg.coreApp) {
coreApps.add(pkg);
}
}
int[] stats = performDexOptUpgrade(coreApps, false,
getCompilerFilterForReason(REASON_CORE_APP));
final int elapsedTimeSeconds =
(int) TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - start);
MetricsLogger.histogram(mContext, "opt_coreapps_time_s", elapsedTimeSeconds);
if (DEBUG_DEXOPT) {
Slog.i(TAG, "Dex-opt core apps took : " + elapsedTimeSeconds + " seconds (" +
stats[0] + ", " + stats[1] + ", " + stats[2] + ")");
}
}
这里我们可以看到在PKMS的BOOT_PROGRESS_PMS_SCAN_END阶段主要是对扫描结束之做一些收尾工作,其大致的相关逻辑如下:
这里的应用数据目录,即/data/data/xxx目录,这个地方读者注意一下即可。
真心肝不动了,先让我缓缓!PKMS的启动的最后一个征程BOOT_PROGRESS_PMS_READY阶段,完成此阶段意味着PKMS向Android世界宣告我已经准备好了,有本事放开那女孩冲我来好了。我们来看下这个阶段,它干了些啥:
...//前面阶段源码
//写入PKMS启动阶段关键日志
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
SystemClock.uptimeMillis());
if (!mOnlyCore) {
mRequiredVerifierPackage = getRequiredButNotReallyRequiredVerifierLPr();
mRequiredInstallerPackage = getRequiredInstallerLPr();
mRequiredUninstallerPackage = getRequiredUninstallerLPr();
mIntentFilterVerifierComponent = getIntentFilterVerifierComponentNameLPr();
mIntentFilterVerifier = new IntentVerifierProxy(mContext,
mIntentFilterVerifierComponent);
mServicesSystemSharedLibraryPackageName = getRequiredSharedLibraryLPr(
PackageManager.SYSTEM_SHARED_LIBRARY_SERVICES);
mSharedSystemSharedLibraryPackageName = getRequiredSharedLibraryLPr(
PackageManager.SYSTEM_SHARED_LIBRARY_SHARED);
} else {
mRequiredVerifierPackage = null;
if (mOnlyPowerOffAlarm) {
mRequiredInstallerPackage = getRequiredInstallerLPr();
} else {
mRequiredInstallerPackage = null;
}
mRequiredUninstallerPackage = null;
mIntentFilterVerifierComponent = null;
mIntentFilterVerifier = null;
mServicesSystemSharedLibraryPackageName = null;
mSharedSystemSharedLibraryPackageName = null;
}
//创建PackageInstallerService对象实例,这个对象主要用于第三方应用安装app使用
mInstallerService = new PackageInstallerService(context, this);
final ComponentName ephemeralResolverComponent = getEphemeralResolverLPr();
final ComponentName ephemeralInstallerComponent = getEphemeralInstallerLPr();
if (ephemeralInstallerComponent != null && ephemeralResolverComponent != null) {
if (DEBUG_EPHEMERAL) {
Slog.i(TAG, "Ephemeral activated; resolver: " + ephemeralResolverComponent
+ " installer:" + ephemeralInstallerComponent);
}
mEphemeralResolverComponent = ephemeralResolverComponent;
mEphemeralInstallerComponent = ephemeralInstallerComponent;
setUpEphemeralInstallerActivityLP(mEphemeralInstallerComponent);
mEphemeralResolverConnection =
new EphemeralResolverConnection(mContext, mEphemeralResolverComponent);
} else {
if (DEBUG_EPHEMERAL) {
final String missingComponent =
(ephemeralResolverComponent == null)
? (ephemeralInstallerComponent == null)
? "resolver and installer"
: "resolver"
: "installer";
Slog.i(TAG, "Ephemeral deactivated; missing " + missingComponent);
}
mEphemeralResolverComponent = null;
mEphemeralInstallerComponent = null;
mEphemeralResolverConnection = null;
}
mEphemeralApplicationRegistry = new EphemeralApplicationRegistry(this);
} // synchronized (mPackages)
} // synchronized (mInstallLock)
//进行资源回收
Runtime.getRuntime().gc();
mInstaller.setWarnIfHeld(mPackages);
//添加一个本地服务,供system_server进程内部使用
LocalServices.addService(PackageManagerInternal.class, new PackageManagerInternalImpl());
在该阶段关键性工作不多,主要是一些扫尾工作,但是在这里创建了一个比较重要的匿名Binder服务对象PackageInstallerService实例,它主要用于第三方应用安装app使用,这个会在后续第三方应用安装的流程中分析到。
至此PKMS的启动阶段我们就简单的介绍完毕了,读者可能发现了虽然这里只是简简单单的概括了一下,就会发现它的工作量之巨大。不过读者也不要灰心,慢慢来,一个阶段一个阶段的开垦,相信自己总会拿下相关山头的。这里我们对PKMS启动各个阶段就不总结了,因为前面已经在各个章节的最后有总结了一下,虽然在本篇博客中我们没有深入各个阶段的源码详细研究但是我们已经对整体流程有了一个整体的把握,后续就是一些的研究了。正是通过PKMS在启动阶段的工作,Android终端开机开机以后才能从砖头进入丰富多彩的Android世界,然后PKMS才能向Android世界提供各个应用的相关信息,应用的四大组件才能被Android世界所知悉并且使用,而这些也奥秘也是我们在后续博客中会一一揭晓的。
好了,到这里PackageManagerService启动详解(一)之整体流程分析就告一段落了,各位青山不改绿水长流,各位江湖见!当然各位读者的点赞和关注是我写作路上前进的最大动力了,如果有啥不对或者不爽的也可以踩一踩也无妨!