PackageManagerService 原始碼解析
一.SystemServer建立PackageManagerService
省略
二.PackageManagerService 建構函式
2.1 Settings
mSettings = new Settings(mPackages); 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);
1.建立Settings 物件;
2.呼叫mSettings.addSharedUserLPw新增 shared_user_id :
6種系統的uid:system radio log nfc bluetooth shell。相同 shareUserId的包可以執行在一個程序中。
Settings類在data目錄下建立了system目錄,然後分別儲存了下面檔案的控制代碼(注意: Setting初始化時並未讀取packages.xml 等檔案中的資訊)
1.packages.xml :記錄系統中所有安裝的應用的資訊:
<package name="com.android.providers.media" codePath="/system/priv-app/MediaProvider" nativeLibraryPath="/system/priv-app/MediaProvider/lib" publicFlags="944291397" privateFlags="8" ft="15659d595e8" it="15659d595e8" ut="15659d595e8" version="800" sharedUserId="10006"> <sigs count="1"> <cert index="2" key="308204a830820390a003020102020900f2b98e6123572c4e300d06092a864886f70d0101040500308194310b3009060355040613025553311330110603550408130a43616c69666f726e6961311630140603550407130d4d6f756e7461696e20566965773110300e060355040a1307416e64726f6964311 </sigs> <perms> <item name="android.permission.ACCESS_CACHE_FILESYSTEM" granted="true" flags="0" /> <item name="android.permission.WRITE_SETTINGS" granted="true" flags="0" /> <item name="android.permission.SEND_DOWNLOAD_COMPLETED_INTENTS" granted="true" flags="0" /> <item name="android.permission.RECEIVE_BOOT_COMPLETED" granted="true" flags="0" /> <item name="android.permission.WRITE_MEDIA_STORAGE" granted="true" flags="0" /> <item name="android.permission.INTERNET" granted="true" flags="0" /> <item name="android.permission.UPDATE_DEVICE_STATS" granted="true" flags="0" /> <item name="android.permission.ACCESS_ALL_DOWNLOADS" granted="true" flags="0" /> <item name="android.permission.ACCESS_DOWNLOAD_MANAGER" granted="true" flags="0" /> <item name="android.permission.MANAGE_USERS" granted="true" flags="0" /> <item name="android.permission.ACCESS_NETWORK_STATE" granted="true" flags="0" /> <item name="android.permission.ACCESS_MTP" granted="true" flags="0" /> <item name="android.permission.INTERACT_ACROSS_USERS" granted="true" flags="0" /> <item name="android.permission.CLEAR_APP_CACHE" granted="true" flags="0" /> <item name="android.permission.CONNECTIVITY_INTERNAL" granted="true" flags="0" /> <item name="android.permission.MODIFY_NETWORK_ACCOUNTING" granted="true" flags="0" /> <item name="android.permission.WAKE_LOCK" granted="true" flags="0" /> <item name="android.permission.UPDATE_APP_OPS_STATS" granted="true" flags="0" /> </perms> <proper-signing-keyset identifier="4" /> </package>
1.packageName;
2.codePath;
3.nativeLibaryPath;
4.此pkg 擁有的許可權相關資訊;
2.packages.list :儲存普通應用的資料目錄和uid資訊(uid > 1000 的pkg):
......
com.android.managedprovisioning 10009 0 /data/data/com.android.managedprovisioning platform 3003
com.android.gifviewer 10042 0 /data/data/com.android.gifviewer default none
com.android.dreams.phototable 10054 0 /data/data/com.android.dreams.phototable default none
com.leadcore.telassistant 1000 0 /data/data/com.leadcore.telassistant platform 3002,1023,1015,3003,3001
com.android.noisefield 10049 0 /data/data/com.android.noisefield default none
com.android.smspush 10064 0 /data/data/com.android.smspush default none
com.leadcore.codescan 10029 0 /data/data/com.leadcore.codescan platform 3003
com.android.wallpaper.livepicker 10046 0 /data/data/com.android.wallpaper.livepicker platform none
jp.co.omronsoft.openwnn 10051 0 /data/data/jp.co.omronsoft.openwnn default none
com.android.settings 1000 0 /data/data/com.android.settings platform 3002,1023,1015,3003,3001
......
3.packages-backup.xml (packages.xml的備份) , packages-stopped.xml (被強行停止的PKG 資訊) , packages-stopped-backup.xml (packages-stopped.xml 的備份);
當Android對檔案 packages.xml 和 packages-stopped.xml 寫之前,會先把它們備份,如果寫檔案成功了,再把備份檔案刪除掉。如果寫的時候,系統出問題重啟了,重啟後會讀取這兩個檔案時,發現有備份檔案,會使用備份檔案的內容,因為這個時候原檔案已經損壞了。
Settings(File dataDir, Object lock) {
... ...
mSystemDir = new File(dataDir, "system");
mSystemDir.mkdirs();
... ...
mSettingsFilename = new File(mSystemDir, "packages.xml");
mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");
mPackageListFilename = new File(mSystemDir, "packages.list");
final File kernelDir = new File("/config/sdcardfs");
... ...
mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
}
Question: Setting 類是如何管理package 資訊的?
Setting 讀取各種xml 檔案,將pkg 資訊放入 PackageSetting物件中,add 到 Settings.mPackages 這個成員陣列中進行統一管理:
private void readPackageLPw(XmlPullParser parser) {
... ...
packageSetting = new PackageSetting( ... ...
... ...
}
2.2 SystemConfig
SystemConfig systemConfig = SystemConfig.getInstance();
mGlobalGids = systemConfig.getGlobalGids();
mSystemPermissions = systemConfig.getSystemPermissions();
mAvailableFeatures = systemConfig.getAvailableFeatures();
PKMS 初始化時建立 systemConfig 物件:
SystemConfig() {
// Read configuration from system
readPermissions(Environment.buildPath(
Environment.getRootDirectory(), "etc", "sysconfig"), false);
// Read configuration from the old permissions dir
readPermissions(Environment.buildPath(
Environment.getRootDirectory(), "etc", "permissions"), false);
// Only read features from OEM config
readPermissions(Environment.buildPath(
Environment.getOemDirectory(), "etc", "sysconfig"), true);
readPermissions(Environment.buildPath(
Environment.getOemDirectory(), "etc", "permissions"), true);
}
SystemConfig 的初始化函式基本全部呼叫readPermissions()讀取system/etc/ 下的檔案,處理permission 相關資訊的錄入,獲取到的”permission“ 相關的資訊都放在Setting.mPermissions 中。
例如/system/etc/permissin/platform.xml:
<assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="media" />
<assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="media" />
<assign-permission name="android.permission.WAKE_LOCK" uid="media" />
<assign-permission name="android.permission.UPDATE_DEVICE_STATS" uid="media" />
都是系統裡permission_name 最原始的定義;
繼續分析PKMS 建構函式:
PackageManagerService () {
... ...
mHandlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
mHandlerThread.start();
mHandler = new PackageHandler(mHandlerThread.getLooper());
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
File dataDir = Environment.getDataDirectory();//存放應用資料目錄
mAppDataDir = new File(dataDir, "data");
mAppInstallDir = new File(dataDir, "app");//放應用
mAppLib32InstallDir = new File(dataDir, "app-lib");//native庫
mAsecInternalPath = new File(dataDir, "app-asec").getPath();
mUserAppDataDir = new File(dataDir, "user");//存放使用者資料
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
... ...
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.gids = appendInts(bp.gids, perm.gids);
}
}
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));
}
}
1.建立訊息處理執行緒 PackageHandler;
2.建立 ”/data/app“ , "/data/data" , "/data/app-lib" , "/data/user" 等目錄;
3.獲取SystemConfig初始化時生成的mPermissions 系統全新相關資訊,一一對應生成BasePermission 物件,放在Settings.mPermission 中 (此時,Setting 中包含PKMS 解析完成的 所有系統許可權相關資訊:Settings.mPermission)
2.3 readLPw函式
mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false));
Settings的readLPw函式的主要作用就是:檢查packages-backup.xml有沒有,有這個檔案說明在寫packages.xml的時候系統出問題了,所以在系統啟動的時候就要讀備份的想xml檔案內容。如果沒有這個備份檔案再去看packages.xml, 然後再去解析xml檔案,把解析出來的內容封裝在各個物件中儲存在mSettings中各個變數中(此時,Setting 中包含PKMS 解析完成的 所有pkg相關資訊:Settings.XXXX)
2.4 掃描檔案
2.4.1 dex
long startTime = SystemClock.uptimeMillis();//記錄開始掃描時間
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
startTime);
... ...
final ArraySet<String> alreadyDexOpted = new ArraySet<String>();//已經優化的檔案集合
... ...
// BOOTCLASSPATH:自定義的一些JAR包 路徑集合
final String bootClassPath = System.getenv("BOOTCLASSPATH");
//SYSTEMSERVERCLASSPATH : framework 和 service 的一些jar 包的集合
final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");
if (bootClassPath != null) {
String[] bootClassPathElements = splitString(bootClassPath, ':');
for (String element : bootClassPathElements) {
alreadyDexOpted.add(element);
}
}
if (systemServerClassPath != null) {
String[] systemServerClassPathElements = splitString(systemServerClassPath, ':');
for (String element : systemServerClassPathElements) {
alreadyDexOpted.add(element);
}
}
... ...
上面這段程式碼就是 把 一些中定義的JAR 包 和 framework 和service 的一些JAR 把 放入”已經dexopt“的陣列中,避免再次dex
if (mSharedLibraries.size() > 0) {
// 處理sharedLibrary 的dex
... ...
for (String dexCodeInstructionSet : dexCodeInstructionSets) {
for (SharedLibraryEntry libEntry : mSharedLibraries.values()) {
final String lib = libEntry.path;
if (lib == null) {
continue;
}
try {
int dexoptNeeded = DexFile.getDexOptNeeded(lib, null, dexCodeInstructionSet, false);
if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
alreadyDexOpted.add(lib);
mInstaller.dexopt(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded, false);
}
} catch (FileNotFoundException e) {
Slog.w(TAG, "Library not found: " + lib);
} catch (IOException e) {
Slog.w(TAG, "Cannot dexopt " + lib + "; is it an APK or JAR? "
+ e.getMessage());
}
}
}
}
上面這段程式碼就是處理一些sharedLib ,共享庫的dex
File frameworkDir = new File(Environment.getRootDirectory(), "framework");
...
alreadyDexOpted.add(frameworkDir.getPath() + "/framework-res.apk");
...
alreadyDexOpted.add(frameworkDir.getPath() + "/core-libart.jar");
...
String[] frameworkFiles = frameworkDir.list();
if (frameworkFiles != null) {
...
for (String dexCodeInstructionSet : dexCodeInstructionSets) {
for (int i=0; i<frameworkFiles.length; i++) {
File libPath = new File(frameworkDir, frameworkFiles[i]);
String path = libPath.getPath();
if (alreadyDexOpted.contains(path)) {
continue;
}
if (!path.endsWith(".apk") && !path.endsWith(".jar")) {
continue;
}
try {
int dexoptNeeded = DexFile.getDexOptNeeded(path, null, dexCodeInstructionSet, false);
if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
mInstaller.dexopt(path, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded, false);
}
}
...
上面這段程式碼: 處理/system/framework 下的dex,僅僅只處理apk 和 jar 檔案。
以上,完成 1.中定義JAR 包;2. framework 和service 相關的一些JAR ;3 .sharedLib 庫 ; 4./system/framework 下apk 和 jar 檔案 的dex 。
2.4.2 掃描APK 相關
scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0);
// Find base frameworks (resource packages without code).
scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED,
scanFlags | SCAN_NO_DEX, 0);
// Collected privileged system packages.
final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);
// Collect ordinary system packages.
final File systemAppDir = new File(Environment.getRootDirectory(), "app");
scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
// Collect all vendor packages.
File vendorAppDir = new File("/vendor/app");
try {
vendorAppDir = vendorAppDir.getCanonicalFile();
} catch (IOException e) {
// failed to look up canonical path, continue with original one
}
scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
// Collect all OEM packages.
final File oemAppDir = new File(Environment.getOemDirectory(), "app");
scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
掃描完APK 後經過一系列處理,呼叫updatePermissionsLPw函式,更新了 mSettings.mPackages 變數。
2.5 賦予許可權,寫packages.xml
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
SystemClock.uptimeMillis());
Slog.i(TAG, "Time to scan packages: "
+ ((SystemClock.uptimeMillis()-startTime)/1000f)
+ " seconds");
... ...
updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL
| (regrantPermissions//是否要重新賦許可權
? (UPDATE_PERMISSIONS_REPLACE_PKG|UPDATE_PERMISSIONS_REPLACE_ALL)
... ...
// All the changes are done during package scanning.
mSettings.updateInternalDatabaseVersion();//更新資料庫
// can downgrade to reader
mSettings.writeLPr();//把mSettings寫入packages.xml
掃描完APK 後經過一系列處理,呼叫updatePermissionsLPw函式,更新了 mSettings.mPackages 變數,呼叫mSettings.writeLPr() 把新的mPackage 資訊寫入packages.xml 中。
packages.xml的更新
1 .PMS的建構函式先讀取儲存在packages.xml中的內容儲存在mSettings中;
2. 建構函式中掃描裝置中幾個應用目錄下的應用檔案,並把掃描結果儲存在PMS的mPackages成員變數中;
3. 通過對比mSettings內容是否有被升級包覆蓋的系統應用,如果有從mPackages去除。這樣mPackages和mSettings的記錄一致了,最後將本次掃描內容(mPackages的內容)寫到packages.xml檔案中。