今天無意間找到這個文章,看了一下,寫得還是不錯的,轉載下,後面繼續學習。
原文鏈接:http://blog.csdn.net/yangwen123/article/details/9464779
瞭解了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介於10000到19999之間,而對於系統應用,UID小於10000- 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掃瞄讀取並解析/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, " 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, " without name at "+ parser.getPositionDescription());
- } else if (lfile == null) {
- Slog.w(TAG, " 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, " 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, " 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, " without name at "+ parser.getPositionDescription());
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
- //读取属性uid的值
- String uidStr = parser.getAttributeValue(null, "uid");
- if (uidStr == null) {
- Slog.w(TAG, " without uid at "+ parser.getPositionDescription());
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
- //根据属性uid字符串转换为uid数值
- int uid = Process.getUidForName(uidStr);
- if (uid < 0) {
- Slog.w(TAG, " with unknown uid \""+ uidStr + "\" at "+ parser.getPositionDescription());
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
- //保存到mSystemPermissions表中
- perm = perm.intern();
- HashSet perms = mSystemPermissions.get(uid);
- if (perms == null) {
- perms = new HashSet();
- 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 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 libFiles = new HashSet();
- //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
- 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 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
- 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
- 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信 |