TShopping

 找回密碼
 註冊
搜索
查看: 1838|回復: 1

[教學] Android服務之PackageManagerService啟動源碼分析

[複製鏈接]
發表於 2013-12-14 00:21:26 | 顯示全部樓層 |閱讀模式
 
Push to Facebook
今天無意間找到這個文章,看了一下,寫得還是不錯的,轉載下,後面繼續學習。

原文鏈接:http://blog.csdn.net/yangwen123/article/details/9464779

瞭解了Android系統的啟動過程的讀者應該知道,Android的所有Java服務都是通過SystemServer進程啟動的,並且駐留在SystemServer進程中。SystemServer進程在啟動時,通過創建一個ServerThread線程來啟動所有服務,本文主要介紹Android服務中PackageManagerService服務啟動過程。首先介紹一些PackageManagerService服務下的相關類關係圖:
1.jpg

在SystemServer進程的ServerThread線程中,執行以下代碼啟動PackageManagerService服務:
  1. // 通过读取属性来判断运行核心应用
  2. String cryptState = SystemProperties.get("vold.decrypt");
  3. boolean onlyCore = false;
  4. if (ENCRYPTING_STATE.equals(cryptState)) {
  5.         Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
  6.         onlyCore = true;
  7. } else if (ENCRYPTED_STATE.equals(cryptState)) {
  8.         Slog.w(TAG, "Device encrypted - only parsing core apps");
  9.         onlyCore = true;
  10. }
  11. //启动PackageManagerService
  12. pm = PackageManagerService.main(context,
  13.                 factoryTest != SystemServer.FACTORY_TEST_OFF,
  14.                 onlyCore);
  15. boolean firstBoot = false;
  16. //判断PackageManagerService是否是第一次启动,SystemServer进程被杀后会被重启
  17. try {
  18.         firstBoot = pm.isFirstBoot();
  19. } catch (RemoteException e) {
  20. }
  21. //PackageManagerService执行dex优化
  22. ...
  23. try {
  24.         pm.performBootDexOpt();
  25. } catch (Throwable e) {
  26.         reportWtf("performing boot dexopt", e);
  27. }
複製代碼
首先啟動PackageManagerService,然後判斷該服務是否是第一次啟動,接著執行dex優化。
  1. public static final IPackageManager main(Context context, boolean factoryTest,
  2.                 boolean onlyCore) {
  3.         //构造PackageManagerService服务对象
  4.         PackageManagerService m = new PackageManagerService(context, factoryTest, onlyCore);
  5.         //注册PackageManagerService服务
  6.         ServiceManager.addService("package", m);
  7.         return m;
  8. }
複製代碼
啟動過程比較簡單,就是構造一個PackageManagerService對象,然後將該服務對像註冊到ServiceManger進程中,關於服務註冊過程請查看Android服務註冊完整過程源碼分析。PackageManagerService對像構造過程非常複雜,構造過程分幾個階段.
  1. //PackageManagerService启动开始
  2. EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,SystemClock.uptimeMillis());
  3. //SDK版本检查
  4. if (mSdkVersion <= 0) {
  5.         Slog.w(TAG, "**** ro.build.version.sdk not set!");
  6. }
  7. //读取开机启动模式
  8. String mode = SystemProperties.get("ro.bootmode", "mode");
  9. engModeEnable = "engtest".equals(mode)?true:false;
  10. Slog.i(TAG, "engModeEnable: " + engModeEnable + " ,mode:"+mode);
  11. mContext = context;
  12. mFactoryTest = factoryTest;//开机模式
  13. mOnlyCore = onlyCore;//是否对包做dex优化
  14. //如果编译版本为eng,则不需要dex优化
  15. mNoDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
  16. //创建显示尺寸信息
  17. mMetrics = new DisplayMetrics();
  18. //存储系统运行过程中的设置信息
  19. mSettings = new Settings();
  20. /*创建SharedUserSetting对象并添加到Settings的成员变量mSharedUsers中,在Android系统中,多个package通过设置sharedUserId属性可以运行在同一个进程,共享同一个UID*/
  21. mSettings.addSharedUserLPw("android.uid.system",Process.SYSTEM_UID, ApplicationInfo.FLAG_SYSTEM);
  22. mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID, ApplicationInfo.FLAG_SYSTEM);
  23. mSettings.addSharedUserLPw("android.uid.log", LOG_UID, ApplicationInfo.FLAG_SYSTEM);
  24. mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID, ApplicationInfo.FLAG_SYSTEM);
  25. String separateProcesses = SystemProperties.get("debug.separate_processes");
  26. if (separateProcesses != null && separateProcesses.length() > 0) {
  27.         if ("*".equals(separateProcesses)) {
  28.                 mDefParseFlags = PackageParser.PARSE_IGNORE_PROCESSES;
  29.                 mSeparateProcesses = null;
  30.                 Slog.w(TAG, "Running with debug.separate_processes: * (ALL)");
  31.         } else {
  32.                 mDefParseFlags = 0;
  33.                 mSeparateProcesses = separateProcesses.split(",");
  34.                 Slog.w(TAG, "Running with debug.separate_processes: "
  35.                                 + separateProcesses);
  36.         }
  37. } else {
  38.         mDefParseFlags = 0;
  39.         mSeparateProcesses = null;
  40. }
  41. mPreInstallDir = new File("/system/preloadapp");
  42. //创建应用安装器
  43. mInstaller = new Installer();
  44. //获取屏幕尺寸大小
  45. WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
  46. Display d = wm.getDefaultDisplay();
  47. d.getMetrics(mMetrics);
  48. synchronized (mInstallLock) {
  49. // writer
  50. synchronized (mPackages) {
  51.         //启动消息处理线程
  52.         mHandlerThread.start();
  53.         //为消息处理线程创建一个消息分发handler
  54.         mHandler = new PackageHandler(mHandlerThread.getLooper());
  55.         // dataDir =/data/
  56.         File dataDir = Environment.getDataDirectory();
  57.         // mAppDataDir = /data/data
  58.         mAppDataDir = new File(dataDir, "data");
  59.         // mAsecInternalPath = /data/app-asec
  60.         mAsecInternalPath = new File(dataDir, "app-asec").getPath();
  61.         // mUserAppDataDir = /data/user
  62.         mUserAppDataDir = new File(dataDir, "user");
  63.         // mDrmAppPrivateInstallDir = /data/app-private
  64.         mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
  65.         sUserManager = new UserManager(mInstaller, mUserAppDataDir);
  66.         //读取并解析/etc/permissions下的XML文件
  67.         readPermissions();
  68.         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類之間的關係圖如下: 2.jpg
PackageSignatures來用來描述Android應用程序安裝包的簽名信息,GrantedPermissions類用於描述應用APK的權限信息。Settings類的成員變量mSharedUsers是一個HashMap,用鍵值對的形式保存所有的SharedUserSetting對象,SharedUserSetting對像用於記錄共享同一進程的所有APK信息,該類的成員變量packages的類型為PackageSetting,用來保存所有共享同一UID的包信息,而成員變量userId則是記錄多個APK共享的UID。首先介紹Settings對象的構造過程:
  1. Settings() {
  2.         //调用另外一个有参构造函数
  3.         this(Environment.getDataDirectory());
  4. }

  5. Settings(File dataDir) {
  6.         //创建/data/system/目录
  7.         mSystemDir = new File(dataDir, "system");
  8.         mSystemDir.mkdirs();
  9.         //设置/data/system目录的权限
  10.         FileUtils.setPermissions(mSystemDir.toString(),
  11.                         FileUtils.S_IRWXU|FileUtils.S_IRWXG
  12.                         |FileUtils.S_IROTH|FileUtils.S_IXOTH,
  13.                         -1, -1);
  14.         //mSettingsFilename=/data/system/packages.xml
  15.         mSettingsFilename = new File(mSystemDir, "packages.xml");
  16.         //mBackupSettingsFilename=/data/system/packages-backup.xml
  17.         mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");
  18.         //mPackageListFilename=/data/system/packages.list
  19.         mPackageListFilename = new File(mSystemDir, "packages.list");
  20.         // Deprecated: Needed for migration
  21.         //mStoppedPackagesFilename = /data/system/packages-stopped.xml
  22.         mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
  23.         //mStoppedPackagesFilename = /data/system/packages-stopped-backup.xml
  24.         mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
  25. }
複製代碼
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對像
  1. SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags) {
  2.         //根据进程UID对应的名称从成员变量mSharedUsers中查找对应的SharedUserSetting对象
  3.         SharedUserSetting s = mSharedUsers.get(name);
  4.         //返回查找到的结果
  5.         if (s != null) {
  6.                 if (s.userId == uid) {
  7.                         return s;
  8.                 }
  9.                 PackageManagerService.reportSettingsProblem(Log.ERROR,
  10.                                 "Adding duplicate shared user, keeping first: " + name);
  11.                 return null;
  12.         }
  13.         //没有查找到对应的SharedUserSetting对象,则创建一个新的SharedUserSetting对象。
  14.         s = new SharedUserSetting(name, pkgFlags);
  15.         s.userId = uid;
  16.         //添加到成员变量mUserIds,mOtherUserIds中,这两个变量主要是加快查找速度
  17.         if (addUserIdLPw(uid, s, name)) {
  18.                 //添加到mSharedUsers表中
  19.                 mSharedUsers.put(name, s);
  20.                 return s;
  21.         }
  22.         return null;
  23. }
複製代碼
函數首先根據字符串名稱從成員變量mSharedUsers哈希表中查找對應的SharedUserSetting對象,如果表中不存在對應的SharedUserSetting對象,則創建一個新的SharedUserSetting對象,並初始化該對象的域,然後根據UID的大小通過函數addUserIdLPw添加到mUserIds或mOtherUserIds中,同時以鍵值對的形式保存在mSharedUsers中。
  1. private boolean addUserIdLPw(int uid, Object obj, Object name) {
  2.         //判断添加的UID是否大于19999
  3.         if (uid > Process.LAST_APPLICATION_UID) {
  4.                 return false;
  5.         }
  6.         //判断添加的UID是否大于等于10000
  7.         if (uid >= Process.FIRST_APPLICATION_UID) {
  8.                 //计算在数组中的索引为uid-10000
  9.                 int N = mUserIds.size();
  10.                 final int index = uid - Process.FIRST_APPLICATION_UID;
  11.                 while (index >= N) {
  12.                         mUserIds.add(null);
  13.                         N++;
  14.                 }
  15.                 if (mUserIds.get(index) != null) {
  16.                         PackageManagerService.reportSettingsProblem(Log.ERROR,
  17.                                         "Adding duplicate user id: " + uid
  18.                                         + " name=" + name);
  19.                         return false;
  20.                 }
  21.                 //添加的SharedUserSetting对象到mUserIds动态数组中
  22.                 mUserIds.set(index, obj);
  23.         } else {//将UID小于1000,则将SharedUserSetting对象添加到mOtherUserIds数组中
  24.                 if (mOtherUserIds.get(uid) != null) {
  25.                         PackageManagerService.reportSettingsProblem(Log.ERROR,
  26.                                         "Adding duplicate shared id: " + uid
  27.                                         + " name=" + name);
  28.                         return false;
  29.                 }
  30.                 mOtherUserIds.put(uid, obj);
  31.         }
  32.         return true;
  33. }
複製代碼
Android定義了應用程序的UID範圍,對於非系統應用,UID介於10000到19999之間,而對於系統應用,UID小於10000
  1. public static final int SYSTEM_UID = 1000;
  2. public static final int PHONE_UID = 1001;
  3. public static final int SHELL_UID = 2000;
  4. public static final int LOG_UID = 1007;
  5. public static final int WIFI_UID = 1010;
  6. public static final int MEDIA_UID = 1013;
  7. public static final int DRM_UID = 1019;
  8. public static final int SDCARD_RW_GID = 1015;
  9. public static final int VPN_UID = 1016;
  10. public static final int NFC_UID = 1027;
  11. public static final int MEDIA_RW_GID = 1023;
  12. //应用程序UID范围
  13. public static final int FIRST_APPLICATION_UID = 10000;
  14. public static final int LAST_APPLICATION_UID = 19999;
  15. //fully isolated sandboxed processes UID范围
  16. public static final int FIRST_ISOLATED_UID = 99000;
  17. public static final int LAST_ISOLATED_UID = 99999;
複製代碼
addUserIdLPw函數將UID介於1000到1999之間的SharedUserSetting對像添加到mUserIds數組中,通過UID來索引數組元素。
3.jpg

UID小於1000的SharedUserSetting保存到數組mOtherUserIds中。回到PackageManagerService的構造函數中,通過Settings的addSharedUserLPw函數向mSharedUsers,mUserIds,mOtherUserIds數組添加了4個特定進程的SharedUserSetting對象。 4.jpg
  1. <img src="http://www.tshopping.com.tw/forum.php?mod=image&aid=181194&size=300x300&key=0558a77bc94df9c2&nocache=yes&type=fixnone" border="0" aid="attachimg_181194" alt="">mPreInstallDir = new File("/system/preloadapp");
  2. //创建应用安装器
  3. mInstaller = new Installer();
  4. //获取屏幕尺寸大小
  5. WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
  6. Display d = wm.getDefaultDisplay();
  7. d.getMetrics(mMetrics);
  8. synchronized (mInstallLock) {
  9. // writer
  10. synchronized (mPackages) {
  11.         //启动消息处理线程
  12.         mHandlerThread.start();
  13.         //为消息处理线程创建一个消息分发handler
  14.         mHandler = new PackageHandler(mHandlerThread.getLooper());
  15.         // dataDir =/data/
  16.         File dataDir = Environment.getDataDirectory();
  17.         // mAppDataDir = /data/data
  18.         mAppDataDir = new File(dataDir, "data");
  19.         // mAsecInternalPath = /data/app-asec
  20.         mAsecInternalPath = new File(dataDir, "app-asec").getPath();
  21.         // mUserAppDataDir = /data/user
  22.         mUserAppDataDir = new File(dataDir, "user");
  23.         // mDrmAppPrivateInstallDir = /data/app-private
  24.         mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
複製代碼
首先創建Installer對象,用於訪問installd服務進程,完成一些apk安裝,卸載,優化工作。然後通過WindowManager得到屏幕尺寸信息,接著啟動一個名為PackageManager的消息線程,該線程是PackageManagerService的工作線程,mHandlerThread線程是一個帶消息循環的工作線程,在定義該線程對象的時候就已經創建。
  1. final HandlerThread mHandlerThread = new HandlerThread("PackageManager",Process.THREAD_PRIORITY_BACKGROUND);
複製代碼
同時為該消息線程創建了一個消息分發器PackageHandler對象,通過該handler對象可以往PackageManager線程發送消息,PackageManager線程通過消息循環處理發送進來的消息,消息處理過程如下:
  1. public void handleMessage(Message msg) {
  2.         try {
  3.                 //直接调用doHandleMessage函数来处理各种消息
  4.                 doHandleMessage(msg);
  5.         } finally {
  6.                 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
  7.         }
  8. }
複製代碼
這裡暫時不介紹doHandleMessage函數,在介紹特定消息時,在來分析該函數對各種消息的處理過程。PackageManagerService的線程模型如下:
5.jpg


最後就是創建一些安裝目錄:
/system/preloadapp
/data/data
/data/app-asec
/data/user
/data/data
/data/app-private
創建用戶管理對像UserManager:
  1. sUserManager = new UserManager(mInstaller, mUserAppDataDir);
複製代碼
構造了一個UserManager,參數一為應用程序安裝器Installer,參數二為用戶安裝目錄/data/user
  1. public UserManager(Installer installer, File baseUserPath) {
  2.         this(Environment.getDataDirectory(), baseUserPath);
  3.         mInstaller = installer;
  4.   }
  5.   
  6.   UserManager(File dataDir, File baseUserPath) {
  7.           // mUsersDir=/data/system/users
  8.         mUsersDir = new File(dataDir, USER_INFO_DIR);
  9.         mUsersDir.mkdirs();
  10.           // userZeroDir=/data/system/users/0
  11.         File userZeroDir = new File(mUsersDir, "0");
  12.         userZeroDir.mkdirs();
  13.         //mBaseUserPath=/data/user
  14.         mBaseUserPath = baseUserPath;
  15.         FileUtils.setPermissions(mUsersDir.toString(),
  16.                                    FileUtils.S_IRWXU|FileUtils.S_IRWXG
  17.                         |FileUtils.S_IROTH|FileUtils.S_IXOTH,
  18.                         -1, -1);
  19.         // mUserListFile=/data/system/users/userlist.xml
  20.         mUserListFile = new File(mUsersDir, USER_LIST_FILENAME);
  21.         readUserList();
  22. }
複製代碼
構造過程比較簡單,就是創建幾個目錄和幾個文件:
/data/system/users
/data/system/users/0
/data/system/users/userlist.xml
然後通過函數readUserList讀取用戶列表,這裡不在介紹該函數的實現,該函數就是從userlist.xml文件中讀取用戶信息,保存到UserManager的成員變量mUsers中。
讀取權限配置信息:
  1. //讀取並解析/etc/permissions下的XML文
複製代碼
函數首先調用readPermissions掃瞄讀取並解析/etc/permissions文件夾下的XML文件,並將解析的數據保存到PackageManagerService的成員變量中
6.jpg
  1. <img src="http://www.tshopping.com.tw/forum.php?mod=image&aid=181196&size=300x300&key=0bbd8668da995653&nocache=yes&type=fixnone" border="0" aid="attachimg_181196" alt="">void readPermissions() {
  2.         // Read permissions from .../etc/permission directory.
  3.         File libraryDir = new File(Environment.getRootDirectory(), "etc/permissions");
  4.         if (!libraryDir.exists() || !libraryDir.isDirectory()) {
  5.                 Slog.w(TAG, "No directory " + libraryDir + ", skipping");
  6.                 return;
  7.         }
  8.         if (!libraryDir.canRead()) {
  9.                 Slog.w(TAG, "Directory " + libraryDir + " cannot be read");
  10.                 return;
  11.         }
  12.         // 循环读取etc/permissions目录下的XML文件
  13.         for (File f : libraryDir.listFiles()) {
  14.                 // 跳过platform.xml文件,最后读取该文件
  15.                 if (f.getPath().endsWith("etc/permissions/platform.xml")) {
  16.                         continue;
  17.                 }
  18.                 if (!f.getPath().endsWith(".xml")) {
  19.                         Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring");
  20.                         continue;
  21.                 }
  22.                 if (!f.canRead()) {
  23.                         Slog.w(TAG, "Permissions library file " + f + " cannot be read");
  24.                         continue;
  25.                 }
  26.                 readPermissionsFromXml(f);
  27.         }
  28.         // Read permissions from .../etc/permissions/platform.xml last so it will take precedence
  29.         final File permFile = new File(Environment.getRootDirectory(),"etc/permissions/platform.xml");
  30.         readPermissionsFromXml(permFile);
  31. }
複製代碼
在etc/permissions目錄下保存了一下配置文件
7.jpg
這些文件在編譯的時候直接從frameworks指定目錄下拷貝過來的,在特定product編譯目錄下的base.mk文件中的配置如下:

8.jpg

函數readPermissionsFromXml使用PULL方式解析這些XML文件,下面分別介紹各個標籤的解析過程。

feature標籤用來描述設備應該支持的硬件特性。解析過程如下:
  1. else if ("feature".equals(name)) {
  2.         //读取熟悉name的值
  3.         String fname = parser.getAttributeValue(null, "name");
  4.         if (fname == null) {
  5.                 Slog.w(TAG, "<feature> without name at "+ parser.getPositionDescription());
  6.         } else {
  7.                 //创建一个FeatureInfo对象
  8.                 FeatureInfo fi = new FeatureInfo();
  9.                 fi.name = fname;
  10.                 //mAvailableFeatures是PackageManagerService的成员变量,以HashMap的方式保存硬件支持的特性
  11.                 mAvailableFeatures.put(fname, fi);
  12.         }
  13.         XmlUtils.skipCurrentTag(parser);
  14.         continue;
  15. }
複製代碼
9.jpg

library用於指定系統庫,當應用程序運行時,系統會為進程加載一些必要庫。該標籤的解析過程如下:
  1. else if ("library".equals(name)) {
  2.         //读取属性name的值
  3.         String lname = parser.getAttributeValue(null, "name");
  4.         //读取属性file的值
  5.         String lfile = parser.getAttributeValue(null, "file");
  6.         if (lname == null) {
  7.                 Slog.w(TAG, "<library> without name at "+ parser.getPositionDescription());
  8.         } else if (lfile == null) {
  9.                 Slog.w(TAG, "<library> without file at "+ parser.getPositionDescription());
  10.         } else {
  11.                 //mSharedLibraries是PackageManagerService的成员变量,以HashMap的形式保存进程运行库
  12.                 mSharedLibraries.put(lname, lfile);
  13.         }
  14.         XmlUtils.skipCurrentTag(parser);
  15.         continue;
  16. }
複製代碼
10.jpg
group標籤用於建立Android層與Linux層之間的權限映射關係。
  1. else if ("permission".equals(name)) {
  2.         //读取属性name的值
  3.         String perm = parser.getAttributeValue(null, "name");
  4.         if (perm == null) {
  5.                 Slog.w(TAG, "<permission> without name at "+ parser.getPositionDescription());
  6.                 XmlUtils.skipCurrentTag(parser);
  7.                 continue;
  8.         }
  9.         perm = perm.intern();
  10.         readPermission(parser, perm);
  11. }

  12. void readPermission(XmlPullParser parser, String name)
  13.                 throws IOException, XmlPullParserException {
  14.         name = name.intern();
  15.         //根据name在mSettings.mPermissions表中查找对应的BasePermission对象
  16.         BasePermission bp = mSettings.mPermissions.get(name);
  17.         //如果不存在,则创建一个新的BasePermission对象,并保存到mSettings.mPermissions中
  18.         if (bp == null) {
  19.                 bp = new BasePermission(name, null, BasePermission.TYPE_BUILTIN);
  20.                 mSettings.mPermissions.put(name, bp);
  21.         }
  22.         int outerDepth = parser.getDepth();
  23.         int type;
  24.         while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
  25.                    && (type != XmlPullParser.END_TAG
  26.                                    || parser.getDepth() > outerDepth)) {
  27.                 if (type == XmlPullParser.END_TAG
  28.                                 || type == XmlPullParser.TEXT) {
  29.                         continue;
  30.                 }
  31.                 String tagName = parser.getName();
  32.                 //读取标签group的属性gid值
  33.                 if ("group".equals(tagName)) {
  34.                         String gidStr = parser.getAttributeValue(null, "gid");
  35.                         if (gidStr != null) {
  36.                                 //根据gid字符串,找到对应的gid数值
  37.                                 int gid = Process.getGidForName(gidStr);
  38.                                 //设置BasePermission对象的gid值
  39.                                 bp.gids = appendInt(bp.gids, gid);
  40.                         } else {
  41.                                 Slog.w(TAG, "<group> without gid at "+ parser.getPositionDescription());
  42.                         }
  43.                 }
  44.                 XmlUtils.skipCurrentTag(parser);
  45.         }
  46. }
複製代碼
assign-permission標籤將解析到的內容保存到mSettings.mPermissions中
  1. else if ("assign-permission".equals(name)) {
  2.          //读取属性name的值
  3.         String perm = parser.getAttributeValue(null, "name");
  4.         if (perm == null) {
  5.                 Slog.w(TAG, "<assign-permission> without name at "+ parser.getPositionDescription());
  6.                 XmlUtils.skipCurrentTag(parser);
  7.                 continue;
  8.         }
  9.         //读取属性uid的值
  10.         String uidStr = parser.getAttributeValue(null, "uid");
  11.         if (uidStr == null) {
  12.                 Slog.w(TAG, "<assign-permission> without uid at "+ parser.getPositionDescription());
  13.                 XmlUtils.skipCurrentTag(parser);
  14.                 continue;
  15.         }
  16.         //根据属性uid字符串转换为uid数值
  17.         int uid = Process.getUidForName(uidStr);
  18.         if (uid < 0) {
  19.                 Slog.w(TAG, "<assign-permission> with unknown uid \""+ uidStr + "\" at "+ parser.getPositionDescription());
  20.                 XmlUtils.skipCurrentTag(parser);
  21.                 continue;
  22.         }
  23.         //保存到mSystemPermissions表中
  24.         perm = perm.intern();
  25.         HashSet<String> perms = mSystemPermissions.get(uid);
  26.         if (perms == null) {
  27.                 perms = new HashSet<String>();
  28.                 mSystemPermissions.put(uid, perms);
  29.         }
  30.         perms.add(perm);
  31.         XmlUtils.skipCurrentTag(parser);
  32. }
複製代碼
11.jpg
讀取安裝包信息

/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。
12.jpg
當系統第一次開機時,這些文件並不存在,而在以後的開機中,掃瞄到的這些XML文件是上一次運行過程中創建的。
  1. boolean readLPw(List<UserInfo> users) {
  2.         FileInputStream str = null;
  3.         //如果/data/system/packages-backup.xml文件存在
  4.         if (mBackupSettingsFilename.exists()) {
  5.                 try {
  6.                         //读取/data/system/packages-backup.xml文件
  7.                         str = new FileInputStream(mBackupSettingsFilename);
  8.                         mReadMessages.append("Reading from backup settings file\n");
  9.                         PackageManagerService.reportSettingsProblem(Log.INFO,"Need to read from backup settings file");
  10.                         //当/data/system/packages.xml文件的备份文件存在时,删除packages.xml文件
  11.                         if (mSettingsFilename.exists()) {
  12.                                 Slog.w(PackageManagerService.TAG, "Cleaning up settings file "+ mSettingsFilename);
  13.                                 mSettingsFilename.delete();
  14.                         }
  15.                 } catch (java.io.IOException e) {
  16.                         // We'll try for the normal settings file.
  17.                 }
  18.         }
  19.         mPendingPackages.clear();
  20.         mPastSignatures.clear();
  21.         try {
  22.                 //如果/data/system/packages-backup.xml文件为空
  23.                 if (str == null) {
  24.                         //同时/data/system/packages.xml文件不存在
  25.                         if (!mSettingsFilename.exists()) {
  26.                                 mReadMessages.append("No settings file found\n");
  27.                                 PackageManagerService.reportSettingsProblem(Log.INFO,
  28.                                                 "No settings file; creating initial state");
  29.                                 //读取/etc/preferred-apps目录下的xml文件
  30.                                 readDefaultPreferredAppsLPw();
  31.                                 return false;
  32.                         }
  33.                         //如果packages.xml的备份文件为空,读取packages.xml文件内容
  34.                         str = new FileInputStream(mSettingsFilename);
  35.                 }
  36.                 //解析文件内容
  37.                 XmlPullParser parser = Xml.newPullParser();
  38.                 parser.setInput(str, null);
  39.                 int type;
  40.                 while ((type = parser.next()) != XmlPullParser.START_TAG
  41.                                 && type != XmlPullParser.END_DOCUMENT) {
  42.                         ;
  43.                 }
  44.                 ...
  45. }
複製代碼
接下來檢測並優化BOOTCLASSPATH環境變量指定的Java運行庫及platform.xml中配置的Java庫,同時優化/system/framework目錄下的Jar包和apk文件,最後刪除/data/dalvik-cache目錄下的一些緩存文件。在init.rc中配置的BOOTCLASSPATH如下:
13.jpg
  1. <img src="http://www.tshopping.com.tw/forum.php?mod=image&aid=181203&size=300x300&key=d53ac82dbd66612c&nocache=yes&type=fixnone" border="0" aid="attachimg_181203" alt="">long startTime = SystemClock.uptimeMillis();
  2. EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,startTime);
  3. // 设置扫描模式
  4. int scanMode = SCAN_MONITOR | SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING;
  5. if (mNoDexOpt) {
  6.         Slog.w(TAG, "Running ENG build: no pre-dexopt!");
  7.         scanMode |= SCAN_NO_DEX;
  8. }
  9. //保存库文件路径
  10. final HashSet<String> libFiles = new HashSet<String>();
  11. //mFrameworkDir = /framework/
  12. mFrameworkDir = new File(Environment.getRootDirectory(), "framework");
  13. //mDalvikCacheDir = /data/dalvik-cache/
  14. mDalvikCacheDir = new File(dataDir, "dalvik-cache");
  15. boolean didDexOpt = false;
  16. //通过属性的方式得到启动Java启动类库的路径,在init.rc中通过BOOTCLASSPATH环境变量的方式设置
  17. String bootClassPath = System.getProperty("java.boot.class.path");
  18. if (bootClassPath != null) {
  19.         String[] paths = splitString(bootClassPath, ':');
  20.         for (int i=0; i<paths.length; i++) {
  21.                 try {
  22.                         //判断Jar包是否需要dex优化
  23.                         if (dalvik.system.DexFile.isDexOptNeeded(paths[i])) {
  24.                                 //如果需要则添加到libFiles表中
  25.                                 libFiles.add(paths[i]);
  26.                                 //通过安装器请求installd服务进程执行dex优化
  27.                                 mInstaller.dexopt(paths[i], Process.SYSTEM_UID, true);
  28.                                 didDexOpt = true;
  29.                         }
  30.                 } catch (FileNotFoundException e) {
  31.                         Slog.w(TAG, "Boot class path not found: " + paths[i]);
  32.                 } catch (IOException e) {
  33.                         Slog.w(TAG, "Cannot dexopt " + paths[i] + "; is it an APK or JAR? "
  34.                                         + e.getMessage());
  35.                 }
  36.         }
  37. } else {
  38.         Slog.w(TAG, "No BOOTCLASSPATH found!");
  39. }
  40. //在前面解析platfor.xml时,将一些额外的类库路径保存到了mSharedLibraries变量中
  41. if (mSharedLibraries.size() > 0) {
  42.         //循环变量mSharedLibraries变量
  43.         Iterator<String> libs = mSharedLibraries.values().iterator();
  44.         while (libs.hasNext()) {
  45.                 String lib = libs.next();
  46.                 try {
  47.                         //判断Jar包是否需要dex优化
  48.                         if (dalvik.system.DexFile.isDexOptNeeded(lib)) {
  49.                                 //如果需要则添加到libFiles表中
  50.                                 libFiles.add(lib);
  51.                                 //通过安装器进行dex优化
  52.                                 mInstaller.dexopt(lib, Process.SYSTEM_UID, true);
  53.                                 didDexOpt = true;
  54.                         }
  55.                 } catch (FileNotFoundException e) {
  56.                         Slog.w(TAG, "Library not found: " + lib);
  57.                 } catch (IOException e) {
  58.                         Slog.w(TAG, "Cannot dexopt " + lib + "; is it an APK or JAR? "
  59.                                         + e.getMessage());
  60.                 }
  61.         }
  62. }
  63. //将/system/frameworks/framework-res.apk添加到libFiles中
  64. libFiles.add(mFrameworkDir.getPath() + "/framework-res.apk");
  65. //列出/system/frameworks目录下的文件
  66. String[] frameworkFiles = mFrameworkDir.list();
  67. if (frameworkFiles != null) {
  68.         //遍历/system/frameworks目录下的文件
  69.         for (int i=0; i<frameworkFiles.length; i++) {
  70.                 File libPath = new File(mFrameworkDir, frameworkFiles[i]);
  71.                 String path = libPath.getPath();
  72.                 //判断libFiles中是否已经包含该文件,如果包含则跳过
  73.                 if (libFiles.contains(path)) {
  74.                         continue;
  75.                 }
  76.                 //跳过不是apk或jar文件
  77.                 if (!path.endsWith(".apk") && !path.endsWith(".jar")) {
  78.                         continue;
  79.                 }
  80.                 try {
  81.                         //判断Jar包或apk是否需要dex优化
  82.                         if (dalvik.system.DexFile.isDexOptNeeded(path)) {
  83.                                 //通过安装器进行dex优化
  84.                                 mInstaller.dexopt(path, Process.SYSTEM_UID, true);
  85.                                 didDexOpt = true;
  86.                         }
  87.                 } catch (FileNotFoundException e) {
  88.                         Slog.w(TAG, "Jar not found: " + path);
  89.                 } catch (IOException e) {
  90.                         Slog.w(TAG, "Exception reading jar: " + path, e);
  91.                 }
  92.         }
  93. }
  94. //如果前面对某个文件做过优化,只要优化了,didDexOpt就被设置为true
  95. if (didDexOpt) {
  96.         //遍历/data/dalvik-cache目录下的文件
  97.         String[] files = mDalvikCacheDir.list();
  98.         if (files != null) {
  99.                 for (int i=0; i<files.length; i++) {
  100.                         String fn = files[i];
  101.                         //删除文件名以"data@app@"和"data@app-private@"开头的文件
  102.                         if (fn.startsWith("data@app@")
  103.                                         || fn.startsWith("data@app-private@")) {
  104.                                 Slog.i(TAG, "Pruning dalvik file: " + fn);
  105.                                 (new File(mDalvikCacheDir, fn)).delete();
  106.                         }
  107.                 }
  108.         }
  109. }
複製代碼
接著掃瞄系統apk信息
  1. mFlagInstall = false;
  2. //创建文件夹监控对象,监视/system/framework目录
  3. mFrameworkInstallObserver = new AppDirObserver(mFrameworkDir.getPath(), OBSERVER_EVENTS, true);
  4. mFrameworkInstallObserver.startWatching();
  5. //扫描/system/framework目录下的apk文件,扫描模式设置为非优化模式
  6. scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM| PackageParser.PARSE_IS_SYSTEM_DIR,scanMode | SCAN_NO_DEX, 0);
  7. //在工厂模式下,调用函数scanDirLIOnly只扫描特定的apk文件
  8. if(engModeEnable){
  9.         //temp null
  10.         mVendorAppDir = null;
  11.         mDrmAppInstallObserver = null;
  12.         mSystemAppDir = null;
  13.         mAppInstallObserver = null;
  14.         mSystemInstallObserver = null;
  15.         mPreInstallObserver = null;
  16.         mVendorInstallObserver = null;
  17.         mAppInstallDir = null;
  18.         Slog.i(TAG, " begin scan the apps !");
  19.         scanDirLIOnly(PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
  20.         Slog.i(TAG, " end scan the apps !");
  21.         engModeEnable = false;
  22. }else{//正常模式下
  23.         //创建文件夹监控对象,监视/system/app目录
  24.         mSystemAppDir = new File(Environment.getRootDirectory(), "app");
  25.         mSystemInstallObserver = new AppDirObserver(
  26.                 mSystemAppDir.getPath(), OBSERVER_EVENTS, true);
  27.         mSystemInstallObserver.startWatching();
  28.         //扫描/system/app目录
  29.         scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM| PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
  30.         //创建文件夹监控对象,监视/vendor/app目录
  31.         mVendorAppDir = new File("/vendor/app");
  32.         mVendorInstallObserver = new AppDirObserver(
  33.                 mVendorAppDir.getPath(), OBSERVER_EVENTS, true);
  34.         mVendorInstallObserver.startWatching();
  35.         //扫描/vendor/app目录
  36.         scanDirLI(mVendorAppDir, PackageParser.PARSE_IS_SYSTEM| PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);

  37.         if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands");
  38.         mInstaller.moveFiles();
  39.         // Prune any system packages that no longer exist.
  40.         final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<String>();
  41.         if (!mOnlyCore) {
  42.                 //遍历Settings的成员变量mPackages
  43.                 Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
  44.                 while (psit.hasNext()) {
  45.                         PackageSetting ps = psit.next();
  46.                         //不是系统app
  47.                         if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
  48.                                 continue;
  49.                         }
  50.                         //如果是系统app,同时已经被PackageManagerService扫描过了
  51.                         final PackageParser.Package scannedPkg = mPackages.get(ps.name);
  52.                         if (scannedPkg != null) {
  53.                                 //该apk包已不能使用
  54.                                 if (mSettings.isDisabledSystemPackageLPr(ps.name)) {
  55.                                         Slog.i(TAG, "Expecting better updatd system app for " + ps.name
  56.                                                         + "; removing system app");
  57.                                         //移除该apk包信息
  58.                                         removePackageLI(scannedPkg, true);
  59.                                 }
  60.                                 continue;
  61.                         }
  62.                         if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {
  63.                                 psit.remove();
  64.                                 String msg = "System package " + ps.name
  65.                                                 + " no longer exists; wiping its data";
  66.                                 reportSettingsProblem(Log.WARN, msg);
  67.                                 mInstaller.remove(ps.name, 0);
  68.                                 sUserManager.removePackageForAllUsers(ps.name);
  69.                         } else {
  70.                                 final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps.name);
  71.                                 if (disabledPs.codePath == null || !disabledPs.codePath.exists()) {
  72.                                         possiblyDeletedUpdatedSystemApps.add(ps.name);
  73.                                 }
  74.                         }
  75.                 }
  76.         }
  77.         //mAppInstallDir = /data/app/
  78.         mAppInstallDir = new File(dataDir, "app");
  79.         //查找未完成安装的apk包
  80.         ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackagesLPr();
  81.         //清除未完成安装包
  82.         for(int i = 0; i < deletePkgsList.size(); i++) {
  83.                 //clean up here
  84.                 cleanupInstallFailedPackage(deletePkgsList.get(i));
  85.         }
  86.         //删除临时文件
  87.         deleteTempPackageFiles();
複製代碼
監控並掃瞄以下三個系統包安裝目錄:
/system/framework :該目錄下的文件都是系統庫
/system/app :該目錄下是默認的系統應用
/vendor/app :該目錄下是廠商定制的應用
最後掃瞄非系統apk信息
  1. if (!mOnlyCore) {
  2.         //标识数据扫描开始        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,SystemClock.uptimeMillis());
  3.         //创建文件夹监控对象,监视/data/app/目录
  4.         mAppInstallObserver = new AppDirObserver(mAppInstallDir.getPath(), OBSERVER_EVENTS, false);
  5.         mAppInstallObserver.startWatching();
  6.         //扫描/data/app/目录下的apk文件
  7.         scanDirLI(mAppInstallDir, 0, scanMode, 0);
  8.         //创建文件夹监控对象,监视/system/preloadapp/目录
  9.         mPreInstallObserver = new AppDirObserver(mPreInstallDir.getPath(), OBSERVER_EVENTS, false);
  10.         mPreInstallObserver.startWatching();
  11.         //扫描/system/preloadapp/目录下的apk文件
  12.         scanDirLI(mPreInstallDir, 0, scanMode, 0);
  13.         //创建文件夹监控对象,监视/data/app-private/目录
  14.         mDrmAppInstallObserver = new AppDirObserver(mDrmAppPrivateInstallDir.getPath(), OBSERVER_EVENTS, false);
  15.         mDrmAppInstallObserver.startWatching();
  16.         //扫描/data/app-private/目录下的apk文件
  17.         scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,scanMode, 0);
  18.         /**
  19.          * Remove disable package settings for any updated system
  20.          * apps that were removed via an OTA. If they're not a
  21.          * previously-updated app, remove them completely.
  22.          * Otherwise, just revoke their system-level permissions.
  23.          */
  24.         for (String deletedAppName : possiblyDeletedUpdatedSystemApps) {
  25.                 PackageParser.Package deletedPkg = mPackages.get(deletedAppName);
  26.                 mSettings.removeDisabledSystemPackageLPw(deletedAppName);
  27.                 String msg;
  28.                 if (deletedPkg == null) {
  29.                         msg = "Updated system package " + deletedAppName+ " no longer exists; wiping its data";
  30.                         mInstaller.remove(deletedAppName, 0);
  31.                         sUserManager.removePackageForAllUsers(deletedAppName);
  32.                 } else {
  33.                         msg = "Updated system app + " + deletedAppName+ " no longer present; removing system privileges for "+ deletedAppName;
  34.                         deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
  35.                         PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName);
  36.                         deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM;
  37.                 }
  38.                 reportSettingsProblem(Log.WARN, msg);
  39.         }
  40. } else {
  41.         mPreInstallObserver = null;
  42.         mAppInstallObserver = null;
  43.         mDrmAppInstallObserver = null;
  44. }
複製代碼
i監控並掃瞄以下三個數據目錄:
/data/app/
/system/preloadapp/
/data/app-private/
最後進入結尾階段,將掃瞄到的信息保存到文件中。
  1. mFlagInstall = true;
  2. EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,SystemClock.uptimeMillis());
  3. final boolean regrantPermissions = mSettings.mInternalSdkPlatform != mSdkVersion;
  4. mSettings.mInternalSdkPlatform = mSdkVersion;
  5. updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL | (regrantPermissions
  6.                                 ? (UPDATE_PERMISSIONS_REPLACE_PKG|UPDATE_PERMISSIONS_REPLACE_ALL): 0));
  7. ArrayList<PreferredActivity> removed = new ArrayList<PreferredActivity>();
  8. for (PreferredActivity pa : mSettings.mPreferredActivities.filterSet()) {
  9.         if (mActivities.mActivities.get(pa.mPref.mComponent) == null) {
  10.                 removed.add(pa);
  11.         }
  12. }
  13. for (int i=0; i<removed.size(); i++) {
  14.         PreferredActivity pa = removed.get(i);
  15.         Slog.w(TAG, "Removing dangling preferred activity: "
  16.                         + pa.mPref.mComponent);
  17.         mSettings.mPreferredActivities.removeFilter(pa);
  18. }
  19. // can downgrade to reader
  20. mSettings.writeLPr();

  21. EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,SystemClock.uptimeMillis());
  22. Runtime.getRuntime().gc();
  23. mRequiredVerifierPackage = getRequiredVerifierLPr();
複製代碼
至此,PackageManagerService就構造完成了,構造過程認為繁重,Apk文件掃瞄解析耗費比較長的時間,這是導致開機速度慢的原因。

轉帖於:http://blog.csdn.net/zhao_3546/article/details/12028271


 

臉書網友討論
發表於 2013-12-14 23:11:43 | 顯示全部樓層
頂!好貼  好文~~~一定要分享.

版主招募中

*滑块验证:
您需要登錄後才可以回帖 登錄 | 註冊 |

本版積分規則



Archiver|手機版|小黑屋|免責聲明|TShopping

GMT+8, 2024-3-28 16:45 , Processed in 0.067227 second(s), 25 queries .

本論壇言論純屬發表者個人意見,與 TShopping綜合論壇 立場無關 如有意見侵犯了您的權益 請寫信聯絡我們。

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回復 返回頂部 返回列表