代碼基于MTK Android 9.x
目錄結(jié)構(gòu)
1 以上過程的主要調(diào)用序列圖如下:
2 mOnlyCore
3 SystemServer.startBootstrapServices
4 PackageManagerService.main
5 new PackageManagerService
5.1 簡(jiǎn)單初始化工作
5.2 addSharedUserLPw
5.2.1 uid和權(quán)限的關(guān)系
5.3 初始化dex優(yōu)化服務(wù)類,解析SystemConfig文件
5.4 SystemConfig讀取配置文件
5.4.1 feature配置文件
5.4.2 系統(tǒng)庫文件
5.4.3 權(quán)限和gid映射文件
5.4.4 assign-permission
5.4.5 allow-in-power-save
5.5 創(chuàng)建PMS的HandlerThread和系統(tǒng)共享庫處理
5.6 readLPw
5.6.1 判斷文件是否存在
5.6.2 解析packages.xml文件
5.6.2.1 package字段解析
5.6.2.2 permissions字段解析
5.6.2.3 shared-user字段解析
5.6.2.4 preferred-activities
5.6.2.5 updated-package字段解析
5.6.2.6 keyset-settings
5.6.2.7 mPendingPackages數(shù)據(jù)的處理
5.6.3 writeKernelMappingLPr
5.7 驗(yàn)證系統(tǒng)指紋
6 總結(jié)
以上過程的主要調(diào)用序列圖如下:

mOnlyCore
// Only run "core" apps if we're encrypting the device.
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;
}
只有當(dāng)vold.decrypt屬性的值為"trigger_restart_min_framework"和“1”時(shí),mOnlyCore才為true
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
coreApp="true"
package="com.qiku.xt_security">
mOnlyCore為true表示,PMS只會(huì)對(duì)聲明了 coreApp="true"的應(yīng)用進(jìn)行解析
SystemServer.startBootstrapServices
mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
PackageManagerService.main
public static PackageManagerService main(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
// Self-check for initial settings.
PackageManagerServiceCompilerMapping.checkProperties();
PackageManagerService m = new PackageManagerService(context, installer,
factoryTest, onlyCore);
m.enableSystemUserPackages();
ServiceManager.addService("package", m);
final PackageManagerNative pmn = m.new PackageManagerNative();
ServiceManager.addService("package_native", pmn);
return m;
}
factoryTest:表示是否運(yùn)行在工廠模式
onlyCore:表示是否運(yùn)行在只解析core應(yīng)用的模式下,也就是加密模式下
checkProperties檢查以pm.dexopt開頭的以下屬性的值
public static final String REASON_STRINGS[] = {
"first-boot", "boot", "install", "bg-dexopt", "ab-ota", "inactive", "shared"
};
λ adb shell getprop | grep pm.dexopt
[pm.dexopt.first-boot]: [quicken]
[pm.dexopt.boot]: [verify]
[pm.dexopt.install]: [speed-profile]
[pm.dexopt.bg-dexopt]: [speed-profile]
[pm.dexopt.ab-ota]: [speed-profile]
[pm.dexopt.inactive]: [verify]
[pm.dexopt.shared]: [speed]
然后,初始化PackageManagerService對(duì)象,并添加到ServiceManager中去
new PackageManagerService
簡(jiǎn)單初始化工作
LockGuard.installLock(mPackages, LockGuard.INDEX_PACKAGES);
mContext = context;
mFactoryTest = factoryTest;
mOnlyCore = onlyCore;
mMetrics = new DisplayMetrics();
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.
LocalServices.addService(
PackageManagerInternal.class, new PackageManagerInternalImpl());
sUserManager = new UserManagerService(context, this,
new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore), mPackages);
mPermissionManager = PermissionManagerService.create(context,
new DefaultPermissionGrantedCallback() {
@Override
public void onDefaultRuntimePermissionsGranted(int userId) {
synchronized(mPackages) {
mSettings.onDefaultRuntimePermissionsGrantedLPr(userId);
}
}
}, mPackages /*externalLock*/);
mDefaultPermissionPolicy = mPermissionManager.getDefaultPermissionGrantPolicy();
mSettings = new Settings(mPermissionManager.getPermissionSettings(), mPackages);
}
}
主要完成變量的賦值;初始化UserManagerService的,UserManagerService用于管理android的多用戶;初始化PermissionManagerService,用于管理應(yīng)用的權(quán)限
初始化mDefaultPermissionPolicy,用于管理平臺(tái)組件默認(rèn)獲取運(yùn)行時(shí)權(quán)限。例如the shell UID is a part of the system and the Phone app should have phone related permission by default.
addSharedUserLPw
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);
mSettings.addSharedUserLPw("android.uid.se", SE_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
addSharedUserLPw主要用于初始化一個(gè)SharedUserSetting數(shù)據(jù)結(jié)構(gòu),用于保存聲明了同一個(gè)android:sharedUserId="android.uid.system" 的應(yīng)用信息
final ArrayMap<String, SharedUserSetting> mSharedUsers = new ArrayMap<String, SharedUserSetting>();

上圖中的userId,指的是uid
一個(gè)應(yīng)用聲明了與其他應(yīng)用共享uid,主要有以下幾個(gè)作用:
兩個(gè)或多個(gè)聲明了同一種sharedUserIds的APK可共享彼此的數(shù)據(jù),并且可運(yùn)行在同一進(jìn)程中
更重要的是,通過聲明特定的sharedUserId,該APK所在進(jìn)程將被賦予指定的UID; SystemUI聲明了system的uid,運(yùn)行SystemUI的進(jìn)程就可享有system用戶所對(duì)應(yīng)的權(quán)限(實(shí)際上就是將該進(jìn)程的uid設(shè)置為system的uid)了
AndroidManifest.xml中聲明sharedUserId外,APK在編譯時(shí)還必須使用對(duì)應(yīng)的證書進(jìn)行簽名。例如SystemUI,在其Android.mk中需要額外聲明LOCAL_CERTIFICATE := platform,如此,才可獲得指定的UID
SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
SharedUserSetting s = mSharedUsers.get(name);
if (s != null) {
if (s.userId == uid) {
return s;
}
PackageManagerService.reportSettingsProblem(Log.ERROR,
"Adding duplicate shared user, keeping first: " + name);
return null;
}
s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
s.userId = uid;
if (addUserIdLPw(uid, s, name)) {
mSharedUsers.put(name, s);
return s;
}
return null;
}
private boolean addUserIdLPw(int uid, Object obj, Object name) {
if (uid > Process.LAST_APPLICATION_UID) {
return false;
}
if (uid >= Process.FIRST_APPLICATION_UID) {//普通應(yīng)用,其值大于10000
int N = mUserIds.size();
final int index = uid - Process.FIRST_APPLICATION_UID;
while (index >= N) {
mUserIds.add(null);
N++;
}
if (mUserIds.get(index) != null) {
PackageManagerService.reportSettingsProblem(Log.ERROR,
"Adding duplicate user id: " + uid
+ " name=" + name);
return false;
}
mUserIds.set(index, obj);
} else {//系統(tǒng)應(yīng)用,其值小于10000
if (mOtherUserIds.get(uid) != null) {
PackageManagerService.reportSettingsProblem(Log.ERROR,
"Adding duplicate shared id: " + uid
+ " name=" + name);
return false;
}
mOtherUserIds.put(uid, obj);
}
return true;
}
除了創(chuàng)建一個(gè)map外,settings中還會(huì)創(chuàng)建另外兩個(gè)數(shù)據(jù)結(jié)構(gòu),mUserIds和mOtherUserIds,這兩個(gè)數(shù)據(jù)結(jié)構(gòu),通過數(shù)組下表映射進(jìn)行查詢,速度比mSharedUsers更快
mUserIds是一個(gè)ArrayList,存儲(chǔ)普通應(yīng)用,以(uid-Process.FIRST_APPLICATION_UID)為index,SharedUserSetting為value
mOtherUserIds是一個(gè),SparseArray,存儲(chǔ)系統(tǒng)應(yīng)用,以u(píng)id為index,SharedUserSetting為value
uid和權(quán)限的關(guān)系
之所以不同的應(yīng)用,通過指定相同的uid,可以共享權(quán)限,是因?yàn)?,系統(tǒng)中是通過uid為映射,存儲(chǔ)應(yīng)用的權(quán)限數(shù)據(jù)的。不同的應(yīng)用,具有同一個(gè)uid,查到的權(quán)限數(shù)據(jù)是相同的
初始化dex優(yōu)化服務(wù)類,解析SystemConfig文件
mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,
"*dexopt*");
DexManager.Listener dexManagerListener = DexLogger.getListener(this,
installer, mInstallLock);
mDexManager = new DexManager(mContext, this, mPackageDexOptimizer, installer, mInstallLock,
dexManagerListener);
mArtManagerService = new ArtManagerService(mContext, this, installer, mInstallLock);
mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
mOnPermissionChangeListeners = new OnPermissionChangeListeners(
FgThread.get().getLooper());
getDefaultDisplayMetrics(context, mMetrics);
初始化dex優(yōu)化相關(guān)類
SystemConfig讀取配置文件
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "get system config");
SystemConfig systemConfig = SystemConfig.getInstance();
mAvailableFeatures = systemConfig.getAvailableFeatures();
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
mProtectedPackages = new ProtectedPackages(mContext);
通過SystemConfig,解析各個(gè)目錄下/etc/permissions和/etc/sysconfig下的各個(gè)配置文件
SystemConfig() {
// Read configuration from system
readPermissions(Environment.buildPath(
Environment.getRootDirectory(), "etc", "sysconfig"), ALLOW_ALL);
// Read configuration from the old permissions dir
readPermissions(Environment.buildPath(
Environment.getRootDirectory(), "etc", "permissions"), ALLOW_ALL);
....
}
讀取的文件目錄的順序?yàn)?system-->/vendor-->/odm-->/oem-->/product
以permissions目錄下的文件為例:
full_k61v1_64_bsp:/system/etc/permissions # ls
android.hardware.faketouch.xml
android.hardware.location.gps.xml
android.hardware.microphone.xml
android.hardware.touchscreen.multitouch.distinct.xml
android.hardware.touchscreen.multitouch.jazzhand.xml
android.hardware.touchscreen.multitouch.xml
android.hardware.touchscreen.xml
android.hardware.usb.host.xml
android.software.live_wallpaper.xml
android.software.webview.xml
com.android.location.provider.xml
com.android.media.remotedisplay.xml
com.android.mediadrm.signer.xml
com.mediatek.ims.plugin.xml
org.simalliance.openmobileapi.xml
platform.xml
pms_sysapp_removable_system_list.txt
privapp-permissions-mediatek.xml
privapp-permissions-platform.xml
feature配置文件
android.hardware..和android.hardware..文件中中聲明了手機(jī)能夠支持的的硬件特性,例如支持camera、支持藍(lán)牙等
數(shù)據(jù)格式為:
<permissions>
<feature name="android.software.webview" />
</permissions>
解析后的數(shù)據(jù)存儲(chǔ)在SystemConfig.mAvailableFeatures中
final ArrayMap<String, FeatureInfo> mAvailableFeatures = new ArrayMap<>();
private void addFeature(String name, int version) {
FeatureInfo fi = mAvailableFeatures.get(name);
if (fi == null) {
fi = new FeatureInfo();
fi.name = name;
fi.version = version;
mAvailableFeatures.put(name, fi);
} else {
fi.version = Math.max(fi.version, version);
}
}
可以通過adb shell pm list feature查看手機(jī)支持的feature
full_k61v1_64_bsp:/ # pm list features
feature:reqGlEsVersion=0x30002
feature:android.hardware.audio.low_latency
feature:android.hardware.audio.output
feature:android.hardware.bluetooth
feature:android.hardware.bluetooth_le
feature:android.hardware.camera
feature:android.hardware.camera.any
feature:android.hardware.camera.autofocus
系統(tǒng)庫文件
library聲明的是系統(tǒng)提供的Java庫,應(yīng)用程序運(yùn)行時(shí)候必須要鏈接這些庫,該工作由系統(tǒng)自動(dòng)完成
數(shù)據(jù)格式為:
<permissions>
<library name="com.android.media.remotedisplay"
file="/system/framework/com.android.media.remotedisplay.jar" />
</permissions>
數(shù)據(jù)解析后,存儲(chǔ)在mSharedLibraries中
final ArrayMap<String, String> mSharedLibraries = new ArrayMap<>();
....
else if ("library".equals(name) && allowLibs) {
String lname = parser.getAttributeValue(null, "name");
String lfile = parser.getAttributeValue(null, "file");
if (lname == null) {
Slog.w(TAG, "<library> without name in " + permFile + " at "
+ parser.getPositionDescription());
} else if (lfile == null) {
Slog.w(TAG, "<library> without file in " + permFile + " at "
+ parser.getPositionDescription());
} else {
//Log.i(TAG, "Got library " + lname + " in " + lfile);
mSharedLibraries.put(lname, lfile);
}
可以通過pm list libraries命令,查看系統(tǒng)中支持的系統(tǒng)庫
full_k61v1_64_bsp:/ # pm list libraries
library:android.ext.services
library:android.ext.shared
library:android.test.base
library:android.test.mock
library:android.test.runner
library:com.android.future.usb.accessory
library:com.android.location.provider
library:com.android.media.remotedisplay
library:com.android.mediadrm.signer
library:com.mediatek.ims.plugin
library:javax.obex
library:org.apache.http.legacy
library:org.simalliance.openmobileapi
權(quán)限和gid映射文件
permission和group用于建立Linux層gid和Android層pemission之間的映射關(guān)系
具有android.permission.BLUETOOTH_ADMIN權(quán)限的應(yīng)用,將獲得net_bt_admin組權(quán)限
數(shù)據(jù)格式為:
<permission name="android.permission.BLUETOOTH_ADMIN" >
<group gid="net_bt_admin" />
</permission>
<permission name="android.permission.BLUETOOTH" >
<group gid="net_bt" />
</permission>
<permission name="android.permission.BLUETOOTH_STACK" >
<group gid="bluetooth" />
<group gid="wakelock" />
<group gid="uhid" />
</permission>
gid的名稱和數(shù)字的對(duì)應(yīng)是在system\core\include\private\android_filesystem_config.h中定義的
#define AID_NET_BT 3002 /* bluetooth: create sco, rfcomm or l2cap sockets */
解析后,數(shù)據(jù)存儲(chǔ)在mPermissions中,PermissionEntry存儲(chǔ)了改權(quán)限關(guān)聯(lián)的gid
final ArrayMap<String, PermissionEntry> mPermissions = new ArrayMap<>();
} else if ("permission".equals(name) && allowPermissions) {
String perm = parser.getAttributeValue(null, "name");//解析name字段
if (perm == null) {
Slog.w(TAG, "<permission> without name in " + permFile + " at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
perm = perm.intern();
readPermission(parser, perm);
void readPermission(XmlPullParser parser, String name)
throws IOException, XmlPullParserException {
if (mPermissions.containsKey(name)) {
throw new IllegalStateException("Duplicate permission definition for " + name);
}
final boolean perUser = XmlUtils.readBooleanAttribute(parser, "perUser", false);
final PermissionEntry perm = new PermissionEntry(name, perUser);
mPermissions.put(name, perm);
int outerDepth = parser.getDepth();
int type;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
|| parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG
|| type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if ("group".equals(tagName)) {
String gidStr = parser.getAttributeValue(null, "gid");
if (gidStr != null) {
int gid = Process.getGidForName(gidStr);
perm.gids = appendInt(perm.gids, gid);
} else {
Slog.w(TAG, "<group> without gid at "
+ parser.getPositionDescription());
}
}
XmlUtils.skipCurrentTag(parser);
}
}
assign-permission
賦予對(duì)應(yīng)uid相應(yīng)的權(quán)限。如果下面一行表示uid為media,那么就賦予它ACCESS_SURFACE_FLINGER的權(quán)限,其實(shí)就是把它加到對(duì)應(yīng)的用戶組中
數(shù)據(jù)格式為:
<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.MODIFY_AUDIO_SETTINGS" uid="cameraserver" />
<assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="cameraserver" />
<assign-permission name="android.permission.WAKE_LOCK" uid="cameraserver" />
解析后,存放在SystemConfig.mSystemPermissions數(shù)據(jù)結(jié)構(gòu)中
final SparseArray<ArraySet<String>> mSystemPermissions = new SparseArray<>();
} else if ("assign-permission".equals(name) && allowPermissions) {
String perm = parser.getAttributeValue(null, "name");
if (perm == null) {
Slog.w(TAG, "<assign-permission> without name in " + permFile + " at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
String uidStr = parser.getAttributeValue(null, "uid");
if (uidStr == null) {
Slog.w(TAG, "<assign-permission> without uid in " + permFile + " at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
int uid = Process.getUidForName(uidStr);
if (uid < 0) {
Slog.w(TAG, "<assign-permission> with unknown uid \""
+ uidStr + " in " + permFile + " at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
perm = perm.intern();
ArraySet<String> perms = mSystemPermissions.get(uid);
if (perms == null) {
perms = new ArraySet<String>();
mSystemPermissions.put(uid, perms);
}
perms.add(perm);
XmlUtils.skipCurrentTag(parser);
}
allow-in-power-save
允許應(yīng)用在省電模式下,訪問網(wǎng)絡(luò)
數(shù)據(jù)格式為:
<!-- These are the standard packages that are white-listed to always have internet
access while in power save mode, even if they aren't in the foreground. -->
<allow-in-power-save package="com.android.providers.downloads" />
<!-- need by ADUPS_FOTA_SUPPORT or FOTA_UPDATE_SUPPORT -->
<allow-in-power-save package="com.adups.fota" />
<allow-in-power-save package="com.adups.fota.sysoper" />
<!-- These are the standard packages that are white-listed to always have internet
access while in data mode, even if they aren't in the foreground. -->
<allow-in-data-usage-save package="com.android.providers.downloads" />
<!-- This is a core platform component that needs to freely run in the background -->
<allow-in-power-save package="com.android.cellbroadcastreceiver" />
<allow-in-power-save package="com.android.shell" />
解析后,存儲(chǔ)在
final ArraySet<String> mAllowInPowerSave = new ArraySet<>();
創(chuàng)建PMS的HandlerThread和系統(tǒng)共享庫處理
//創(chuàng)建PMS的HanderThread,進(jìn)行異步任務(wù)的處理
mHandlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
mHandlerThread.start();
mHandler = new PackageHandler(mHandlerThread.getLooper());
mProcessLoggingHandler = new ProcessLoggingHandler();
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
mInstantAppRegistry = new InstantAppRegistry(this);
//獲取上文所說的系統(tǒng)共享庫
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);
}
addSharedLibraryLPw中各個(gè)參數(shù)的意義:
PLATFORM_PACKAGE_NAME : "android"
TYPE_BUILTIN = 0;: Shared library type: this library is a part of the OS and cannot be updated or uninstalled.
VERSION_UNDEFINED = -1: Constant for referring to an undefined version.
如上文所示,共享庫的格式為:
<permissions>
<library name="com.android.media.remotedisplay"
file="/system/framework/com.android.media.remotedisplay.jar" />
</permissions>
addSharedLibraryLPw
private boolean addSharedLibraryLPw(String path, String apk, String name, long version,
int type, String declaringPackageName, long declaringVersionCode) {
LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(name);
if (versionedLib == null) {
versionedLib = new LongSparseArray<>();
mSharedLibraries.put(name, versionedLib);
if (type == SharedLibraryInfo.TYPE_STATIC) {//如果是靜態(tài)類型的系統(tǒng)共享包,各個(gè)類型的意義見下面代碼解釋
mStaticLibsByDeclaringPackage.put(declaringPackageName, versionedLib);
}
} else if (versionedLib.indexOfKey(version) >= 0) {
return false;
}
SharedLibraryEntry libEntry = new SharedLibraryEntry(path, apk, name,
version, type, declaringPackageName, declaringVersionCode);
versionedLib.put(version, libEntry);
return true;
}
/**
* Shared library type: this library is a part of the OS
* and cannot be updated or uninstalled.
*/
public static final int TYPE_BUILTIN = 0;
/**
* Shared library type: this library is backwards-compatible, can
* be updated, and updates can be uninstalled. Clients link against
* the latest version of the library.
*/
public static final int TYPE_DYNAMIC = 1;
/**
* Shared library type: this library is <strong>not</strong> backwards
* -compatible, can be updated and updates can be uninstalled. Clients
* link against a specific version of the library.
*/
public static final int TYPE_STATIC = 2;
上述代碼邏輯對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)為:
final ArrayMap<String, LongSparseArray<SharedLibraryEntry>> mSharedLibraries = new ArrayMap<>();
( name="com.android.media.remotedisplay" LongSparseArray<SharedLibraryEntry>)
(int version, SharedLibraryEntry)
即可以通過name-->得到該系統(tǒng)共享包對(duì)應(yīng)的各個(gè)版本的SharedLibraryEntry信息
readLPw
LPw, local printwrite
改方法主要對(duì)三個(gè)文件進(jìn)行讀取
packages.xml,packages_backup.xml: PKMS掃描完目標(biāo)文件夾后會(huì)創(chuàng)建該文件。
當(dāng)系統(tǒng)進(jìn)行程序安裝、卸載和更新等操作時(shí),均會(huì)更新該文件。該文件保存了系統(tǒng)中與package相關(guān)的一些信息。
backup是臨時(shí)文件。PKMS先把數(shù)據(jù)寫到backup中,信息都寫成功后再改名成非backup的文件。
其目的是防止在寫文件過程中出錯(cuò),導(dǎo)致信息丟失;如果存在backup文件,則表示寫入過程中,被異常中指。packages.list:描述系統(tǒng)中存在的所有非系統(tǒng)自帶的APK的信息。當(dāng)這些程序有變動(dòng)時(shí),PKMS就會(huì)更新該文件。
packages-stopped.xml:從系統(tǒng)自帶的設(shè)置程序中進(jìn)入應(yīng)用程序頁面,然后在選擇強(qiáng)制停止(ForceStop)某個(gè)應(yīng)用時(shí),系統(tǒng)會(huì)將該應(yīng)用的相關(guān)信息記錄到此文件中。也就是該文件保存系統(tǒng)中被用戶強(qiáng)制停止的Package的信息。
packages-stopped.xml文件,在Android 9去除了
在該方法開始的時(shí)候,會(huì)判斷packages.xml,packages_backup.xml是否存在,如果都不能存在的話,會(huì)直接返回false
判斷文件是否存在
Log.d(TAG, Log.getStackTraceString(new Throwable()));
FileInputStream str = null;
if (mBackupSettingsFilename.exists()) {
try {
//如果packages_backup.xml文件存在,則設(shè)置成,讀取backup文件
str = new FileInputStream(mBackupSettingsFilename);
mReadMessages.append("Reading from backup settings file\n");
PackageManagerService.reportSettingsProblem(Log.INFO,
"Need to read from backup settings file");
if (mSettingsFilename.exists()) {//如果packages_backup.xml和packages.xml兩個(gè)文件都存在,則刪除packages.xml文件,因?yàn)?,正常的文件寫入流程可能已?jīng)被中斷
// If both the backup and settings file exist, we
// ignore the settings since it might have been
// corrupted.
Slog.w(PackageManagerService.TAG, "Cleaning up settings file "
+ mSettingsFilename);
mSettingsFilename.delete();
}
} catch (java.io.IOException e) {
// We'll try for the normal settings file.
}
}
//清楚本都數(shù)據(jù)信息,每個(gè)屬性代表的字段,后面講解
mPendingPackages.clear();
mPastSignatures.clear();
mKeySetRefs.clear();
mInstallerPackages.clear();
try {
if (str == null) {
//如果packages_backup.xml和packages.xml兩個(gè)文件都不存在,則讀取fingerprint屬性值后,退出
if (!mSettingsFilename.exists()) {
mReadMessages.append("No settings file found\n");
PackageManagerService.reportSettingsProblem(Log.INFO,
"No settings file; creating initial state");
// It's enough to just touch version details to create them
// with default values
//獲取系統(tǒng)指紋,此時(shí)packages.xml文件不存在,從系統(tǒng)屬性中獲取系統(tǒng)指紋
findOrCreateVersion(StorageManager.UUID_PRIVATE_INTERNAL).forceCurrent();
findOrCreateVersion(StorageManager.UUID_PRIMARY_PHYSICAL).forceCurrent();
return false;
}
str = new FileInputStream(mSettingsFilename);
}
獲取系統(tǒng)指紋的方法為:
先判斷ro.build.fingerprint屬性的值,不為空的情況下,通過多個(gè)屬性值進(jìn)行拼接
處于各種目的,有的項(xiàng)目會(huì)將ro.build.fingerprint設(shè)置成固定的值
private static String deriveFingerprint() {
String finger = SystemProperties.get("ro.build.fingerprint");
if (TextUtils.isEmpty(finger)) {
finger = getString("ro.product.brand") + '/' +
getString("ro.product.name") + '/' +
getString("ro.product.device") + ':' +
getString("ro.build.version.release") + '/' +
getString("ro.build.id") + '/' +
getString("ro.build.version.incremental") + ':' +
getString("ro.build.type") + '/' +
getString("ro.build.tags");
}
return finger;
}
解析packages.xml文件
解析和讀取packages.xml文件,如下所示,我們一個(gè)一個(gè)的分析,packages.xml文件的各個(gè)字段的解析過程
int outerDepth = parser.getDepth();
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if (tagName.equals("package")) {
readPackageLPw(parser);
} else if (tagName.equals("permissions")) {
mPermissions.readPermissions(parser);
} else if (tagName.equals("permission-trees")) {
mPermissions.readPermissionTrees(parser);
} else if (tagName.equals("shared-user")) {
readSharedUserLPw(parser);
} else if (tagName.equals("preferred-packages")) {
// no longer used.
} else if (tagName.equals("preferred-activities")) {
// Upgrading from old single-user implementation;
// these are the preferred activities for user 0.
readPreferredActivitiesLPw(parser, 0);
} else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) {
// TODO: check whether this is okay! as it is very
// similar to how preferred-activities are treated
readPersistentPreferredActivitiesLPw(parser, 0);
} else if (tagName.equals(TAG_CROSS_PROFILE_INTENT_FILTERS)) {
// TODO: check whether this is okay! as it is very
// similar to how preferred-activities are treated
readCrossProfileIntentFiltersLPw(parser, 0);
} else if (tagName.equals(TAG_DEFAULT_BROWSER)) {
readDefaultAppsLPw(parser, 0);
} else if (tagName.equals("updated-package")) {
readDisabledSysPackageLPw(parser);
} else if (tagName.equals("cleaning-package")) {
String name = parser.getAttributeValue(null, ATTR_NAME);
String userStr = parser.getAttributeValue(null, ATTR_USER);
String codeStr = parser.getAttributeValue(null, ATTR_CODE);
if (name != null) {
int userId = UserHandle.USER_SYSTEM;
boolean andCode = true;
try {
if (userStr != null) {
userId = Integer.parseInt(userStr);
}
} catch (NumberFormatException e) {
}
if (codeStr != null) {
andCode = Boolean.parseBoolean(codeStr);
}
addPackageToCleanLPw(new PackageCleanItem(userId, name, andCode));
}
} else if (tagName.equals("renamed-package")) {
String nname = parser.getAttributeValue(null, "new");
String oname = parser.getAttributeValue(null, "old");
if (nname != null && oname != null) {
mRenamedPackages.put(nname, oname);
}
} else if (tagName.equals("restored-ivi")) {
readRestoredIntentFilterVerifications(parser);
} else if (tagName.equals("last-platform-version")) {
// Upgrade from older XML schema
final VersionInfo internal = findOrCreateVersion(
StorageManager.UUID_PRIVATE_INTERNAL);
final VersionInfo external = findOrCreateVersion(
StorageManager.UUID_PRIMARY_PHYSICAL);
internal.sdkVersion = XmlUtils.readIntAttribute(parser, "internal", 0);
external.sdkVersion = XmlUtils.readIntAttribute(parser, "external", 0);
internal.fingerprint = external.fingerprint =
XmlUtils.readStringAttribute(parser, "fingerprint");
} else if (tagName.equals("database-version")) {
// Upgrade from older XML schema
final VersionInfo internal = findOrCreateVersion(
StorageManager.UUID_PRIVATE_INTERNAL);
final VersionInfo external = findOrCreateVersion(
StorageManager.UUID_PRIMARY_PHYSICAL);
internal.databaseVersion = XmlUtils.readIntAttribute(parser, "internal", 0);
external.databaseVersion = XmlUtils.readIntAttribute(parser, "external", 0);
} else if (tagName.equals("verifier")) {
final String deviceIdentity = parser.getAttributeValue(null, "device");
try {
mVerifierDeviceIdentity = VerifierDeviceIdentity.parse(deviceIdentity);
} catch (IllegalArgumentException e) {
Slog.w(PackageManagerService.TAG, "Discard invalid verifier device id: "
+ e.getMessage());
}
} else if (TAG_READ_EXTERNAL_STORAGE.equals(tagName)) {
final String enforcement = parser.getAttributeValue(null, ATTR_ENFORCEMENT);
mReadExternalStorageEnforced =
"1".equals(enforcement) ? Boolean.TRUE : Boolean.FALSE;
} else if (tagName.equals("keyset-settings")) {
mKeySetManagerService.readKeySetsLPw(parser, mKeySetRefs);
} else if (TAG_VERSION.equals(tagName)) {
final String volumeUuid = XmlUtils.readStringAttribute(parser,
ATTR_VOLUME_UUID);
final VersionInfo ver = findOrCreateVersion(volumeUuid);
ver.sdkVersion = XmlUtils.readIntAttribute(parser, ATTR_SDK_VERSION);
ver.databaseVersion = XmlUtils.readIntAttribute(parser, ATTR_DATABASE_VERSION);
ver.fingerprint = XmlUtils.readStringAttribute(parser, ATTR_FINGERPRINT);
/* 360OS begin, add for fingerprint_runtime */
try {
ver.fingerprintRuntime = XmlUtils.readStringAttribute(parser,
FingerPrintRuntime.ATTR_FINGERPRINT_RUNTIME);
} catch (Exception e) {
ver.fingerprintRuntime = "";
e.printStackTrace();
}
/* 360OS end */
} else {
Slog.w(PackageManagerService.TAG, "Unknown element under <packages>: "
+ parser.getName());
XmlUtils.skipCurrentTag(parser);
}
}
str.close();
} catch (XmlPullParserException e) {
mReadMessages.append("Error reading: " + e.toString());
PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading settings: " + e);
Slog.wtf(PackageManagerService.TAG, "Error reading package manager settings", e);
} catch (java.io.IOException e) {
mReadMessages.append("Error reading: " + e.toString());
PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading settings: " + e);
Slog.wtf(PackageManagerService.TAG, "Error reading package manager settings", e);
}
package字段解析
數(shù)據(jù)格式為:
<package name="com.android.systemui" codePath="/system/priv-app/QK_SystemUIML" nativeLibraryPath="/system/priv-app/QK_SystemUIML/lib" primaryCpuAbi="arm64-v8a" publicFlags="810040845" privateFlags="8" ft="11e8dc5d800" it="11e8dc5d800" ut="11e8dc5d800" version="1" sharedUserId="10035" isOrphaned="true"
installer="...">
<sigs count="1" schemeVersion="1">
<cert index="2" />
</sigs>
<perms>
<item name="android.permission.REAL_GET_TASKS" granted="true" flags="0" />
<item name="android.permission.REMOTE_AUDIO_PLAYBACK" granted="true" flags="0" />
<item name="android.permission.REGISTER_WINDOW_MANAGER_LISTENERS" granted="true" flags="0" />
<item name="android.permission.WRITE_SETTINGS" granted="true" flags="0" />
<item name="android.permission.CARE_MODE_CHANGED" granted="true" flags="0" />
....
</perms>
<proper-signing-keyset identifier="3" />
</package>
** 各個(gè)字段意義: **
- name:應(yīng)用包名
- codePath:應(yīng)用安裝位置,可以安裝在:/system/priv-app/,/system/app/,/vendor/overlay,/system/presetapp,/data/app目錄
- nativeLibraryPath:表示 app 使用的 xxx.so 庫存放的位置
- primaryCpuAbi: app 以哪種 abi 架構(gòu)運(yùn)行
- ft 表示 apk 文件上次被更改的時(shí)間, it 表示 app 第一次安裝的時(shí)間, ut 表示 app 上次被更新時(shí)間, 它的值好像一直和 ft 相同, ota 或 app 重裝之后, 這里的ft和ut可能會(huì)改變。
- sharedUserId:應(yīng)用設(shè)置的shanre uid,沒有設(shè)置的情況下,該字段為:userid
- isOrphaned:Indicates if the package that installed this app has been uninstalled
- version 是 app 的版本號(hào)信息, 也就是在 AndroidManifest.xml 里配置的 android:versioncode
- sigs 塊里的 count 表示這個(gè) app 有多少個(gè)簽名信息, 有的 app 可能會(huì)被多個(gè)證書簽名。cert 里的 index 表示這個(gè) app 使用的證書的序號(hào), 當(dāng)系統(tǒng)發(fā)現(xiàn)一個(gè)新的證書, 這個(gè)號(hào)就會(huì)加1, key 是 app 使用的證書內(nèi)容的 ascii 碼值。PKMS 在掃 apk 文件過程中, 如果發(fā)現(xiàn)它和之前掃描到的 apk 使用的是相同的簽名證書, 這里就只會(huì)有個(gè) index 的值, 并沒有 key 的值。擁有相同的 index 的 package, 表明它們使用的是相同的簽名
<sigs count="1" schemeVersion="3">
<cert index="0" key="308204a8...90b1b357" />
</sigs>
perms 塊里是這個(gè) app 擁有的權(quán)限, 對(duì)于一般的 app, 這些權(quán)限是在 AndroidManifest.xml 里寫明的; 那些使用了相同 UID 的 app, 這里的權(quán)限就是所有使用相同 UID 的 app 申請(qǐng)的權(quán)限的總和。 granted表示這個(gè)權(quán)限是不是已經(jīng)被允許
proper-signing-keyset 里的 identifier 就是packages.xml中的的 keysets 里 identifier 的值。它是用來標(biāo)明這個(gè) app 使用的是哪個(gè)公鑰。
<keyset-settings version="1">
<keys>
<public-key identifier="1" value="MIIBIDANBgk...xUugvhaRXU89fwZBxxe7IJwIBAw==" />
...
<public-key identifier="16" value="MIGfM...QAB" />
<public-key identifier="17" value="MIGf...AQAB" />
<public-key identifier="18" value="MIGf...DAQAB" />
<public-key identifier="19" value="MIG...AQAB" />
<public-key identifier="20" value="MIGf...AQAB" />
</keys>
- installer 表示該應(yīng)用的安裝來源,默認(rèn)集成的應(yīng)用沒有該字段
** Settings.mPackages **
以上信心,解析后,存儲(chǔ)在Settings.PackageSetting對(duì)象中,添加到Settings.mPackages變量中
final ArrayMap<String, PackageSetting> mPackages = new ArrayMap<>();
} else if (userId > 0) {
packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr),
new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiString,
secondaryCpuAbiString, cpuAbiOverrideString, userId, versionCode, pkgFlags,
pkgPrivateFlags, parentPackageName, null /*childPackageNames*/,
null /*usesStaticLibraries*/, null /*usesStaticLibraryVersions*/);
if (PackageManagerService.DEBUG_SETTINGS)
Log.i(PackageManagerService.TAG, "Reading package " + name + ": userId="
+ userId + " pkg=" + packageSetting);
if (packageSetting == null) {
PackageManagerService.reportSettingsProblem(Log.ERROR, "Failure adding uid "
+ userId + " while parsing settings at "
+ parser.getPositionDescription());
} else {
packageSetting.setTimeStamp(timeStamp);//ft, 表示apk上次被修改的時(shí)間
packageSetting.firstInstallTime = firstInstallTime;//it, 第一次被安裝的時(shí)間
packageSetting.lastUpdateTime = lastUpdateTime;//ut, 上一次更新的時(shí)間
}
** Settings.mPendingPackages **
如果應(yīng)用聲明了android:sharedUserId,則將應(yīng)用添加到mPendingPackages中
/**
* Used to track packages that have a shared user ID that hasn't been read
* in yet.
* <p>
* TODO: make this just a local variable that is passed in during package
* scanning to make it less confusing.
*/
private final ArrayList<PackageSetting> mPendingPackages = new ArrayList<>();
if (sharedUserId > 0) {
packageSetting = new PackageSetting(name.intern(), realName, new File(
codePathStr), new File(resourcePathStr), legacyNativeLibraryPathStr,
primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
versionCode, pkgFlags, pkgPrivateFlags, parentPackageName,
null /*childPackageNames*/, sharedUserId,
null /*usesStaticLibraries*/, null /*usesStaticLibraryVersions*/);
packageSetting.setTimeStamp(timeStamp);
packageSetting.firstInstallTime = firstInstallTime;
packageSetting.lastUpdateTime = lastUpdateTime;
mPendingPackages.add(packageSetting);
if (PackageManagerService.DEBUG_SETTINGS)
Log.i(PackageManagerService.TAG, "Reading package " + name
+ ": sharedUserId=" + sharedUserId + " pkg=" + packageSetting);
}
以上即是packages.xml中package配置項(xiàng)的解析過程,和相關(guān)的數(shù)據(jù)結(jié)構(gòu)。字段較短,沒有一一說明,后續(xù)遇到具體場(chǎng)景再具體分析
對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)為:

permissions字段解析
** 數(shù)據(jù)格式為:**
<permissions>
<item name="android.permission.REAL_GET_TASKS" package="android" protection="18" />
<item name="android.permission.ACCESS_CACHE_FILESYSTEM" package="android" protection="18" />
<item name="android.permission.REMOTE_AUDIO_PLAYBACK" package="android" protection="2" />
<item name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" package="com.android.providers.downloads" />
...
<item name="com.qiku.android.permission.feedback.ACCESS_FEEDBACK" package="com.qiku.android.feedback" protection="2" />
<item name="org.simalliance.openmobileapi.BIND_TERMINAL" package="com.mediatek" protection="18" />
<item name="android.permission.SET_SCREEN_COMPATIBILITY" package="android" protection="2" />
<item name="android.permission.MEDIA_CONTENT_CONTROL" package="android" protection="18" />
<item name="android.permission.DELETE_PACKAGES" package="android" protection="18" />
<item name="android.permission.MANAGE_WIFI_WHEN_PERMISSION_REVIEW_REQUIRED" package="android" protection="2" />
<item name="com.android.voicemail.permission.ADD_VOICEMAIL" package="android" protection="1" />
</permissions>
- name 表示權(quán)限的名字
- package 表示聲明權(quán)限的package
- protection表示權(quán)限的級(jí)別, 如normal, dangerous之類
解析后,存儲(chǔ)在Settings.mPermissions中
final ArrayMap<String, BasePermission> mPermissions = new ArrayMap<String, BasePermission>();

可以通過pm list permissions -f命令獲取權(quán)限信息
full_k61v1_64_bsp:/ # pm list permissions -f
+ permission:org.simalliance.openmobileapi.BIND_TERMINAL
package:com.mediatek
label:Bind OMAPI Terminal
description:An app with this permission can bind to the System's OMAPI Terminals. Only SmartcardService should use it.
protectionLevel:signature|privileged
+ permission:android.permission.SET_SCREEN_COMPATIBILITY
package:android
label:null
description:null
protectionLevel:signature
+ permission:android.permission.MEDIA_CONTENT_CONTROL
package:android
label:null
description:null
protectionLevel:signature|privileged
+ permission:android.permission.DELETE_PACKAGES
package:android
label:null
description:null
protectionLevel:signature|privileged
+ permission:android.permission.MANAGE_WIFI_WHEN_PERMISSION_REVIEW_REQUIRED
package:android
label:null
description:null
protectionLevel:signature
shared-user字段解析
數(shù)據(jù)格式:
<shared-user name="android.media" userId="10010">
<sigs count="1" schemeVersion="3">
<cert index="4" />
</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.USE_RESERVED_DISK" granted="true" flags="0" />
<item name="android.permission.SEND_DOWNLOAD_COMPLETED_INTENTS" granted="true" flags="0" />
<item name="android.permission.FOREGROUND_SERVICE" 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.GET_TASKS" 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.MANAGE_USB" 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>
</shared-user>
name:應(yīng)用中設(shè)置的android:sharedUserId的名稱,userId為該名稱在系統(tǒng)中對(duì)應(yīng)的uid,定義在Process.java中
sigs 和 package 塊里的意思相同erms 表示這個(gè)user所具有的權(quán)限。在開機(jī)掃描 apk 文件時(shí), 它會(huì)將所有使用了相同 uid 的 app 的權(quán)限收集到一起, 然后放在這里。并且最后還會(huì)把這些權(quán)限再下發(fā)給那些使用了相同 uid 的 app。最后的結(jié)果就是, 系統(tǒng)中使用相同 uid 的 app, 它們具有一樣的權(quán)限
解析后,存儲(chǔ)在
final ArrayMap<String, SharedUserSetting> mSharedUsers = new ArrayMap<String, SharedUserSetting>();
對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)關(guān)系為:

查找關(guān)系為:
通過name="android.media"獲得SharedUserSetting---獲取PermissionsState---獲取對(duì)應(yīng)的所欲權(quán)限mPermissions
----進(jìn)而獲取單個(gè)權(quán)限相關(guān)的數(shù)據(jù)
preferred-activities#####
數(shù)據(jù)格式為:
<preferred-activities>
<item name="com.mx.browser/.MxBrowserActivity" match="200000" set="2">
<set name="com.android.browser/.BrowserActivity" />
<set name="com.mx.browser/.MxBrowserActivity" />
<filter>
<action name="android.intent.action.VIEW" />
<cat name="android.intent.category.BROWSABLE" />
<cat name="android.intent.category.DEFAULT" />
<scheme name="http" />
</filter>
</item>
</preferred-activities>
用于記錄系統(tǒng)中,默認(rèn)的默認(rèn)應(yīng)用activity信息
如下圖,解析的
item.name 對(duì)應(yīng)PreferredComponent.mComponent
<set name=""/>的字段,解析后分別將報(bào)名和activity名稱存入mSetPackages : string[]和mSetClasses : string[]
<set name=""/>整個(gè)字段對(duì)應(yīng)mSetComponents
match:match="200000"

updated-package字段解析
系統(tǒng)應(yīng)用會(huì)涉及到系統(tǒng)內(nèi)部的服務(wù)和關(guān)鍵功能,所以它們一般情況下是不能被刪除的,但是可以被升級(jí)。升級(jí)系統(tǒng)應(yīng)用的手段就是安裝一個(gè)包名相同、但版本更高的應(yīng)用在/data/app/下。Android系統(tǒng)為了區(qū)分這種升級(jí)關(guān)系,內(nèi)部維護(hù)了一個(gè)package.xml文件,它會(huì)記錄系統(tǒng)每次開機(jī)時(shí),當(dāng)前所有APK的關(guān)鍵信息;在packages.xml下,標(biāo)簽<updated-package>來標(biāo)識(shí)這種被升級(jí)的系統(tǒng)應(yīng)用。下次系統(tǒng)再次啟動(dòng)時(shí),還會(huì)再掃描系統(tǒng)中的APK信息,將當(dāng)前掃描的信息與上一次APK掃描的信息(存放在packages.xml中)進(jìn)行對(duì)比,就能得知APK的變化信息;系統(tǒng)再根據(jù)這些變化信息來做具體的APK的升級(jí)、刪除等處理
數(shù)據(jù)格式為:
<updated-package name="com.qiku.multiprocess" codePath="/system/app/QK_MultiProcessProfileML" ft="11e8dc5d800" it="11e8dc5d800" ut="11e8dc5d800" version="1022" nativeLibraryPath="/system/app/QK_MultiProcessProfileML/lib" userId="10087">
<perms>
<item name="android.permission.WRITE_SETTINGS" granted="true" flags="0" />
<item name="android.permission.BIND_DEVICE_ADMIN" granted="true" flags="0" />
<item name="com.qiku.configcenter.permission.SEND_NOTIFICATION" granted="true" flags="0" />
<item name="android.permission.MANAGE_ACCOUNTS" granted="true" flags="0" />
<item name="android.permission.INSTALL_PACKAGES" granted="true" flags="0" />
<item name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" granted="true" flags="0" />
<item name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" granted="true" flags="0" />
.....
</perms>
</updated-package>
<package name="com.qiku.multiprocess" codePath="/data/app/com.qiku.multiprocess-tHL9np5ad3koIxsvEBlHgg==" nativeLibraryPath="/data/app/com.qiku.multiprocess-tHL9np5ad3koIxsvEBlHgg==/lib" publicFlags="944258695" privateFlags="0" ft="16db9783d40" it="11e8dc5d800" ut="16db978414d" version="1022" userId="10087" installer="root" isOrphaned="true">
<sigs count="1" schemeVersion="1">
<cert index="2" />
</sigs>
<perms>
<item name="android.permission.WRITE_SETTINGS" granted="true" flags="0" />
<item name="android.permission.BIND_DEVICE_ADMIN" granted="true" flags="0" />
<item name="com.qiku.configcenter.permission.SEND_NOTIFICATION" granted="true" flags="0" />
<item name="android.permission.MANAGE_ACCOUNTS" granted="true" flags="0" />
<item name="android.permission.INSTALL_PACKAGES" granted="true" flags="0" />
....
<item name="android.permission.DELETE_PACKAGES" granted="true" flags="0" />
</perms>
<proper-signing-keyset identifier="3" />
</package>
updated-package記錄應(yīng)用升級(jí)前的信息,package記錄升級(jí)后的apk信息。實(shí)際運(yùn)行的是package中的apk的信息,updated-package用來記錄被升級(jí)系統(tǒng)應(yīng)用的原始信息
updated-package中的信息被存儲(chǔ)在mDisabledSysPackages數(shù)據(jù)結(jié)構(gòu)中,存儲(chǔ)的信息數(shù)據(jù)結(jié)構(gòu)也是PackageSetting,跟package一樣
private final ArrayMap<String, PackageSetting> mDisabledSysPackages =
new ArrayMap<String, PackageSetting>();
keyset-settings
用來存儲(chǔ)系統(tǒng)中所有應(yīng)用使用的公鑰信息
數(shù)據(jù)格式為:
<keyset-settings version="1">
<keys>
<public-key identifier="1" value="MIIBIDANBgk...xUugvhaRXU89fwZBxxe7IJwIBAw==" />
...
<public-key identifier="16" value="MIGfM...QAB" />
<public-key identifier="17" value="MIGf...AQAB" />
<public-key identifier="18" value="MIGf...DAQAB" />
<public-key identifier="19" value="MIG...AQAB" />
<public-key identifier="20" value="MIGf...AQAB" />
</keys>
接續(xù)后存儲(chǔ)在一個(gè)LongSparseArray數(shù)據(jù)結(jié)構(gòu)中,讀取packages.xml中package數(shù)據(jù)信息的時(shí)候,會(huì)通過 <proper-signing-keyset identifier="3" />的identifier
查詢KeySetManagerService.mPublicKeys
KeySetManagerService.java
private final LongSparseArray<PublicKeyHandle> mPublicKeys;
mPendingPackages數(shù)據(jù)的處理
以上字段解析完畢后,會(huì)將Settings.mPendingPackages的數(shù)據(jù)添加到Settings.mPackages中去,然后清除mPendingPackages的數(shù)據(jù)
final int N = mPendingPackages.size();
for (int i = 0; i < N; i++) {
final PackageSetting p = mPendingPackages.get(i);
final int sharedUserId = p.getSharedUserId();
final Object idObj = getUserIdLPr(sharedUserId);
if (idObj instanceof SharedUserSetting) {//將mPendingPackages的SharedUserSetting數(shù)據(jù),添加到mPacakages中去
final SharedUserSetting sharedUser = (SharedUserSetting) idObj;
p.sharedUser = sharedUser;
p.appId = sharedUser.userId;
addPackageSettingLPw(p, sharedUser);
} else if (idObj != null) {
String msg = "Bad package setting: package " + p.name + " has shared uid "
+ sharedUserId + " that is not a shared uid\n";
mReadMessages.append(msg);
PackageManagerService.reportSettingsProblem(Log.ERROR, msg);
} else {
String msg = "Bad package setting: package " + p.name + " has shared uid "
+ sharedUserId + " that is not defined\n";
mReadMessages.append(msg);
PackageManagerService.reportSettingsProblem(Log.ERROR, msg);
}
}
mPendingPackages.clear();//清除mPendingPackages的數(shù)據(jù)
writeKernelMappingLPr
final File kernelDir = new File("/config/sdcardfs");
mKernelMappingFilename = kernelDir.exists() ? kernelDir : null;
void writeKernelMappingLPr() {
if (mKernelMappingFilename == null) return;
//遍歷/config/sdcardfs目錄,獲取文件名。文件名為各個(gè)應(yīng)用的包名
final String[] known = mKernelMappingFilename.list();
final ArraySet<String> knownSet = new ArraySet<>(known.length);
for (String name : known) {
knownSet.add(name);
}
for (final PackageSetting ps : mPackages.values()) {
// Package is actively claimed
knownSet.remove(ps.name);//刪除已安裝的應(yīng)用的數(shù)據(jù),保留未安裝的應(yīng)用數(shù)據(jù),后續(xù)處理
writeKernelMappingLPr(ps);
}
// Remove any unclaimed mappings
for (int i = 0; i < knownSet.size(); i++) {
final String name = knownSet.valueAt(i);
if (DEBUG_KERNEL) Slog.d(TAG, "Dropping mapping " + name);
//刪除未安裝的應(yīng)用的數(shù)據(jù),和文件。也就是處理臟數(shù)據(jù)
mKernelMapping.remove(name);
new File(mKernelMappingFilename, name).delete();
}
}
private static final class KernelPackageState {
int appId;
int[] excludedUserIds;
}
/** Map from package name to appId and excluded userids */
private final ArrayMap<String, KernelPackageState> mKernelMapping = new ArrayMap<>();
void writeKernelMappingLPr(PackageSetting ps) {
if (mKernelMappingFilename == null || ps == null || ps.name == null) return;
//查詢緩存中,是否含有改應(yīng)用
KernelPackageState cur = mKernelMapping.get(ps.name);
final boolean firstTime = cur == null;//如果,緩存中沒有數(shù)據(jù),則表明是第一次
int[] excludedUserIds = ps.getNotInstalledUserIds();//得到應(yīng)用禁止在其他多用戶空間安裝的用戶空間userid
final boolean userIdsChanged = firstTime
|| !Arrays.equals(excludedUserIds, cur.excludedUserIds);//判斷應(yīng)用的uid是否發(fā)生變化
// Package directory
final File dir = new File(mKernelMappingFilename, ps.name);
if (firstTime) {//如果,應(yīng)用是第一次執(zhí)行該操作,則創(chuàng)建應(yīng)用對(duì)應(yīng)目錄,并保存對(duì)應(yīng)的KernelPackageState對(duì)象在內(nèi)存中
dir.mkdir();
// Create a new mapping state
cur = new KernelPackageState();
mKernelMapping.put(ps.name, cur);
}
// If mapping is incorrect or non-existent, write the appid file
if (cur.appId != ps.appId) {//更新應(yīng)用的appid,數(shù)據(jù)存儲(chǔ)在/config/sdcardfs/pkgname/appid 文件中
final File appIdFile = new File(dir, "appid");
writeIntToFile(appIdFile, ps.appId);
if (DEBUG_KERNEL) Slog.d(TAG, "Mapping " + ps.name + " to " + ps.appId);
}
if (userIdsChanged) {//如果firstTime,或者excludedUserIds發(fā)生變化
// Build the exclusion list -- the ids to add to the exclusion list
//excludeUserIds發(fā)生變化,更新/config/sdcardfs/pkgname/excluded_userids文件中的數(shù)據(jù)
for (int i = 0; i < excludedUserIds.length; i++) {
if (cur.excludedUserIds == null || !ArrayUtils.contains(cur.excludedUserIds,
excludedUserIds[i])) {
writeIntToFile(new File(dir, "excluded_userids"), excludedUserIds[i]);
if (DEBUG_KERNEL) Slog.d(TAG, "Writing " + excludedUserIds[i] + " to "
+ ps.name + "/excluded_userids");
}
}
// Build the inclusion list -- the ids to remove from the exclusion list
//excludedUserIds表示當(dāng)前應(yīng)用當(dāng)前的id數(shù)據(jù)
//cur.excludedUserIds[i] 表示上一次緩存的數(shù)據(jù)
//相比上次去除的userid,存儲(chǔ)在/config/sdcardfs/pkgname/clear_userid文件中
if (cur.excludedUserIds != null) {
for (int i = 0; i < cur.excludedUserIds.length; i++) {
if (!ArrayUtils.contains(excludedUserIds, cur.excludedUserIds[i])) {
writeIntToFile(new File(dir, "clear_userid"),
cur.excludedUserIds[i]);
if (DEBUG_KERNEL) Slog.d(TAG, "Writing " + cur.excludedUserIds[i] + " to "
+ ps.name + "/clear_userid");
}
}
}
cur.excludedUserIds = excludedUserIds;
}
}
以搜狐輸入法為例,看下對(duì)應(yīng)目錄下文件存儲(chǔ)的數(shù)據(jù)
130|full_k61v1_64_bsp:/config/sdcardfs # cd com.sohu.inputmethod.sogou
full_k61v1_64_bsp:/config/sdcardfs/com.sohu.inputmethod.sogou # ls
appid clear_userid excluded_userids
//當(dāng)前搜狗輸入法的appid
full_k61v1_64_bsp:/config/sdcardfs/com.sohu.inputmethod.sogou # cat appid
10075
//表示搜狗輸入法,禁止在那些多用戶空間安裝
full_k61v1_64_bsp:/config/sdcardfs/com.sohu.inputmethod.sogou # cat excluded_userids <
1003 1002 1001 1000 999 998 997
//文件內(nèi)容無權(quán)限查看,內(nèi)容是當(dāng)前excluded_userids 文件內(nèi)容與上次文件內(nèi)容的差集
full_k61v1_64_bsp:/config/sdcardfs/com.sohu.inputmethod.sogou # cat clear_userid
cat: clear_userid: Permission denied
驗(yàn)證系統(tǒng)指紋
final VersionInfo ver = mSettings.getInternalVersion();
//系統(tǒng)通過判斷當(dāng)前手機(jī)版本的指紋和上一個(gè)版本的指紋信息是否相同,來判斷是否屬于系統(tǒng)升級(jí)
mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint);
if (mIsUpgrade) {
logCriticalInfo(Log.INFO,
"Upgrading from " + ver.fingerprint + " to " + Build.FINGERPRINT);
}
// when upgrading from pre-M, promote system app permissions from install to runtime
//如果,系統(tǒng)從M版本升級(jí)上來,則將系統(tǒng)app的權(quán)限從安裝權(quán)限提升到運(yùn)行時(shí)權(quán)限
mPromoteSystemApps =
mIsUpgrade && ver.sdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1;
// When upgrading from pre-N, we need to handle package extraction like first boot,
// as there is no profiling data available.
//用于處理從N版本升級(jí)上來的情況
mIsPreNUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N;
mIsPreNMR1Upgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N_MR1;
// 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) {//處理從M版本升級(jí)上來時(shí),系統(tǒng)app的權(quán)限問題
Iterator<PackageSetting> pkgSettingIter = mSettings.mPackages.values().iterator();
while (pkgSettingIter.hasNext()) {
PackageSetting ps = pkgSettingIter.next();
if (isSystemApp(ps)) {
mExistingSystemPackages.add(ps.name);
}
}
}
mCacheDir = preparePackageParserCache(mIsUpgrade);
如果是系統(tǒng)判斷是升級(jí)了,則會(huì)刪除/data/system/package_cache目錄的應(yīng)用緩存數(shù)據(jù)
private static File preparePackageParserCache(boolean isUpgrade) {
....
// If this is a system upgrade scenario, delete the contents of the package cache dir.
// This also serves to "GC" unused entries when the package cache version changes (which
// can only happen during upgrades).
if (isUpgrade) {
FileUtils.deleteContents(cacheBaseDir);
}
關(guān)于系統(tǒng)版本指紋驗(yàn)證,在實(shí)際項(xiàng)目中遇到一個(gè)問題:
很多手機(jī)廠商,因?yàn)楦鞣N原因,在OTA升級(jí)時(shí),會(huì)固定系統(tǒng)指紋信息; 也即,在升級(jí)前和升級(jí)后,將系統(tǒng)指紋信息保持不變
這就導(dǎo)致了一個(gè)問題:
Android8.0開始增加了一個(gè)應(yīng)用cache路徑/data/system/package_cache,如果是系統(tǒng)升級(jí)了,則會(huì)清除這個(gè)cache下的緩存信息
如果fingerprint未變,isUpgrade始終為false,pms就會(huì)繼續(xù)讀取這個(gè)緩存信息
如果在升級(jí)過程中,某個(gè)應(yīng)用更新了組件相關(guān)信息,因?yàn)樽x取的還是緩存的應(yīng)用組件信息,則apk就會(huì)報(bào)錯(cuò)找不到相關(guān)組件
另外OTA之后進(jìn)行一次恢復(fù)出廠設(shè)置之后應(yīng)用也能恢復(fù)更新后的基線,因?yàn)榛謴?fù)出廠設(shè)置之后data下面的數(shù)據(jù)會(huì)被清除
總結(jié)
上述流程完成的主要工作如下:
- 簡(jiǎn)單初始化PMS依賴的相關(guān)變量
- addSharedUserLPw創(chuàng)建共享uid相關(guān)的數(shù)據(jù)結(jié)構(gòu)
- 初始化dex優(yōu)化服務(wù)類,解析SystemConfig文件
- SystemConfig讀取配置feature配置,系統(tǒng)共享庫,uid權(quán)限對(duì)應(yīng)關(guān)系等數(shù)據(jù)
- 創(chuàng)建PMS的HandlerThread和系統(tǒng)共享庫處理
- readLPw解析packages.xml文件
- 驗(yàn)證系統(tǒng)指紋
本章節(jié),主要解析PMS初始化過程中,解析packages.xml的文件內(nèi)容,下一章節(jié),我們重點(diǎn)解析PMS掃描apk的過程