???PackageManagerService啟動詳解(一)之整體流程分析
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
一.system_server行程啟動PKMS服務
??對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;
}
...
}
在分析該方法之前,我們先來簡單看下它的四個入參,如下:
- context:表示system_server行程的背景關系資訊
- installer:Installer的實體物件,主要用于和Native層的installd進行socket通信,傳遞命令
- factoryTest:表示是否是工廠測驗模式,在Android正常啟動模式下此值為false
- onlyCore:表示是否只加載系統核心應用,在Android正常啟動模式下此值也為false
入參引數的含義我們搞懂了,我們接著來看main方法,可以看到它的邏輯比較簡單,主要干了如下兩件事情:
- 通過main()傳入的引數構建PKMS物件實體
- 將前面創建的PKMS物件實體添加到ServiceManager進行管理,這樣第三方應用就可以通過Binder呼叫PKMS服務了
可以看到這里的main方法非常簡單,只有短短幾行代碼,但是它的執行時間卻較長!我想對于從事Android系統開發的小伙伴來說,一定有想方設法的縮減這段代碼執行的時間而奮戰過(縮減開機時間!),
至于為啥此處簡簡單單的幾行代碼要耗費如此多的時間,主要原因是 PKMS在其構造方法中做了很多“重體力活”(主要是掃描應用),這也是 Android 啟動速度慢的主要原因之一,
下圖是經過優化之后的PKMS開機執行耗時時間圖,對于這快的時間通常是越短越好!
二.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的初始化分為以下幾個階段:
- 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啟動階段
上面的幾個階段,也就我們前面通過logcat查看的標記資訊,我們后續的章節也會以這個EventLog為劃分標準,來說明在PKMS構造方法中每個階段的"歷史重任"!
三.PKMS的啟動PMS_START階段
啥也不多說了,翠花上酸菜!跑題了,直接上原始碼:
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階段,做了非常多的初始化作業,主要是為了后續掃描作業做準備,而這些初始化作業是非常重要也是必要的,這里我們挑出重點的一些初始化來先熟悉熟悉:
- 首先Settings實體物件,該實體物件非常重要,它主要用來記錄上次Android終端已安裝應用相關資訊的一個管理類,如果Android終端設備是第一次啟動(如果沒有發生例外的情況)則會創建packages.xml、packages-backup.xml、packages.list等檔案,同時添加 system, phone, log, nfc, bluetooth, shell 這六種 shareUserId 到mSettings中進行管理,該物件被初始化成功之后,后面的掃描安裝都會被用到,如果應用的情況(即有新的應用安裝,卸載,升級)發生變化,該物件也會進行相應的更新
- 初始mInstaller物件,該實體物件主要用于和Native層的installed通過socket進行互動,處理應用的安裝和卸載
- 創建 PackageDexOptimizer物件,該物件主要用于對應用進行相關的dex優化操作
- 創建OnPermissionChangeListeners物件,該物件主要用于監聽權限改變
- 創建SystemConfig物件實體,它主要用于獲取系統配置資訊,譬如共享庫,系統feather,權限等
- 構建一個ServiceThread物件實體以及關聯的PackageHandler實體物件,用來處理應用的安裝和卸載相應事物的處理
- 創建用戶管理服務UserManagerService實體物件
- 接著通過前面創建的Settings物件實體和SystemConfig物件實體處理已安裝應用的相關權限資訊
- 呼叫SystemConfig實體物件相關資訊構建系統共享庫
初始化階段干的事情挺多的,這里就不細究了,后續會由專門的篇章來詳細分析的,
四.PKMS的啟動PMS_SYSTEM_SCAN_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中<update-package>標簽標示的應用
*/
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 {
/*
如果這個系統應用當前已經不在系統中,但是卻在被標記為<update-package>的不可用串列中
則將其加入到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我們大致過了下,我們來簡單總結一下該階段的主要流程如下:
- 首先是獲取BOOTCLASSPATH對應的Java啟動類別庫的路徑和以及SYSTEMSERVERCLASSPATH對應的systemServer路徑
這里我們可以在Android終端中通過執行env或者echo $var進行查看相關的取值,并且我有寫過專門的一篇博客來介紹可以詳見博客Android獲取和設定系統環境變數指南,
- 接著獲取當前Android系統當前支持的系統指令集,至于當前你的Android系統支持那些,這個得根據具體的情況來確定,譬如32位機器,還是64位機器等,
我們可以通過shell命令進行簡單查看,當前Android終端支持那些相關的abi指令集,如下:
- 對第一階段從SystemConfig實體物件中回傳的共享庫,進行dex優化操作,優化操作之后額共享庫會在/data/dalvik-cache之下
至于我們當前終端有那些共享庫,我們也可以在終端下執行shell命令進行查看,這個每個平臺,每個芯片廠商都會有所不同,根據具體情況而定,
被優化之后的相關jar包和共享庫
-
判斷當前Android版本是否是通過OTA升級來的,如果是從7.0之前版本升級過來的,需要對之前安裝的應用做一些特殊處理,這一塊主要是涉及到運行時權限有關
-
正式開始掃描系統級應用目錄,這主要包括如下幾個目錄:
- /vendor/overlay
- /system/framework
- /system/priv-app
- /system/app
- /vendor/app
- /oem/app
掃描指定目錄下的apk檔案,這個是PKMS的三大核心功能,它會通過呼叫PackageParser來完成對應用App中Apk的AndroidManifest.xml的檔案的決議,生成 Application,activity,service,broadcast,provider等四大組件資訊,并將上述決議得到的四大組件資訊注冊到PKMS中,供Android系統查詢并使用,
- 對掃描到的系統App,并且存在升級情況的App做特殊處理,暫時將其從當前掃描串列中洗掉,放入mExpectingBetter中,待掃描完非系統應用安裝目錄后再進行處理
這里可能存在如情況,就是升級完成的app被例外洗掉了,比如root之后手動洗掉了,或者其它的例外情況,網上有些說可能是OTA格式化了data磁區,這個很明顯不成立,如果都清除了data磁區,Settings實體怎么可能從data磁區中讀取packages.xml的資訊呢,
- 清除那些已經不存在的系統應用包相關資訊,為后續掃描/data/app目錄準備
這里可能存在如下情況:
1.就是系統應用覆寫升級完成后,系統內最初的的app應用被洗掉了,比如root之后手動洗掉了
2.就是系統應用覆寫升級完成后,然后OTA升級韌體,新的韌體將該系統app洗掉了
- 清理所有安裝不完全的應用包
- 移除臨時檔案
- 移除沒有和應用程式包相關聯的共享用戶id
五.PKMS的啟動PMS_DATA_SCAN_START階段
前面戰場清掃完畢,現在開始進入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
所以此時包名的標簽是只有<update-package>,執行到這步之后變成<package>標簽,
*/
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我們大致過了下,我們來簡單總結一下該階段的主要流程如下:
-
掃描非系統應用磁區目錄下的應用,主要包括如下幾個目錄:
- /data/app
- /data/app-asec
- /data/app-ephemeral
- /data/app-private
-
在系統應用和非系統應用掃描完成之后,會最后來統一處理前面一次掃描的系統App不存在的情況,或者前面一次掃描系統App存在覆寫升級安裝在data目錄下的應用,但是這次掃描沒有存在則需要重新決議system磁區的package
這里可以看到對于系統應用被洗掉,或者被覆寫升級的情況,PKMS處理起來是非常謹慎和細致的,這個也是剛開始讀者學習的時候讓人有點迷糊的地方,
- 更新所有應用的動態庫路徑,保證他們有正確的共享庫路徑
- 調整所有共享uid 的package的指令集
- 系統中所有的package都被掃描刀了,最后是更新上一次的掃描的相關資訊,即將最新的掃描結果寫入packages.xml中
六.PKMS的啟動BOOT_PROGRESS_PMS_SCAN_END階段
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階段主要是對掃描結束之做一些收尾作業,其大致的相關邏輯如下:
- 如果當前的Android版本是通過OTA升級上來的,需要對相關的應用的權限做一些動態調整
- 做一些相關的清理作業,并把最新的Settings的內容保存到packages.xml中去
- 呼叫reconcileAppsDataLI()方法處理應用資料目錄,假如被掃描的應用沒有對應的應用資料目錄,則會進行創建和處理
這里的應用資料目錄,即/data/data/xxx目錄,這個地方讀者注意一下即可,
- 如果當前的Android終端是第一次開機,對Android核心的應用做odex的優化處理
七.PKMS的啟動BOOT_PROGRESS_PMS_READY階段
真心肝不動了,先讓我緩緩!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啟動詳解(一)之整體流程分析就告一段落了,各位青山不改綠水長流,各位江湖見!當然各位讀者的點贊和關注是我寫作路上前進的最大動力了,如果有啥不對或者不爽的也可以踩一踩也無妨!
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/254093.html
標籤:其他





