了解了Android系统的启动过程的读者应该知道,Android的所有Java服务都是通过SystemServer进程启动的,并且驻留在SystemServer进程中。SystemServer进程在启动时,通过创建一个ServerThread线程来启动所有服务,本文主要介绍Android服务中PackageManagerService服务启动过程。首先介绍一些PackageManagerService服务下的相关类关系图:

在SystemServer进程的ServerThread线程中,执行以下代码启动PackageManagerService服务:

// 通过读取属性来判断运行核心应用
String cryptState = SystemProperties.get("vold.decrypt");
boolean onlyCore = false;
if (ENCRYPTING_STATE.equals(cryptState)) {
Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
onlyCore = true;
} else if (ENCRYPTED_STATE.equals(cryptState)) {
Slog.w(TAG, "Device encrypted - only parsing core apps");
onlyCore = true;
}
//启动PackageManagerService
pm = PackageManagerService.main(context,
factoryTest != SystemServer.FACTORY_TEST_OFF,
onlyCore);
boolean firstBoot = false;
//判断PackageManagerService是否是第一次启动,SystemServer进程被杀后会被重启
try {
firstBoot = pm.isFirstBoot();
} catch (RemoteException e) {
}
//PackageManagerService执行dex优化
...
try {
pm.performBootDexOpt();
} catch (Throwable e) {
reportWtf("performing boot dexopt", e);
}

首先启动PackageManagerService,然后判断该服务是否是第一次启动,接着执行dex优化。

public static final IPackageManager main(Context context, boolean factoryTest,
boolean onlyCore) {
//构造PackageManagerService服务对象
PackageManagerService m = new PackageManagerService(context, factoryTest, onlyCore);
//注册PackageManagerService服务
ServiceManager.addService("package", m);
return m;
}

启动过程比较简单,就是构造一个PackageManagerService对象,然后将该服务对象注册到ServiceManger进程中,关于服务注册过程请查看Android服务注册完整过程源码分析。PackageManagerService对象构造过程非常复杂,构造过程分几个阶段.

//PackageManagerService启动开始
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,SystemClock.uptimeMillis());
//SDK版本检查
if (mSdkVersion <= 0) {
Slog.w(TAG, "**** ro.build.version.sdk not set!");
}
//读取开机启动模式
String mode = SystemProperties.get("ro.bootmode", "mode");
engModeEnable = "engtest".equals(mode)?true:false;
Slog.i(TAG, "engModeEnable: " + engModeEnable + " ,mode:"+mode);
mContext = context;
mFactoryTest = factoryTest;//开机模式
mOnlyCore = onlyCore;//是否对包做dex优化
//如果编译版本为eng,则不需要dex优化
mNoDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
//创建显示尺寸信息
mMetrics = new DisplayMetrics();
//存储系统运行过程中的设置信息
mSettings = new Settings();
/*创建SharedUserSetting对象并添加到Settings的成员变量mSharedUsers中,在Android系统中,多个package通过设置sharedUserId属性可以运行在同一个进程,共享同一个UID*/
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);
String separateProcesses = SystemProperties.get("debug.separate_processes");
if (separateProcesses != null && separateProcesses.length() > 0) {
if ("*".equals(separateProcesses)) {
mDefParseFlags = PackageParser.PARSE_IGNORE_PROCESSES;
mSeparateProcesses = null;
Slog.w(TAG, "Running with debug.separate_processes: * (ALL)");
} else {
mDefParseFlags = 0;
mSeparateProcesses = separateProcesses.split(",");
Slog.w(TAG, "Running with debug.separate_processes: "
+ separateProcesses);
}
} else {
mDefParseFlags = 0;
mSeparateProcesses = null;
}
mPreInstallDir = new File("/system/preloadapp");
//创建应用安装器
mInstaller = new Installer();
//获取屏幕尺寸大小
WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
Display d = wm.getDefaultDisplay();
d.getMetrics(mMetrics);
synchronized (mInstallLock) {
// writer
synchronized (mPackages) {
//启动消息处理线程
mHandlerThread.start();
//为消息处理线程创建一个消息分发handler
mHandler = new PackageHandler(mHandlerThread.getLooper());
// dataDir =/data/
File dataDir = Environment.getDataDirectory();
// mAppDataDir = /data/data
mAppDataDir = new File(dataDir, "data");
// mAsecInternalPath = /data/app-asec
mAsecInternalPath = new File(dataDir, "app-asec").getPath();
// mUserAppDataDir = /data/user
mUserAppDataDir = new File(dataDir, "user");
// mDrmAppPrivateInstallDir = /data/app-private
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
sUserManager = new UserManager(mInstaller, mUserAppDataDir);
//读取并解析/etc/permissions下的XML文件
readPermissions();
mRestoredSettings = mSettings.readLPw(getUsers());

函数首先创建一个Settings对象,用来保存一些设置信息,然后调用addSharedUserLPw向Settings类的成员变量mSharedUsers中添加SharedUserSetting对象,在Android系统中,通过设置android:sharedUserId="android.uid.system"属性可以为应用程序指定UID,SharedUserSetting对象保存同一共享进程UID的所有包信息,Settings对象用于管理Android系统运行过程中的一些设置信息,Settings的成员变量mSharedUsers以键值对的方式保存UID对应的SharedUserSetting对象。在Android系统中,可以通过在AndroidManifest.xml文件中设置sharedUserId属性来设置多个APK运行在同一个进程中。PackageManagerService的任务就是构造一些数据结构来保存所有APK的配置信息,关于Settings类之间的关系图如下:


PackageSignatures来用来描述Android应用程序安装包的签名信息,GrantedPermissions类用于描述应用APK的权限信息。Settings类的成员变量mSharedUsers是一个HashMap,用键值对的形式保存所有的SharedUserSetting对象,SharedUserSetting对象用于记录共享同一进程的所有APK信息,该类的成员变量packages的类型为PackageSetting,用来保存所有共享同一UID的包信息,而成员变量userId则是记录多个APK共享的UID。首先介绍Settings对象的构造过程:

Settings() {
//调用另外一个有参构造函数
this(Environment.getDataDirectory());
} Settings(File dataDir) {
//创建/data/system/目录
mSystemDir = new File(dataDir, "system");
mSystemDir.mkdirs();
//设置/data/system目录的权限
FileUtils.setPermissions(mSystemDir.toString(),
FileUtils.S_IRWXU|FileUtils.S_IRWXG
|FileUtils.S_IROTH|FileUtils.S_IXOTH,
-1, -1);
//mSettingsFilename=/data/system/packages.xml
mSettingsFilename = new File(mSystemDir, "packages.xml");
//mBackupSettingsFilename=/data/system/packages-backup.xml
mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");
//mPackageListFilename=/data/system/packages.list
mPackageListFilename = new File(mSystemDir, "packages.list");
// Deprecated: Needed for migration
//mStoppedPackagesFilename = /data/system/packages-stopped.xml
mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
//mStoppedPackagesFilename = /data/system/packages-stopped-backup.xml
mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
}

Settings对象的构造过程很简单,就是创建一些目录和文件。首先创建/data/system/目录,然后分别创建以下四个文件:

/data/system/packages.xml

/data/system/packages-backup.xml

/data/system/packages.list

/data/system/packages-stopped.xml

/data/system/packages-stopped-backup.xml

Settings通过addSharedUserLPw函数添加向mSharedUsers预先添加SharedUserSetting对象

SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags) {
//根据进程UID对应的名称从成员变量mSharedUsers中查找对应的SharedUserSetting对象
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;
}
//没有查找到对应的SharedUserSetting对象,则创建一个新的SharedUserSetting对象。
s = new SharedUserSetting(name, pkgFlags);
s.userId = uid;
//添加到成员变量mUserIds,mOtherUserIds中,这两个变量主要是加快查找速度
if (addUserIdLPw(uid, s, name)) {
//添加到mSharedUsers表中
mSharedUsers.put(name, s);
return s;
}
return null;
}

函数首先根据字符串名称从成员变量mSharedUsers哈希表中查找对应的SharedUserSetting对象,如果表中不存在对应的SharedUserSetting对象,则创建一个新的SharedUserSetting对象,并初始化该对象的域,然后根据UID的大小通过函数addUserIdLPw添加到mUserIds或mOtherUserIds中,同时以键值对的形式保存在mSharedUsers中。

private boolean addUserIdLPw(int uid, Object obj, Object name) {
//判断添加的UID是否大于19999
if (uid > Process.LAST_APPLICATION_UID) {
return false;
}
//判断添加的UID是否大于等于10000
if (uid >= Process.FIRST_APPLICATION_UID) {
//计算在数组中的索引为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;
}
//添加的SharedUserSetting对象到mUserIds动态数组中
mUserIds.set(index, obj);
} else {//将UID小于1000,则将SharedUserSetting对象添加到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;
}

Android定义了应用程序的UID范围,对于非系统应用,UID介于1000到1999之间,而对于系统应用,UID小于1000

public static final int SYSTEM_UID = 1000;
public static final int PHONE_UID = 1001;
public static final int SHELL_UID = 2000;
public static final int LOG_UID = 1007;
public static final int WIFI_UID = 1010;
public static final int MEDIA_UID = 1013;
public static final int DRM_UID = 1019;
public static final int SDCARD_RW_GID = 1015;
public static final int VPN_UID = 1016;
public static final int NFC_UID = 1027;
public static final int MEDIA_RW_GID = 1023;
//应用程序UID范围
public static final int FIRST_APPLICATION_UID = 10000;
public static final int LAST_APPLICATION_UID = 19999;
//fully isolated sandboxed processes UID范围
public static final int FIRST_ISOLATED_UID = 99000;
public static final int LAST_ISOLATED_UID = 99999;

addUserIdLPw函数将UID介于1000到1999之间的SharedUserSetting对象添加到mUserIds数组中,通过UID来索引数组元素。

UID小于1000的SharedUserSetting保存到数组mOtherUserIds中。回到PackageManagerService的构造函数中,通过Settings的addSharedUserLPw函数向mSharedUsers,mUserIds,mOtherUserIds数组添加了4个特定进程的SharedUserSetting对象。

mPreInstallDir = new File("/system/preloadapp");
//创建应用安装器
mInstaller = new Installer();
//获取屏幕尺寸大小
WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
Display d = wm.getDefaultDisplay();
d.getMetrics(mMetrics);
synchronized (mInstallLock) {
// writer
synchronized (mPackages) {
//启动消息处理线程
mHandlerThread.start();
//为消息处理线程创建一个消息分发handler
mHandler = new PackageHandler(mHandlerThread.getLooper());
// dataDir =/data/
File dataDir = Environment.getDataDirectory();
// mAppDataDir = /data/data
mAppDataDir = new File(dataDir, "data");
// mAsecInternalPath = /data/app-asec
mAsecInternalPath = new File(dataDir, "app-asec").getPath();
// mUserAppDataDir = /data/user
mUserAppDataDir = new File(dataDir, "user");
// mDrmAppPrivateInstallDir = /data/app-private
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");

首先创建Installer对象,用于访问installd服务进程,完成一些apk安装,卸载,优化工作。然后通过WindowManager得到屏幕尺寸信息,接着启动一个名为PackageManager的消息线程,该线程是PackageManagerService的工作线程,mHandlerThread线程是一个带消息循环的工作线程,在定义该线程对象的时候就已经创建

final HandlerThread mHandlerThread = new HandlerThread("PackageManager",Process.THREAD_PRIORITY_BACKGROUND);

同时为该消息线程创建了一个消息分发器PackageHandler对象,通过该handler对象可以往PackageManager线程发送消息,PackageManager线程通过消息循环处理发送进来的消息,消息处理过程如下:

public void handleMessage(Message msg) {
try {
//直接调用doHandleMessage函数来处理各种消息
doHandleMessage(msg);
} finally {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
}
}

这里暂时不介绍doHandleMessage函数,在介绍特定消息时,在来分析该函数对各种消息的处理过程。PackageManagerService的线程模型如下:

最后就是创建一些安装目录:

/system/preloadapp

/data/data

/data/app-asec

/data/user

/data/data

/data/app-private

创建用户管理对象UserManager:

sUserManager = new UserManager(mInstaller, mUserAppDataDir);

构造了一个UserManager,参数一为应用程序安装器Installer,参数二为用户安装目录/data/user

public UserManager(Installer installer, File baseUserPath) {
this(Environment.getDataDirectory(), baseUserPath);
mInstaller = installer;
  }
  
  UserManager(File dataDir, File baseUserPath) {
   // mUsersDir=/data/system/users
mUsersDir = new File(dataDir, USER_INFO_DIR);
mUsersDir.mkdirs();
   // userZeroDir=/data/system/users/0
File userZeroDir = new File(mUsersDir, "0");
userZeroDir.mkdirs();
//mBaseUserPath=/data/user
mBaseUserPath = baseUserPath;
FileUtils.setPermissions(mUsersDir.toString(),
FileUtils.S_IRWXU|FileUtils.S_IRWXG
|FileUtils.S_IROTH|FileUtils.S_IXOTH,
-1, -1);
// mUserListFile=/data/system/users/userlist.xml
mUserListFile = new File(mUsersDir, USER_LIST_FILENAME);
readUserList();
}

构造过程比较简单,就是创建几个目录和几个文件:

/data/system/users

/data/system/users/0

/data/system/users/userlist.xml

然后通过函数readUserList读取用户列表,这里不在介绍该函数的实现,该函数就是从userlist.xml文件中读取用户信息,保存到UserManager的成员变量mUsers中。

读取权限配置信息:

//读取并解析/etc/permissions下的XML文件
readPermissions();

函数首先调用readPermissions扫描读取并解析/etc/permissions文件夹下的XML文件,并将解析的数据保存到PackageManagerService的成员变量中

void readPermissions() {
// Read permissions from .../etc/permission directory.
File libraryDir = new File(Environment.getRootDirectory(), "etc/permissions");
if (!libraryDir.exists() || !libraryDir.isDirectory()) {
Slog.w(TAG, "No directory " + libraryDir + ", skipping");
return;
}
if (!libraryDir.canRead()) {
Slog.w(TAG, "Directory " + libraryDir + " cannot be read");
return;
}
// 循环读取etc/permissions目录下的XML文件
for (File f : libraryDir.listFiles()) {
// 跳过platform.xml文件,最后读取该文件
if (f.getPath().endsWith("etc/permissions/platform.xml")) {
continue;
}
if (!f.getPath().endsWith(".xml")) {
Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring");
continue;
}
if (!f.canRead()) {
Slog.w(TAG, "Permissions library file " + f + " cannot be read");
continue;
}
readPermissionsFromXml(f);
}
// Read permissions from .../etc/permissions/platform.xml last so it will take precedence
final File permFile = new File(Environment.getRootDirectory(),"etc/permissions/platform.xml");
readPermissionsFromXml(permFile);
}

在etc/permissions目录下保存了一下配置文件


这些文件在编译的时候直接从frameworks指定目录下拷贝过来的,在特定product编译目录下的base.mk文件中的配置如下:

函数readPermissionsFromXml使用PULL方式解析这些XML文件,下面分别介绍各个标签的解析过程。

feature标签用来描述设备应该支持的硬件特性。解析过程如下:

else if ("feature".equals(name)) {
//读取熟悉name的值
String fname = parser.getAttributeValue(null, "name");
if (fname == null) {
Slog.w(TAG, "<feature> without name at "+ parser.getPositionDescription());
} else {
//创建一个FeatureInfo对象
FeatureInfo fi = new FeatureInfo();
fi.name = fname;
//mAvailableFeatures是PackageManagerService的成员变量,以HashMap的方式保存硬件支持的特性
mAvailableFeatures.put(fname, fi);
}
XmlUtils.skipCurrentTag(parser);
continue;
}

library用于指定系统库,当应用程序运行时,系统会为进程加载一些必要库。该标签的解析过程如下:

else if ("library".equals(name)) {
//读取属性name的值
String lname = parser.getAttributeValue(null, "name");
//读取属性file的值
String lfile = parser.getAttributeValue(null, "file");
if (lname == null) {
Slog.w(TAG, "<library> without name at "+ parser.getPositionDescription());
} else if (lfile == null) {
Slog.w(TAG, "<library> without file at "+ parser.getPositionDescription());
} else {
//mSharedLibraries是PackageManagerService的成员变量,以HashMap的形式保存进程运行库
mSharedLibraries.put(lname, lfile);
}
XmlUtils.skipCurrentTag(parser);
continue;
}

group标签用于建立Android层与Linux层之间的权限映射关系。

else if ("permission".equals(name)) {
//读取属性name的值
String perm = parser.getAttributeValue(null, "name");
if (perm == null) {
Slog.w(TAG, "<permission> without name at "+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
perm = perm.intern();
readPermission(parser, perm);
} void readPermission(XmlPullParser parser, String name)
throws IOException, XmlPullParserException {
name = name.intern();
//根据name在mSettings.mPermissions表中查找对应的BasePermission对象
BasePermission bp = mSettings.mPermissions.get(name);
//如果不存在,则创建一个新的BasePermission对象,并保存到mSettings.mPermissions中
if (bp == null) {
bp = new BasePermission(name, null, BasePermission.TYPE_BUILTIN);
mSettings.mPermissions.put(name, bp);
}
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();
//读取标签group的属性gid值
if ("group".equals(tagName)) {
String gidStr = parser.getAttributeValue(null, "gid");
if (gidStr != null) {
//根据gid字符串,找到对应的gid数值
int gid = Process.getGidForName(gidStr);
//设置BasePermission对象的gid值
bp.gids = appendInt(bp.gids, gid);
} else {
Slog.w(TAG, "<group> without gid at "+ parser.getPositionDescription());
}
}
XmlUtils.skipCurrentTag(parser);
}
}

assign-permission标签将解析到的内容保存到mSettings.mPermissions中

else if ("assign-permission".equals(name)) {
//读取属性name的值
String perm = parser.getAttributeValue(null, "name");
if (perm == null) {
Slog.w(TAG, "<assign-permission> without name at "+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
//读取属性uid的值
String uidStr = parser.getAttributeValue(null, "uid");
if (uidStr == null) {
Slog.w(TAG, "<assign-permission> without uid at "+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
//根据属性uid字符串转换为uid数值
int uid = Process.getUidForName(uidStr);
if (uid < 0) {
Slog.w(TAG, "<assign-permission> with unknown uid \""+ uidStr + "\" at "+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
//保存到mSystemPermissions表中
perm = perm.intern();
HashSet<String> perms = mSystemPermissions.get(uid);
if (perms == null) {
perms = new HashSet<String>();
mSystemPermissions.put(uid, perms);
}
perms.add(perm);
XmlUtils.skipCurrentTag(parser);
}


读取安装包信息

/data/system/packages.xml文件用于记录系统中所安装的Package信息;/data/system/packages-backup.xml文件是/data/packages.xml文件的备份。在PackageManagerService扫描完目标文件夹后会创建该文件,当系统进行程序安装卸载时会更新该文件。
/data/system/packages-stopped.xml文件用于记录系统中强制停止运行的Package信息。/data/system/packages-stopped-backup.xml是/data/packages-stopped.xml文件的备份。在强制停止某个应用时,会将应用相关信息记录到该文件中。
/data/system/packages.list保存系统中存在的所有非系统自带的APK信息,即UID大于1000的apk。

当系统第一次开机时,这些文件并不存在,而在以后的开机中,扫描到的这些XML文件是上一次运行过程中创建的。

boolean readLPw(List<UserInfo> users) {
FileInputStream str = null;
//如果/data/system/packages-backup.xml文件存在
if (mBackupSettingsFilename.exists()) {
try {
//读取/data/system/packages-backup.xml文件
str = new FileInputStream(mBackupSettingsFilename);
mReadMessages.append("Reading from backup settings file\n");
PackageManagerService.reportSettingsProblem(Log.INFO,"Need to read from backup settings file");
//当/data/system/packages.xml文件的备份文件存在时,删除packages.xml文件
if (mSettingsFilename.exists()) {
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();
try {
//如果/data/system/packages-backup.xml文件为空
if (str == null) {
//同时/data/system/packages.xml文件不存在
if (!mSettingsFilename.exists()) {
mReadMessages.append("No settings file found\n");
PackageManagerService.reportSettingsProblem(Log.INFO,
"No settings file; creating initial state");
//读取/etc/preferred-apps目录下的xml文件
readDefaultPreferredAppsLPw();
return false;
}
//如果packages.xml的备份文件为空,读取packages.xml文件内容
str = new FileInputStream(mSettingsFilename);
}
//解析文件内容
XmlPullParser parser = Xml.newPullParser();
parser.setInput(str, null);
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG
&& type != XmlPullParser.END_DOCUMENT) {
;
}
...
}

接下来检测并优化BOOTCLASSPATH环境变量指定的Java运行库及platform.xml中配置的Java库,同时优化/system/framework目录下的Jar包和apk文件,最后删除/data/dalvik-cache目录下的一些缓存文件。在init.rc中配置的BOOTCLASSPATH如下:

long startTime = SystemClock.uptimeMillis();
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,startTime);
// 设置扫描模式
int scanMode = SCAN_MONITOR | SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING;
if (mNoDexOpt) {
Slog.w(TAG, "Running ENG build: no pre-dexopt!");
scanMode |= SCAN_NO_DEX;
}
//保存库文件路径
final HashSet<String> libFiles = new HashSet<String>();
//mFrameworkDir = /framework/
mFrameworkDir = new File(Environment.getRootDirectory(), "framework");
//mDalvikCacheDir = /data/dalvik-cache/
mDalvikCacheDir = new File(dataDir, "dalvik-cache");
boolean didDexOpt = false;
//通过属性的方式得到启动Java启动类库的路径,在init.rc中通过BOOTCLASSPATH环境变量的方式设置
String bootClassPath = System.getProperty("java.boot.class.path");
if (bootClassPath != null) {
String[] paths = splitString(bootClassPath, ':');
for (int i=0; i<paths.length; i++) {
try {
//判断Jar包是否需要dex优化
if (dalvik.system.DexFile.isDexOptNeeded(paths[i])) {
//如果需要则添加到libFiles表中
libFiles.add(paths[i]);
//通过安装器请求installd服务进程执行dex优化
mInstaller.dexopt(paths[i], Process.SYSTEM_UID, true);
didDexOpt = true;
}
} catch (FileNotFoundException e) {
Slog.w(TAG, "Boot class path not found: " + paths[i]);
} catch (IOException e) {
Slog.w(TAG, "Cannot dexopt " + paths[i] + "; is it an APK or JAR? "
+ e.getMessage());
}
}
} else {
Slog.w(TAG, "No BOOTCLASSPATH found!");
}
//在前面解析platfor.xml时,将一些额外的类库路径保存到了mSharedLibraries变量中
if (mSharedLibraries.size() > 0) {
//循环变量mSharedLibraries变量
Iterator<String> libs = mSharedLibraries.values().iterator();
while (libs.hasNext()) {
String lib = libs.next();
try {
//判断Jar包是否需要dex优化
if (dalvik.system.DexFile.isDexOptNeeded(lib)) {
//如果需要则添加到libFiles表中
libFiles.add(lib);
//通过安装器进行dex优化
mInstaller.dexopt(lib, Process.SYSTEM_UID, true);
didDexOpt = true;
}
} catch (FileNotFoundException e) {
Slog.w(TAG, "Library not found: " + lib);
} catch (IOException e) {
Slog.w(TAG, "Cannot dexopt " + lib + "; is it an APK or JAR? "
+ e.getMessage());
}
}
}
//将/system/frameworks/framework-res.apk添加到libFiles中
libFiles.add(mFrameworkDir.getPath() + "/framework-res.apk");
//列出/system/frameworks目录下的文件
String[] frameworkFiles = mFrameworkDir.list();
if (frameworkFiles != null) {
//遍历/system/frameworks目录下的文件
for (int i=0; i<frameworkFiles.length; i++) {
File libPath = new File(mFrameworkDir, frameworkFiles[i]);
String path = libPath.getPath();
//判断libFiles中是否已经包含该文件,如果包含则跳过
if (libFiles.contains(path)) {
continue;
}
//跳过不是apk或jar文件
if (!path.endsWith(".apk") && !path.endsWith(".jar")) {
continue;
}
try {
//判断Jar包或apk是否需要dex优化
if (dalvik.system.DexFile.isDexOptNeeded(path)) {
//通过安装器进行dex优化
mInstaller.dexopt(path, Process.SYSTEM_UID, true);
didDexOpt = true;
}
} catch (FileNotFoundException e) {
Slog.w(TAG, "Jar not found: " + path);
} catch (IOException e) {
Slog.w(TAG, "Exception reading jar: " + path, e);
}
}
}
//如果前面对某个文件做过优化,只要优化了,didDexOpt就被设置为true
if (didDexOpt) {
//遍历/data/dalvik-cache目录下的文件
String[] files = mDalvikCacheDir.list();
if (files != null) {
for (int i=0; i<files.length; i++) {
String fn = files[i];
//删除文件名以"data@app@"和"data@app-private@"开头的文件
if (fn.startsWith("data@app@")
|| fn.startsWith("data@app-private@")) {
Slog.i(TAG, "Pruning dalvik file: " + fn);
(new File(mDalvikCacheDir, fn)).delete();
}
}
}
}

接着扫描系统apk信息

mFlagInstall = false;
//创建文件夹监控对象,监视/system/framework目录
mFrameworkInstallObserver = new AppDirObserver(mFrameworkDir.getPath(), OBSERVER_EVENTS, true);
mFrameworkInstallObserver.startWatching();
//扫描/system/framework目录下的apk文件,扫描模式设置为非优化模式
scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM| PackageParser.PARSE_IS_SYSTEM_DIR,scanMode | SCAN_NO_DEX, 0);
//在工厂模式下,调用函数scanDirLIOnly只扫描特定的apk文件
if(engModeEnable){
//temp null
mVendorAppDir = null;
mDrmAppInstallObserver = null;
mSystemAppDir = null;
mAppInstallObserver = null;
mSystemInstallObserver = null;
mPreInstallObserver = null;
mVendorInstallObserver = null;
mAppInstallDir = null;
Slog.i(TAG, " begin scan the apps !");
scanDirLIOnly(PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
Slog.i(TAG, " end scan the apps !");
engModeEnable = false;
}else{//正常模式下
//创建文件夹监控对象,监视/system/app目录
mSystemAppDir = new File(Environment.getRootDirectory(), "app");
mSystemInstallObserver = new AppDirObserver(
mSystemAppDir.getPath(), OBSERVER_EVENTS, true);
mSystemInstallObserver.startWatching();
//扫描/system/app目录
scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM| PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
//创建文件夹监控对象,监视/vendor/app目录
mVendorAppDir = new File("/vendor/app");
mVendorInstallObserver = new AppDirObserver(
mVendorAppDir.getPath(), OBSERVER_EVENTS, true);
mVendorInstallObserver.startWatching();
//扫描/vendor/app目录
scanDirLI(mVendorAppDir, PackageParser.PARSE_IS_SYSTEM| PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0); if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands");
mInstaller.moveFiles();
// Prune any system packages that no longer exist.
final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<String>();
if (!mOnlyCore) {
//遍历Settings的成员变量mPackages
Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
while (psit.hasNext()) {
PackageSetting ps = psit.next();
//不是系统app
if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
continue;
}
//如果是系统app,同时已经被PackageManagerService扫描过了
final PackageParser.Package scannedPkg = mPackages.get(ps.name);
if (scannedPkg != null) {
//该apk包已不能使用
if (mSettings.isDisabledSystemPackageLPr(ps.name)) {
Slog.i(TAG, "Expecting better updatd system app for " + ps.name
+ "; removing system app");
//移除该apk包信息
removePackageLI(scannedPkg, true);
}
continue;
}
if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {
psit.remove();
String msg = "System package " + ps.name
+ " no longer exists; wiping its data";
reportSettingsProblem(Log.WARN, msg);
mInstaller.remove(ps.name, 0);
sUserManager.removePackageForAllUsers(ps.name);
} else {
final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps.name);
if (disabledPs.codePath == null || !disabledPs.codePath.exists()) {
possiblyDeletedUpdatedSystemApps.add(ps.name);
}
}
}
}
//mAppInstallDir = /data/app/
mAppInstallDir = new File(dataDir, "app");
//查找未完成安装的apk包
ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackagesLPr();
//清除未完成安装包
for(int i = 0; i < deletePkgsList.size(); i++) {
//clean up here
cleanupInstallFailedPackage(deletePkgsList.get(i));
}
//删除临时文件
deleteTempPackageFiles();

监控并扫描以下三个系统包安装目录:

/system/framework :该目录下的文件都是系统库

/system/app :该目录下是默认的系统应用

/vendor/app :该目录下是厂商定制的应用

最后扫描非系统apk信息

if (!mOnlyCore) {
//标识数据扫描开始 EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,SystemClock.uptimeMillis());
//创建文件夹监控对象,监视/data/app/目录
mAppInstallObserver = new AppDirObserver(mAppInstallDir.getPath(), OBSERVER_EVENTS, false);
mAppInstallObserver.startWatching();
//扫描/data/app/目录下的apk文件
scanDirLI(mAppInstallDir, 0, scanMode, 0);
//创建文件夹监控对象,监视/system/preloadapp/目录
mPreInstallObserver = new AppDirObserver(mPreInstallDir.getPath(), OBSERVER_EVENTS, false);
mPreInstallObserver.startWatching();
//扫描/system/preloadapp/目录下的apk文件
scanDirLI(mPreInstallDir, 0, scanMode, 0);
//创建文件夹监控对象,监视/data/app-private/目录
mDrmAppInstallObserver = new AppDirObserver(mDrmAppPrivateInstallDir.getPath(), OBSERVER_EVENTS, false);
mDrmAppInstallObserver.startWatching();
//扫描/data/app-private/目录下的apk文件
scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,scanMode, 0);
/**
* Remove disable package settings for any updated system
* apps that were removed via an OTA. If they're not a
* previously-updated app, remove them completely.
* Otherwise, just revoke their system-level permissions.
*/
for (String deletedAppName : possiblyDeletedUpdatedSystemApps) {
PackageParser.Package deletedPkg = mPackages.get(deletedAppName);
mSettings.removeDisabledSystemPackageLPw(deletedAppName);
String msg;
if (deletedPkg == null) {
msg = "Updated system package " + deletedAppName+ " no longer exists; wiping its data";
mInstaller.remove(deletedAppName, 0);
sUserManager.removePackageForAllUsers(deletedAppName);
} else {
msg = "Updated system app + " + deletedAppName+ " no longer present; removing system privileges for "+ deletedAppName;
deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName);
deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM;
}
reportSettingsProblem(Log.WARN, msg);
}
} else {
mPreInstallObserver = null;
mAppInstallObserver = null;
mDrmAppInstallObserver = null;
}

监控并扫描以下三个数据目录:

/data/app/

/system/preloadapp/

/data/app-private/

最后进入结尾阶段,将扫描到的信息保存到文件中。

mFlagInstall = true;
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,SystemClock.uptimeMillis());
final boolean regrantPermissions = mSettings.mInternalSdkPlatform != mSdkVersion;
mSettings.mInternalSdkPlatform = mSdkVersion;
updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL | (regrantPermissions
? (UPDATE_PERMISSIONS_REPLACE_PKG|UPDATE_PERMISSIONS_REPLACE_ALL): 0));
ArrayList<PreferredActivity> removed = new ArrayList<PreferredActivity>();
for (PreferredActivity pa : mSettings.mPreferredActivities.filterSet()) {
if (mActivities.mActivities.get(pa.mPref.mComponent) == null) {
removed.add(pa);
}
}
for (int i=0; i<removed.size(); i++) {
PreferredActivity pa = removed.get(i);
Slog.w(TAG, "Removing dangling preferred activity: "
+ pa.mPref.mComponent);
mSettings.mPreferredActivities.removeFilter(pa);
}
// can downgrade to reader
mSettings.writeLPr(); EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,SystemClock.uptimeMillis());
Runtime.getRuntime().gc();
mRequiredVerifierPackage = getRequiredVerifierLPr();

至此,PackageManagerService就构造完成了,构造过程认为繁重,Apk文件扫描解析耗费比较长的时间,这是导致开机速度慢的原因。




Android服务之PackageManagerService启动源码分析的更多相关文章

  1. Netty服务端的启动源码分析

    ServerBootstrap的构造: public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, Serve ...

  2. Android Small插件化框架源码分析

    Android Small插件化框架源码分析 目录 概述 Small如何使用 插件加载流程 待改进的地方 一.概述 Small是一个写得非常简洁的插件化框架,工程源码位置:https://github ...

  3. RocketMQ中Broker的启动源码分析(一)

    在RocketMQ中,使用BrokerStartup作为启动类,相较于NameServer的启动,Broker作为RocketMQ的核心可复杂得多 [RocketMQ中NameServer的启动源码分 ...

  4. RocketMQ中Broker的启动源码分析(二)

    接着上一篇博客  [RocketMQ中Broker的启动源码分析(一)] 在完成准备工作后,调用start方法: public static BrokerController start(Broker ...

  5. RocketMQ中PullConsumer的启动源码分析

    通过DefaultMQPullConsumer作为默认实现,这里的启动过程和Producer很相似,但相比复杂一些 [RocketMQ中Producer的启动源码分析] DefaultMQPullCo ...

  6. Django如何启动源码分析

    Django如何启动源码分析 启动 我们启动Django是通过python manage.py runsever的命令 解决 这句话就是执行manage.py文件,并在命令行发送一个runsever字 ...

  7. Netty源码分析 (三)----- 服务端启动源码分析

    本文接着前两篇文章来讲,主要讲服务端类剩下的部分,我们还是来先看看服务端的代码 /** * Created by chenhao on 2019/9/4. */ public final class ...

  8. Netty之旅三:Netty服务端启动源码分析,一梭子带走!

    Netty服务端启动流程源码分析 前记 哈喽,自从上篇<Netty之旅二:口口相传的高性能Netty到底是什么?>后,迟迟两周才开启今天的Netty源码系列.源码分析的第一篇文章,下一篇我 ...

  9. Android线程间异步通信机制源码分析

    本文首先从整体架构分析了Android整个线程间消息传递机制,然后从源码角度介绍了各个组件的作用和完成的任务.文中并未对基础概念进行介绍,关于threadLacal和垃圾回收等等机制请自行研究. 基础 ...

随机推荐

  1. Step by step guide to set up master and slave machines(转)

    Note: There is no need to install Jenkins on the slave machine. On your master machine go to Manage ...

  2. 基于Server-Sent Event的简单聊天室 Web 2.0时代,即时通信已经成为必不可少的网站功能,那实现Web即时通信的机制有哪些呢?在这门项目课中我们将一一介绍。最后我们将会实现一个基于Server-Sent Event和Flask简单的在线聊天室。

    基于Server-Sent Event的简单聊天室 Web 2.0时代,即时通信已经成为必不可少的网站功能,那实现Web即时通信的机制有哪些呢?在这门项目课中我们将一一介绍.最后我们将会实现一个基于S ...

  3. Linux less命令

    less 工具也是对文件或其它输出进行分页显示的工具,应该说是linux正统查看文件内容的工具,功能极其强大.less 的用法比起 more 更加的有弹性.在 more 的时候,我们并没有办法向前面翻 ...

  4. UVA 1524 - Hot or Cold?(数学)

    UVA 1524 - Hot or Cold? 题目链接 题意:给一个一元n次方程,带入x表示时间,f(x)表示温度,如今要求[s, e]的平均温度 思路:平均温度就是 总温度/ (e - s),画出 ...

  5. Troubleshooting:oVirt-engine

    问题:执行./create_db_devel.sh -u postgres创建数据库时出错 描述: [root@localhost dbscripts]# ./create_db_devel.sh - ...

  6. MongoDB使用手册

    一.安装包下载地址 http://www.mongodb.org/downloads 二.服务器端使用 LINUX版本: ①用tar -zxvf mongodb-linux-x86_64-1.2.4. ...

  7. android Asynctask的优缺点?是否能同一时候并发100+asynctask呢?

    一  Asynctask的优缺点? AsyncTask,是android提供的轻量级的异步类,能够直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步运行的程度(能够通过接口实现UI ...

  8. 基于visual Studio2013解决C语言竞赛题之1090测量重量

        题目 解决代码及点评 /************************************************************************/ /* ...

  9. 如何编译tizen源码(图文教程)?

    前一篇文章已经介绍了如何下载tizen源码,下面我将继续讲述如何编译源码. 1 下载安装gbs编译工具 tizen源码是用gbs工具进行编译的,因此我们首先得将此工具下载下来,并且设置好. 下面的Ub ...

  10. excel使用cube