8.2 PackageManagerService的启动过程
PackageManagerService的启动过程非常复杂,是Package Manager的核心组成部分,Android包管理器的一切功能都基于该步骤的处理结果。
在这个复杂的启动过程中,涉及Settings对象、属性系统、Installer系统、Package Handler、系统permission和feature信息、AndroidManifest.xml、Resource、dexopt操作、FileObserver以及APK包安装扫描等一系列操作。其启动流程如图8-4所示。
图 8-4 PackageManagerService启动过程
本节承接上一节的内容分析PackageManagerService的启动过程。
8.2.1 创建并初始化Settings对象
PackageManagerService启动的第一步是创建和设置Settings对象。Settings对象保存包的运行信息,代码如下:
public PackageManagerService(Context context, boolean factoryTest, boolean onlyCore){
……
mSettings=new Settings();
mSettings.addSharedUserLPw("android.uid.system",Process.SYSTEM_UID,
ApplicationInfo.FLAG_SYSTEM);
mSettings.addSharedUserLPw("android.uid.phone",RADIO_UID,
ApplicationInfo.FLAG_SYSTEM);
mSettings.addSharedUserLPw("android.uid.log",LOG_UID,
ApplicationInfo.FLAG_SYSTEM);
mSettings.addSharedUserLPw("android.uid.nfc",NFC_UID,
ApplicationInfo.FLAG_SYSTEM);
Settings初始化阶段包括以下两部分工作:
1)调用构造函数初始化。
2)调用addSharedUserLPw方法添加4个默认共享用户ID。
Settings定义位于frameworks/base/services/java/com/android/server/pm/Settings.java中。接下来分别分析Settings初始化阶段的两部分工作。
1.调用构造函数初始化
在构造函数初始化过程中,主要初始化5个全局文件目录,其代码如下:
final class Settings{
……
Settings(){
this(Environment.getDataDirectory());
}
Settings(File dataDir){
mSystemDir=new File(dataDir,"system");
mSystemDir.mkdirs();
FileUtils.setPermissions(mSystemDir.toString(),
FileUtils.S_IRWXU|FileUtils.S_IRWXG
|FileUtils.S_IROTH|FileUtils.S_IXOTH,-1,-1);
mSettingsFilename=new File(mSystemDir,"packages.xml");
mBackupSettingsFilename=new File(mSystemDir,"packages-backup.xml");
mPackageListFilename=new File(mSystemDir,"packages.list");
mStoppedPackagesFilename=new File(mSystemDir,
"packages-stopped.xml");
mBackupStoppedPackagesFilename=new File(mSystemDir,
"packages-stopped-backup.xml");
}
……
具体说明如下:
mSettingsFilename初始化为/data/system/packages.xml。
mBackupSettingsFilename初始化为/data/system/packages-backup.xml。
mPackageListFilename初始化为/data/system/packages.list。
mStoppedPackagesFilename初始化为/data/system/packages-stopped.xml。
mBackupStoppedPackagesFilename初始化为/data/system/packages-stopped-backup.xml。
packages. xml中记录了系统中所有已安装的APK的运行信息。
packages-backup. xml是packages.xml的备份文件,用于安装和卸载APK导致需要更新packages.xml时,临时备份packages.xml。
packages. list中记录了系统中所有已安装APK的简略信息。
packages-stopped. xml中记录强制stop的应用程序信息,其备份文件为packages-stopped-backup.xml,这两个文件在Jelly Bean中已经标记为废弃。
2.添加默认共享用户ID
mSettings. addSharedUserLPw方法的代码如下:
final HashMap<String, SharedUserSetting>mSharedUsers=
new HashMap<String, SharedUserSetting>();
private final ArrayList<Object>mUserIds=new ArrayList<Object>();
private final SparseArray<Object>mOtherUserIds=
new SparseArray<Object>();
……
SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags){
SharedUserSetting s=mSharedUsers.get(name);
//如果Settings的成员变量mSharedUsers中已经存在该共享用户信息,则直接返回
if(s!=null){
if(s.userId==uid){//uid和name是一一对应的
return s;
}
//如果name和uid不是一一对应的,则直接返回null,这样仅保留第一次添加的信息
PackageManagerService.reportSettingsProblem(Log.ERROR,
"Adding duplicate shared user, keeping first:"+name);
return null;
}
//以传入的三个参数创建SharedUserSetting
s=new SharedUserSetting(name, pkgFlags);
s.userId=uid;
if(addUserIdLPw(uid, s,name)){
mSharedUsers.put(name, s);//将SharedUserSetting存入mSharedUsers
return s;
}
return null;
}
……
private boolean addUserIdLPw(int uid, Object obj, Object name){
//LAST_APPLICATION_UID为19999,表示应用程序的最大UID
if(uid>Process.LAST_APPLICATION_UID){
return false;
}
//FIRST_APPLICATION_UID为10000,表示应用程序的最小UID
if(uid>=Process.FIRST_APPLICATION_UID){
int N=mUserIds.size();
//以当前UID值与最小UID的差表示该UID在mUserIds中的位置
final int index=uid-Process.FIRST_APPLICATION_UID;
//在mUserIds中找到该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);//obj即SharedUserSetting
}else{//将UID小于10000的共享用户存入mOtherUserIds中
if(mOtherUserIds.get(uid)!=null){
PackageManagerService.reportSettingsProblem(Log.ERROR,
"Adding duplicate shared id:"+uid+"name="+name);
return false;
}
mOtherUserIds.put(uid, obj);
}
return true;
}
addSharedUserLPw首先检查mSettings.mSharedUsers变量中是否存在这4个共享用户名(共享用户名即addSharedUserLPw方法的第一个参数),如果不存在,以addSharedUserLPw方法的三个参数为值,创建一个SharedUserSetting类型的对象。对象创建完成后调用Settings.addUserIdLPw方法,在该方法中首先判断SharedUserSetting对象的UID大小。
如果uid>=FIRST_APPLICATION_UID,将该SharedUserSetting对象存入mSettings.mUserIds中。
如果uid<FIRST_APPLICATION_UID,将该SharedUserSetting对象存入mSettings.mOtherUserIds中。
FIRST_APPLICATION_UID常量定义的是Java应用程序的起始UID,其值为10000。UID<10000代表系统程序的UID。这4个共享用户名对应的UID都小于10000,均对应系统UID和系统权限。
通过以上分析,Settings初始化阶段建立了如图8-5所示的类关系结构。
图 8-5 Settings初始化阶段类关系结构
图8-5的类关系可以归纳如下:
Settings提供了mSharedUsers、mUserIds和mOtherUserIds三个成员变量用于存储共享用户信息。
mSharedUsers是HashMap<String,SharedUserSetting>类型的成员变量,以共享用户name为键,以共享用户信息SharedUserSetting为值存储共享用户信息。
mUserIds是ArrayList<Object>类型的成员变量,以大于或等于FIRST_APPLICATION_UID的共享用户UID为键,以共享用户信息SharedUserSetting为值存储共享用户信息。
mOtherUserIds是SparseArray<Object>类型的成员变量,以小于FIRST_APPLICATION_UID的共享用户UID为键,以共享用户信息SharedUserSetting为值存储共享用户信息。
共享用户信息以SharedUserSetting类表示,其成员变量name存储共享用户名,成员变量userId存储共享用户UID,成员变量packages存储使用该共享用户的包信息。
包信息由PackageSetting类表示,多个包可以使用同一共享用户信息。
包信息和共享用户信息都继承自GrantedPermissions类,该类定义共享用户和包的权限信息。
注意 SparseArray是Android提供的数据类型,以int类型为键存储对象,效率相对较高。
Settings中定义了很多属性和方法,本节只分析了Settings初始化使用的部分,在其他小节中再对其他属性或方法做详细解释。
注意 类图是UML中的核心组件,如果读者不熟悉UML,可以在Eclipse中使用Ctrl+O组合键查看类的结构信息。