8.2 PackageManagerService的启动过程

PackageManagerService的启动过程非常复杂,是Package Manager的核心组成部分,Android包管理器的一切功能都基于该步骤的处理结果。

在这个复杂的启动过程中,涉及Settings对象、属性系统、Installer系统、Package Handler、系统permission和feature信息、AndroidManifest.xml、Resource、dexopt操作、FileObserver以及APK包安装扫描等一系列操作。其启动流程如图8-4所示。

8.2 PackageManagerService的启动过程 - 图1

图 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.2 PackageManagerService的启动过程 - 图2

图 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组合键查看类的结构信息。