代码基于MTK Android 9.x
目录结构
1 以上过程的主要调用序列图如下:
2 mOnlyCore
3 SystemServer.startBootstrapServices
4 PackageManagerService.main
5 new PackageManagerService
5.1 简单初始化工作
5.2 addSharedUserLPw
5.2.1 uid和权限的关系
5.3 初始化dex优化服务类,解析SystemConfig文件
5.4 SystemConfig读取配置文件
5.4.1 feature配置文件
5.4.2 系统库文件
5.4.3 权限和gid映射文件
5.4.4 assign-permission
5.4.5 allow-in-power-save
5.5 创建PMS的HandlerThread和系统共享库处理
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数据的处理
5.6.3 writeKernelMappingLPr
5.7 验证系统指纹
6 总结
以上过程的主要调用序列图如下:
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;
}
只有当vold.decrypt属性的值为"trigger_restart_min_framework"和“1”时,mOnlyCore才为true
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
coreApp="true"
package="com.qiku.xt_security">
mOnlyCore为true表示,PMS只会对声明了 coreApp="true"的应用进行解析
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:表示是否运行在工厂模式
onlyCore:表示是否运行在只解析core应用的模式下,也就是加密模式下
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对象,并添加到ServiceManager中去
new PackageManagerService
简单初始化工作
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,用于管理应用的权限
初始化mDefaultPermissionPolicy,用于管理平台组件默认获取运行时权限。例如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主要用于初始化一个SharedUserSetting数据结构,用于保存声明了同一个android:sharedUserId="android.uid.system" 的应用信息
final ArrayMap<String, SharedUserSetting> mSharedUsers = new ArrayMap<String, SharedUserSetting>();
上图中的userId,指的是uid
一个应用声明了与其他应用共享uid,主要有以下几个作用:
两个或多个声明了同一种sharedUserIds的APK可共享彼此的数据,并且可运行在同一进程中
更重要的是,通过声明特定的sharedUserId,该APK所在进程将被赋予指定的UID; SystemUI声明了system的uid,运行SystemUI的进程就可享有system用户所对应的权限(实际上就是将该进程的uid设置为system的uid)了
AndroidManifest.xml中声明sharedUserId外,APK在编译时还必须使用对应的证书进行签名。例如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) {//普通应用,其值大于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 {//系统应用,其值小于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;
}
除了创建一个map外,settings中还会创建另外两个数据结构,mUserIds和mOtherUserIds,这两个数据结构,通过数组下表映射进行查询,速度比mSharedUsers更快
mUserIds是一个ArrayList,存储普通应用,以(uid-Process.FIRST_APPLICATION_UID)为index,SharedUserSetting为value
mOtherUserIds是一个,SparseArray,存储系统应用,以uid为index,SharedUserSetting为value
uid和权限的关系
之所以不同的应用,通过指定相同的uid,可以共享权限,是因为,系统中是通过uid为映射,存储应用的权限数据的。不同的应用,具有同一个uid,查到的权限数据是相同的
初始化dex优化服务类,解析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优化相关类
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,解析各个目录下/etc/permissions和/etc/sysconfig下的各个配置文件
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);
....
}
读取的文件目录的顺序为/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..文件中中声明了手机能够支持的的硬件特性,例如支持camera、支持蓝牙等
数据格式为:
<permissions>
<feature name="android.software.webview" />
</permissions>
解析后的数据存储在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查看手机支持的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
系统库文件
library声明的是系统提供的Java库,应用程序运行时候必须要链接这些库,该工作由系统自动完成
数据格式为:
<permissions>
<library name="com.android.media.remotedisplay"
file="/system/framework/com.android.media.remotedisplay.jar" />
</permissions>
数据解析后,存储在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命令,查看系统中支持的系统库
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
权限和gid映射文件
permission和group用于建立Linux层gid和Android层pemission之间的映射关系
具有android.permission.BLUETOOTH_ADMIN权限的应用,将获得net_bt_admin组权限
数据格式为:
<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的名称和数字的对应是在system\core\include\private\android_filesystem_config.h中定义的
#define AID_NET_BT 3002 /* bluetooth: create sco, rfcomm or l2cap sockets */
解析后,数据存储在mPermissions中,PermissionEntry存储了改权限关联的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
赋予对应uid相应的权限。如果下面一行表示uid为media,那么就赋予它ACCESS_SURFACE_FLINGER的权限,其实就是把它加到对应的用户组中
数据格式为:
<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数据结构中
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
允许应用在省电模式下,访问网络
数据格式为:
<!-- 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" />
解析后,存储在
final ArraySet<String> mAllowInPowerSave = new ArraySet<>();
创建PMS的HandlerThread和系统共享库处理
//创建PMS的HanderThread,进行异步任务的处理
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);
//获取上文所说的系统共享库
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中各个参数的意义:
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) {//如果是静态类型的系统共享包,各个类型的意义见下面代码解释
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;
上述代码逻辑对应的数据结构为:
final ArrayMap<String, LongSparseArray<SharedLibraryEntry>> mSharedLibraries = new ArrayMap<>();
( name="com.android.media.remotedisplay" LongSparseArray<SharedLibraryEntry>)
(int version, SharedLibraryEntry)
即可以通过name-->得到该系统共享包对应的各个版本的SharedLibraryEntry信息
readLPw
LPw, local printwrite
改方法主要对三个文件进行读取
packages.xml,packages_backup.xml: PKMS扫描完目标文件夹后会创建该文件。
当系统进行程序安装、卸载和更新等操作时,均会更新该文件。该文件保存了系统中与package相关的一些信息。
backup是临时文件。PKMS先把数据写到backup中,信息都写成功后再改名成非backup的文件。
其目的是防止在写文件过程中出错,导致信息丢失;如果存在backup文件,则表示写入过程中,被异常中指。packages.list:描述系统中存在的所有非系统自带的APK的信息。当这些程序有变动时,PKMS就会更新该文件。
packages-stopped.xml:从系统自带的设置程序中进入应用程序页面,然后在选择强制停止(ForceStop)某个应用时,系统会将该应用的相关信息记录到此文件中。也就是该文件保存系统中被用户强制停止的Package的信息。
packages-stopped.xml文件,在Android 9去除了
在该方法开始的时候,会判断packages.xml,packages_backup.xml是否存在,如果都不能存在的话,会直接返回false
判断文件是否存在
Log.d(TAG, Log.getStackTraceString(new Throwable()));
FileInputStream str = null;
if (mBackupSettingsFilename.exists()) {
try {
//如果packages_backup.xml文件存在,则设置成,读取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两个文件都存在,则删除packages.xml文件,因为,正常的文件写入流程可能已经被中断
// 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.
}
}
//清楚本都数据信息,每个属性代表的字段,后面讲解
mPendingPackages.clear();
mPastSignatures.clear();
mKeySetRefs.clear();
mInstallerPackages.clear();
try {
if (str == null) {
//如果packages_backup.xml和packages.xml两个文件都不存在,则读取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
//获取系统指纹,此时packages.xml文件不存在,从系统属性中获取系统指纹
findOrCreateVersion(StorageManager.UUID_PRIVATE_INTERNAL).forceCurrent();
findOrCreateVersion(StorageManager.UUID_PRIMARY_PHYSICAL).forceCurrent();
return false;
}
str = new FileInputStream(mSettingsFilename);
}
获取系统指纹的方法为:
先判断ro.build.fingerprint属性的值,不为空的情况下,通过多个属性值进行拼接
处于各种目的,有的项目会将ro.build.fingerprint设置成固定的值
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文件,如下所示,我们一个一个的分析,packages.xml文件的各个字段的解析过程
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字段解析
数据格式为:
<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>
** 各个字段意义: **
- name:应用包名
- codePath:应用安装位置,可以安装在:/system/priv-app/,/system/app/,/vendor/overlay,/system/presetapp,/data/app目录
- nativeLibraryPath:表示 app 使用的 xxx.so 库存放的位置
- primaryCpuAbi: app 以哪种 abi 架构运行
- ft 表示 apk 文件上次被更改的时间, it 表示 app 第一次安装的时间, ut 表示 app 上次被更新时间, 它的值好像一直和 ft 相同, ota 或 app 重装之后, 这里的ft和ut可能会改变。
- sharedUserId:应用设置的shanre uid,没有设置的情况下,该字段为:userid
- isOrphaned:Indicates if the package that installed this app has been uninstalled
- version 是 app 的版本号信息, 也就是在 AndroidManifest.xml 里配置的 android:versioncode
- sigs 块里的 count 表示这个 app 有多少个签名信息, 有的 app 可能会被多个证书签名。cert 里的 index 表示这个 app 使用的证书的序号, 当系统发现一个新的证书, 这个号就会加1, key 是 app 使用的证书内容的 ascii 码值。PKMS 在扫 apk 文件过程中, 如果发现它和之前扫描到的 apk 使用的是相同的签名证书, 这里就只会有个 index 的值, 并没有 key 的值。拥有相同的 index 的 package, 表明它们使用的是相同的签名
<sigs count="1" schemeVersion="3">
<cert index="0" key="308204a8...90b1b357" />
</sigs>
perms 块里是这个 app 拥有的权限, 对于一般的 app, 这些权限是在 AndroidManifest.xml 里写明的; 那些使用了相同 UID 的 app, 这里的权限就是所有使用相同 UID 的 app 申请的权限的总和。 granted表示这个权限是不是已经被允许
proper-signing-keyset 里的 identifier 就是packages.xml中的的 keysets 里 identifier 的值。它是用来标明这个 app 使用的是哪个公钥。
<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 表示该应用的安装来源,默认集成的应用没有该字段
** Settings.mPackages **
以上信心,解析后,存储在Settings.PackageSetting对象中,添加到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上次被修改的时间
packageSetting.firstInstallTime = firstInstallTime;//it, 第一次被安装的时间
packageSetting.lastUpdateTime = lastUpdateTime;//ut, 上一次更新的时间
}
** Settings.mPendingPackages **
如果应用声明了android:sharedUserId,则将应用添加到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配置项的解析过程,和相关的数据结构。字段较短,没有一一说明,后续遇到具体场景再具体分析
对应的数据结构为:
permissions字段解析
** 数据格式为:**
<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 表示权限的名字
- package 表示声明权限的package
- protection表示权限的级别, 如normal, dangerous之类
解析后,存储在Settings.mPermissions中
final ArrayMap<String, BasePermission> mPermissions = new ArrayMap<String, BasePermission>();
可以通过pm list permissions -f命令获取权限信息
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字段解析
数据格式:
<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:应用中设置的android:sharedUserId的名称,userId为该名称在系统中对应的uid,定义在Process.java中
sigs 和 package 块里的意思相同erms 表示这个user所具有的权限。在开机扫描 apk 文件时, 它会将所有使用了相同 uid 的 app 的权限收集到一起, 然后放在这里。并且最后还会把这些权限再下发给那些使用了相同 uid 的 app。最后的结果就是, 系统中使用相同 uid 的 app, 它们具有一样的权限
解析后,存储在
final ArrayMap<String, SharedUserSetting> mSharedUsers = new ArrayMap<String, SharedUserSetting>();
对应的数据结构关系为:
查找关系为:
通过name="android.media"获得SharedUserSetting---获取PermissionsState---获取对应的所欲权限mPermissions
----进而获取单个权限相关的数据
preferred-activities#####
数据格式为:
<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>
用于记录系统中,默认的默认应用activity信息
如下图,解析的
item.name 对应PreferredComponent.mComponent
<set name=""/>的字段,解析后分别将报名和activity名称存入mSetPackages : string[]和mSetClasses : string[]
<set name=""/>整个字段对应mSetComponents
match:match="200000"
updated-package字段解析
系统应用会涉及到系统内部的服务和关键功能,所以它们一般情况下是不能被删除的,但是可以被升级。升级系统应用的手段就是安装一个包名相同、但版本更高的应用在/data/app/下。Android系统为了区分这种升级关系,内部维护了一个package.xml文件,它会记录系统每次开机时,当前所有APK的关键信息;在packages.xml下,标签<updated-package>来标识这种被升级的系统应用。下次系统再次启动时,还会再扫描系统中的APK信息,将当前扫描的信息与上一次APK扫描的信息(存放在packages.xml中)进行对比,就能得知APK的变化信息;系统再根据这些变化信息来做具体的APK的升级、删除等处理
数据格式为:
<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记录应用升级前的信息,package记录升级后的apk信息。实际运行的是package中的apk的信息,updated-package用来记录被升级系统应用的原始信息
updated-package中的信息被存储在mDisabledSysPackages数据结构中,存储的信息数据结构也是PackageSetting,跟package一样
private final ArrayMap<String, PackageSetting> mDisabledSysPackages =
new ArrayMap<String, PackageSetting>();
keyset-settings
用来存储系统中所有应用使用的公钥信息
数据格式为:
<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>
接续后存储在一个LongSparseArray数据结构中,读取packages.xml中package数据信息的时候,会通过 <proper-signing-keyset identifier="3" />的identifier
查询KeySetManagerService.mPublicKeys
KeySetManagerService.java
private final LongSparseArray<PublicKeyHandle> mPublicKeys;
mPendingPackages数据的处理
以上字段解析完毕后,会将Settings.mPendingPackages的数据添加到Settings.mPackages中去,然后清除mPendingPackages的数据
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数据,添加到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的数据
writeKernelMappingLPr
final File kernelDir = new File("/config/sdcardfs");
mKernelMappingFilename = kernelDir.exists() ? kernelDir : null;
void writeKernelMappingLPr() {
if (mKernelMappingFilename == null) return;
//遍历/config/sdcardfs目录,获取文件名。文件名为各个应用的包名
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);//删除已安装的应用的数据,保留未安装的应用数据,后续处理
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);
//删除未安装的应用的数据,和文件。也就是处理脏数据
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;
//查询缓存中,是否含有改应用
KernelPackageState cur = mKernelMapping.get(ps.name);
final boolean firstTime = cur == null;//如果,缓存中没有数据,则表明是第一次
int[] excludedUserIds = ps.getNotInstalledUserIds();//得到应用禁止在其他多用户空间安装的用户空间userid
final boolean userIdsChanged = firstTime
|| !Arrays.equals(excludedUserIds, cur.excludedUserIds);//判断应用的uid是否发生变化
// Package directory
final File dir = new File(mKernelMappingFilename, ps.name);
if (firstTime) {//如果,应用是第一次执行该操作,则创建应用对应目录,并保存对应的KernelPackageState对象在内存中
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) {//更新应用的appid,数据存储在/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发生变化
// Build the exclusion list -- the ids to add to the exclusion list
//excludeUserIds发生变化,更新/config/sdcardfs/pkgname/excluded_userids文件中的数据
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表示当前应用当前的id数据
//cur.excludedUserIds[i] 表示上一次缓存的数据
//相比上次去除的userid,存储在/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;
}
}
以搜狐输入法为例,看下对应目录下文件存储的数据
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
//当前搜狗输入法的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
//文件内容无权限查看,内容是当前excluded_userids 文件内容与上次文件内容的差集
full_k61v1_64_bsp:/config/sdcardfs/com.sohu.inputmethod.sogou # cat clear_userid
cat: clear_userid: Permission denied
验证系统指纹
final VersionInfo ver = mSettings.getInternalVersion();
//系统通过判断当前手机版本的指纹和上一个版本的指纹信息是否相同,来判断是否属于系统升级
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
//如果,系统从M版本升级上来,则将系统app的权限从安装权限提升到运行时权限
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版本升级上来的情况
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版本升级上来时,系统app的权限问题
Iterator<PackageSetting> pkgSettingIter = mSettings.mPackages.values().iterator();
while (pkgSettingIter.hasNext()) {
PackageSetting ps = pkgSettingIter.next();
if (isSystemApp(ps)) {
mExistingSystemPackages.add(ps.name);
}
}
}
mCacheDir = preparePackageParserCache(mIsUpgrade);
如果是系统判断是升级了,则会删除/data/system/package_cache目录的应用缓存数据
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);
}
关于系统版本指纹验证,在实际项目中遇到一个问题:
很多手机厂商,因为各种原因,在OTA升级时,会固定系统指纹信息; 也即,在升级前和升级后,将系统指纹信息保持不变
这就导致了一个问题:
Android8.0开始增加了一个应用cache路径/data/system/package_cache,如果是系统升级了,则会清除这个cache下的缓存信息
如果fingerprint未变,isUpgrade始终为false,pms就会继续读取这个缓存信息
如果在升级过程中,某个应用更新了组件相关信息,因为读取的还是缓存的应用组件信息,则apk就会报错找不到相关组件
另外OTA之后进行一次恢复出厂设置之后应用也能恢复更新后的基线,因为恢复出厂设置之后data下面的数据会被清除
总结
上述流程完成的主要工作如下:
- 简单初始化PMS依赖的相关变量
- addSharedUserLPw创建共享uid相关的数据结构
- 初始化dex优化服务类,解析SystemConfig文件
- SystemConfig读取配置feature配置,系统共享库,uid权限对应关系等数据
- 创建PMS的HandlerThread和系统共享库处理
- readLPw解析packages.xml文件
- 验证系统指纹
本章节,主要解析PMS初始化过程中,解析packages.xml的文件内容,下一章节,我们重点解析PMS扫描apk的过程