PakageManagerService的启动流程图

1.PakageManagerService概述

PakageManagerService是android系统中一个核心的服务,它负责系统中Package的管理,应该程序的安装、卸载等。后面PakageManagerService简称PMS。

2.SystemServer启动PackageManagerService

我之前的ATA文章有说到,SystemServer进程是Zygote孵化出的第一个进程,该进程主要的工作是启动android系统服务进程,其中包括PackageManagerService服务,SystemServer启动PMS关键源码如下:

  1. private void startBootstrapServices() {
  2. //...
  3. //调用PMS的main函数
  4. mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
  5. mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
  6. //判断本次是否为初次启动,当Zygote或者SystemServer退出时,init会再次启动它们,所以这里
  7. //的firstBoot指的是开机后的第一次启动
  8. mFirstBoot = mPackageManagerService.isFirstBoot();
  9. mPackageManager = mSystemContext.getPackageManager();
  10. //...
  11. }

关键点

  • PMS的main函数,该函数是PKM的核心。

3.PMS的main方法

PackageManagerService的主要功能是,扫描Android系统中几个目标文件夹的APK,建立对应的数据结构来管理Package信息、四大组件信息、权限信息等各种信息。例如PKMS解析APK包中的AndroidMainfest.xml,并根据其中声明的Activity标签来创建对应的对象并加以保管。PMS的main方法的代码如下:

  1. public static PackageManagerService main(Context context, Installer installer,
  2. boolean factoryTest, boolean onlyCore) {
  3. //new 一个PackageManagerService对象
  4. PackageManagerService m = new PackageManagerService(context, installer,
  5. factoryTest, onlyCore);
  6. //PKM注册到ServiceManager上。ServiceManager相当于安卓系统服务的DNS服务器
  7. ServiceManager.addService("package", m);
  8. return m;
  9. }

该方法看似很简单,只有几行代码,然而执行事件却比较长,这是因为PMS在其构造函数中做了很多的“重体力活”,这也是android启动速度慢的主要因素之一。安装的应用越多,系统启动开机时间越长。
PMS构造函数的主要工作流程

  • 扫描目标文件夹之前的准备工作。
  • 扫描目标文件夹。
  • 扫描之后的工作。

4.PMS的前期准备工作

4.1探究Setting

  1. public PackageManagerService(Context context, Installer installer,
  2. boolean factoryTest, boolean onlyCore) {
  3. EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
  4. SystemClock.uptimeMillis());
  5. if (mSdkVersion <= 0) {
  6. Slog.w(TAG, "**** ro.build.version.sdk not set!");
  7. }
  8. mContext = context;
  9. //是否在工厂测试模式下,假定为false
  10. mFactoryTest = factoryTest;
  11. mOnlyCore = onlyCore;
  12. //如果此系统是“eng”版,扫描Package后,不对package做dex优化
  13. mLazyDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
  14. //用于存储与显示屏相关的一些属性,例如屏幕的宽高分辨率等。
  15. mMetrics = new DisplayMetrics();
  16. mSettings = new Settings(mPackages);
  17. //第一个参数是字符串“android.uid.system”;第二个是SYSTEM_UID,其值为1000,
  18. //第三个是FLAG_SYSTEM标志,用于标识系统Package。
  19. mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
  20. ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
  21. mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
  22. ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
  23. mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
  24. ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
  25. mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,
  26. ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
  27. mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,
  28. ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
  29. mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
  30. ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
4.1.1Android系统中UID/GID

UID为用户ID的缩写,GID为用户组ID的缩写,这两个概念均与Linux系统中进程的权限管理有关。一般来说,每一个进程都有一个对应的UID,表示该进程属于哪个用户,不同用户有不同权限。一个进程也可分属不同的用户组,每个用户组都有对应的权限。
在android系统中,系统定义的UID/GID在Process.java文件中,关键源码如下所示

  1. /**
  2. * Defines the UID/GID under which system code runs.
  3. */
  4. public static final int SYSTEM_UID = 1000;
  5. /**
  6. * Defines the UID/GID under which the telephony code runs.
  7. */
  8. public static final int PHONE_UID = 1001;
  9. /**
  10. * Defines the UID/GID for the user shell.
  11. * @hide
  12. */
  13. public static final int SHELL_UID = 2000;
  14. /**
  15. * Defines the UID/GID for the log group.
  16. * @hide
  17. */
  18. public static final int LOG_UID = 1007;
  19. /**
  20. * Defines the UID/GID for the WIFI supplicant process.
  21. * @hide
  22. */
  23. public static final int WIFI_UID = 1010;
  24. /**
  25. * Defines the UID/GID for the mediaserver process.
  26. * @hide
  27. */
  28. public static final int MEDIA_UID = 1013;
  29. /**
  30. * Defines the UID/GID for the DRM process.
  31. * @hide
  32. */
  33. public static final int DRM_UID = 1019;
  34. /**
  35. * Defines the UID/GID for the group that controls VPN services.
  36. * @hide
  37. */
  38. public static final int VPN_UID = 1016;
  39. /**
  40. * Defines the UID/GID for the NFC service process.
  41. * @hide
  42. */
  43. public static final int NFC_UID = 1027;
  44. /**
  45. * Defines the UID/GID for the Bluetooth service process.
  46. * @hide
  47. */
  48. public static final int BLUETOOTH_UID = 1002;
  49. /**
  50. * Defines the GID for the group that allows write access to the internal media storage.
  51. * @hide
  52. */
  53. public static final int MEDIA_RW_GID = 1023;
  54. /**
  55. * Access to installed package details
  56. * @hide
  57. */
  58. public static final int PACKAGE_INFO_GID = 1032;
  59. /**
  60. * Defines the UID/GID for the shared RELRO file updater process.
  61. * @hide
  62. */
  63. public static final int SHARED_RELRO_UID = 1037;
  64. /**
  65. * Defines the start of a range of UIDs (and GIDs), going from this
  66. * number to {@link #LAST_APPLICATION_UID} that are reserved for assigning
  67. * to applications.
  68. */
  69. public static final int FIRST_APPLICATION_UID = 10000;
  70. /**
  71. * Last of application-specific UIDs starting at
  72. * {@link #FIRST_APPLICATION_UID}.
  73. */
  74. public static final int LAST_APPLICATION_UID = 19999;
  75. /**
  76. * First uid used for fully isolated sandboxed processes (with no permissions of their own)
  77. * @hide
  78. */
  79. public static final int FIRST_ISOLATED_UID = 99000;
  80. /**
  81. * Last uid used for fully isolated sandboxed processes (with no permissions of their own)
  82. * @hide
  83. */
  84. public static final int LAST_ISOLATED_UID = 99999;
4.1.2 探究SharedUserSetting

Setting中有一个mShareUsers成员,该成员存储的是字符串变量name与SharedUserSetting健值对。

  1. SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
  2. //mSharedUsers是一个HashMap.key为字符串,值为ShareUserSetting对象
  3. SharedUserSetting s = mSharedUsers.get(name);
  4. if (s != null) {
  5. if (s.userId == uid) {
  6. return s;
  7. }
  8. //...
  9. return null;
  10. }
  11. 创建一个SharedUserSetting对象,并设置为useriduid
  12. s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
  13. s.userId = uid;
  14. if (addUserIdLPw(uid, s, name)) {
  15. mSharedUsers.put(name, s);
  16. return s;
  17. }
  18. return null;
  19. }

例如在SystemUI.apk的AndroidManifest.xml文件中,有关键代码:

  1. <mainfest xmlns:android="http://schemas.android.com/apk/res/android"
  2. package="com.android.systemui"
  3. coreApp="true"
  4. android:sharedUserId="android.uid.system"
  5. android:process="system">
  6. ....

在该标签中,声明了一个android:sharedUserId的属性,其值为“android.uid.system”。sharedUserId和UID有关,它的作用是

  • 两个或者多个声明了同一种sharedUserid的APK可共享彼此的数据,并且可运行在同一进程中。
  • 通过声明特定的sharedUserId,该APK所在的进程将被赋予指定UID。

例如SystemUI声明了system的uid,运行SystemUI的进程就可享有system用户所对应的权限了,实际上就是将该进程的UID设置为system的uid了

接下来分析addUserIdLPw的功能,它主要就是将SharedUserSettings对象保存到对应的数组中,代码如下

  1. private boolean addUserIdLPw(int uid, Object obj, Object name) {
  2. //uid不能超出限制,Android对uid进行归纳,系统APK所在进程小于10000
  3. //应用APK所在进程的uid从10000开始
  4. if (uid > Process.LAST_APPLICATION_UID) {
  5. return false;
  6. }
  7. //FIRST_APPLICATION_UID = 10000,属于应用APK
  8. if (uid >= Process.FIRST_APPLICATION_UID) {
  9. int N = mUserIds.size();
  10. //计算索引,其值是uid和FIRST_APPLICATION_UID的差
  11. final int index = uid - Process.FIRST_APPLICATION_UID;
  12. while (index >= N) {
  13. mUserIds.add(null);
  14. N++;
  15. }
  16. //如果索引位置不为空,返回
  17. if (mUserIds.get(index) != null) {
  18. PackageManagerService.reportSettingsProblem(Log.ERROR,
  19. "Adding duplicate user id: " + uid
  20. + " name=" + name);
  21. return false;
  22. }
  23. //mUserIds保存应用Package的uid,obj是SharedUserSettings
  24. mUserIds.set(index, obj);
  25. } else {
  26. if (mOtherUserIds.get(uid) != null) {
  27. PackageManagerService.reportSettingsProblem(Log.ERROR,
  28. "Adding duplicate shared id: " + uid
  29. + " name=" + name);
  30. return false;
  31. }
  32. mOtherUserIds.put(uid, obj);
  33. }
  34. return true;
  35. }

4.2 XML文件扫描

接下来是扫描系统目录下与系统权限相关的xml文件,将其存放到PKM中,关键源码如下:

  1. // 获取系统相关的权限,它主要是解析系统目录下xml文件,获得设备相关的权限
  2. SystemConfig systemConfig = SystemConfig.getInstance();
  3. mGlobalGids = systemConfig.getGlobalGids();
  4. mSystemPermissions = systemConfig.getSystemPermissions();
  5. mAvailableFeatures = systemConfig.getAvailableFeatures();
  6. synchronized (mInstallLock) {
  7. // writer
  8. synchronized (mPackages) {
  9. //创建一个ThreadHandler对象,实际就是创建一个带消息队列循环处理的线程,
  10. //该线程的工作是:程序的安装和卸载等。
  11. mHandlerThread = new ServiceThread(TAG,
  12. Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
  13. mHandlerThread.start();
  14. //以ThreadHandler线程的消息循环(Looper对象)作为参数new一个
  15. //PackageHandler,因此该Handler的handlemessage方法将运行在此线程上
  16. mHandler = new PackageHandler(mHandlerThread.getLooper());
  17. Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
  18. // /data目录
  19. File dataDir = Environment.getDataDirectory();
  20. // /data/data目录
  21. mAppDataDir = new File(dataDir, "data");
  22. // /data/app目录
  23. mAppInstallDir = new File(dataDir, "app");
  24. // /data/app-lib目录
  25. mAppLib32InstallDir = new File(dataDir, "app-lib");
  26. // /data/app-asec目录
  27. mAsecInternalPath = new File(dataDir, "app-asec").getPath();
  28. // /data/user目录
  29. mUserAppDataDir = new File(dataDir, "user");
  30. // /data/app-private目录
  31. mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
  32. //new一个UserManager对象,目前没有什么作用,但其前途不可限量。
  33. //google设想,未来手机将支持多个User,每个User安装自己的应用
  34. //该功能为android智能手机推向企业用户打下基础
  35. sUserManager = new UserManagerService(context, this,
  36. mInstallLock, mPackages);
  37. // 获取系统相关的权限,它主要是解析系统目录下xml文件,获得设备相关的权限
  38. ArrayMap<String, SystemConfig.PermissionEntry> permConfig
  39. = systemConfig.getPermissions();
  40. for (int i=0; i<permConfig.size(); i++) {
  41. SystemConfig.PermissionEntry perm = permConfig.valueAt(i);
  42. BasePermission bp = mSettings.mPermissions.get(perm.name);
  43. if (bp == null) {
  44. bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN);
  45. mSettings.mPermissions.put(perm.name, bp);
  46. }
  47. if (perm.gids != null) {
  48. bp.setGids(perm.gids, perm.perUser);
  49. }
  50. }
  51. //获得系统的Libraries,也就是系统的一些jar
  52. ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries();
  53. for (int i=0; i<libConfig.size(); i++) {
  54. mSharedLibraries.put(libConfig.keyAt(i),
  55. new SharedLibraryEntry(libConfig.valueAt(i), null));
  56. }
  57. mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();
  58. mRestoredSettings = mSettings.readLPw(this, sUserManager.getUsers(false),
  59. mSdkVersion, mOnlyCore);
  60. String customResolverActivity = Resources.getSystem().getString(
  61. R.string.config_customResolverActivity);
  62. if (TextUtils.isEmpty(customResolverActivity)) {
  63. customResolverActivity = null;
  64. } else {
  65. mCustomResolverComponentName = ComponentName.unflattenFromString(
  66. customResolverActivity);
  67. }
  68. long startTime = SystemClock.uptimeMillis();

进一步我们再观察SystemConfig是如何解析系统权限xml文件的,在SystemConfig的构造函数中,它会去分别读取etc目录下的sysconfig,permissions,sysconfig目录下的文件。

  1. SystemConfig() {
  2. // Read configuration from system
  3. readPermissions(Environment.buildPath(
  4. Environment.getRootDirectory(), "etc", "sysconfig"), false);
  5. // Read configuration from the old permissions dir
  6. readPermissions(Environment.buildPath(
  7. Environment.getRootDirectory(), "etc", "permissions"), false);
  8. // Only read features from OEM config
  9. readPermissions(Environment.buildPath(
  10. Environment.getOemDirectory(), "etc", "sysconfig"), true);
  11. readPermissions(Environment.buildPath(
  12. Environment.getOemDirectory(), "etc", "permissions"), true);
  13. }

我们看看到底这些目录下放着什么样的文件,例如/etc/permissions目录下的文件如下图:

我们再打开第一个文件来探究,没错,这个文件代表蓝牙权限,表示该设备支持蓝牙。具体代码如下

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <permissions>
  3. <feature name="android.hardware.bluetooth" />
  4. </permissions>

总结一下PMS的前期工作,其实就是扫描并解析XML文件,将其中的信息保存到特定的数据结构中。

5.PMS扫描Package

第二个阶段的工作主要是扫描系统中的APK,由于需要逐个扫描apk文件,因此手机上安装的程序越多,PKM的工作量越大,系统启动速度越慢,也就是开机时间越长。

5.1系统库的dex优化

以下的代码主要是对系统库BOOTCLASSPATH指定,或platform.xml定义,或者/system/frameworks目录下的jar
和apk包进行一次检查,该dex优化的优化.dex优化后会在相应的目录生成.odex文件。/system/frameworks如下图:


关键源码如下:

  1. // Set flag to monitor and not change apk file paths when
  2. // scanning install directories.
  3. //定义扫描参数
  4. final int scanFlags = SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING | SCAN_INITIAL;
  5. //用于存储已经dex优化过或者不需要优化的包
  6. final ArraySet<String> alreadyDexOpted = new ArraySet<String>();
  7. /**
  8. * Add everything in the in the boot class path to the
  9. * list of process files because dexopt will have been run
  10. * if necessary during zygote startup.
  11. */
  12. //获取java启动类库的路径,在init.rc文件中通过BOOTCLASSPATH环境变量输出
  13. //主要是/system/framework/下的系统jar包
  14. final String bootClassPath = System.getenv("BOOTCLASSPATH");
  15. final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");
  16. if (bootClassPath != null) {
  17. String[] bootClassPathElements = splitString(bootClassPath, ':');
  18. //循环遍历/system/framework/下的系统jar包的绝对路径,添加到alreadyDexOpted
  19. for (String element : bootClassPathElements) {
  20. alreadyDexOpted.add(element);
  21. }
  22. }
  23. if (mSharedLibraries.size() > 0) {
  24. //...
  25. if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
  26. alreadyDexOpted.add(lib);
  27. //dex优化
  28. mInstaller.dexopt(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded);
  29. }
  30. }
  31. //...
  32. File frameworkDir = new File(Environment.getRootDirectory(), "framework");
  33. //framework-res.apk定义了系统常用的资源,还有几个重要的Activity,如长按Power键弹出选择框
  34. //不需要dex优化
  35. alreadyDexOpted.add(frameworkDir.getPath() + "/framework-res.apk");
  36. alreadyDexOpted.add(frameworkDir.getPath() + "/core-libart.jar");
  37. //扫描framework/下的apk或者jar进行dex优化
  38. String[] frameworkFiles = frameworkDir.list();
  39. if (frameworkFiles != null) {
  40. // TODO: We could compile these only for the most preferred ABI. We should
  41. // first double check that the dex files for these commands are not referenced
  42. // by other system apps.
  43. for (String dexCodeInstructionSet : dexCodeInstructionSets) {
  44. for (int i=0; i<frameworkFiles.length; i++) {
  45. File libPath = new File(frameworkDir, frameworkFiles[i]);
  46. String path = libPath.getPath();
  47. // 跳过已经存在的包
  48. if (alreadyDexOpted.contains(path)) {
  49. continue;
  50. }
  51. // 不是apk或者jar的不做处理
  52. if (!path.endsWith(".apk") && !path.endsWith(".jar")) {
  53. continue;
  54. }
  55. int dexoptNeeded = DexFile.getDexOptNeeded(path, null, dexCodeInstructionSet, false);
  56. if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
  57. //dex优化
  58. mInstaller.dexopt(path, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded);
  59. }
  60. }
  61. }
  62. }

5.2扫描系统的APK

对apk或者jar进行dex优化后,现在PKM进入了重点阶段,扫描系统的APK,每一个APK对应一个Package对象,主要是扫描APK的AndroidManifest.xml,解析application标签及其子标签actvity、service、recever等,也就是android的四大组件,解析后将它们保存到Package对应的数据结构中,最后将它们注册到PKM中,要扫描以下几个目录:

  • /system/frameworks:该目录下的文件都是系统库,例如service.jar、framework.jar、framework-res.apk。不过只扫描framework-res.apk文件
  • /system/app:该目录下全是默认的系统应用和厂商特定的APK文件,例如Buletooth.apk、和SystemUI.apk等

解析AndroidManifest.xml关键的源码如下:

  1. private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,
  2. String[] outError) throws XmlPullParserException, IOException {
  3. while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
  4. && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
  5. if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
  6. continue;
  7. }
  8. String tagName = parser.getName();
  9. //解析<application>
  10. if (tagName.equals("application")) {
  11. if (foundApp) {
  12. if (RIGID_PARSER) {
  13. outError[0] = "<manifest> has more than one <application>";
  14. mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
  15. return null;
  16. } else {
  17. Slog.w(TAG, "<manifest> has more than one <application>");
  18. XmlUtils.skipCurrentTag(parser);
  19. continue;
  20. }
  21. }
  22. foundApp = true;
  23. //解析<application>及其子标签
  24. if (!parseBaseApplication(pkg, res, parser, attrs, flags, outError)) {
  25. return null;
  26. }
  27. //解析<application>
  28. } else if (tagName.equals("overlay")) {
  29. } else if (tagName.equals("key-sets")) {
  30. } else if (tagName.equals("permission-group")) {
  31. } else if (tagName.equals("permission")) {
  32. } else if (tagName.equals("permission-tree")) {
  33. //解析<uses-permission>
  34. } else if (tagName.equals("uses-permission")) {
  35. } else if (tagName.equals("uses-permission-sdk-m")
  36. || tagName.equals("uses-permission-sdk-23")) {
  37. } else if (tagName.equals("uses-configuration")) {
  38. } else if (tagName.equals("uses-feature")) {
  39. } else if (tagName.equals("feature-group")) {
  40. } else if (tagName.equals("uses-sdk")) {
  41. } else if (tagName.equals("supports-screens")) {
  42. } else if (tagName.equals("instrumentation")) {
  43. } else if (tagName.equals("original-package")) {
  44. } else if (tagName.equals("adopt-permissions")) {
  45. } else if (tagName.equals("uses-gl-texture")) {
  46. } else if (tagName.equals("compatible-screens")) {
  47. } else if (tagName.equals("supports-input")) {
  48. } else if (tagName.equals("eat-comment")) {
  49. } else if (RIGID_PARSER) {
  50. } else {
  51. }
  52. }
  53. }

具体看一下怎么解析application标签下的四大组件的,依次解析activity,receiver,service,provider,其中可以发现,receiver被当成activity来解析了,PKM通过PackageParser类将解析后的四大组件保存到对应数据结构中,也就是存放到PackageParser的activities,receivers,providers,services对象中。关键源码如下:

  1. private boolean parseBaseApplication(Package owner, Resources res,
  2. XmlPullParser parser, AttributeSet attrs, int flags, String[] outError){
  3. //...
  4. while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
  5. && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
  6. if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
  7. continue;
  8. }
  9. String tagName = parser.getName();
  10. //activity标签
  11. if (tagName.equals("activity")) {
  12. //解析activity标签
  13. Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false,
  14. owner.baseHardwareAccelerated);
  15. //添加activity到owner.activities中
  16. owner.activities.add(a);
  17. //receiver标签
  18. } else if (tagName.equals("receiver")) {
  19. //解析receiver标签,receiver其实被当成Activity来解析了。
  20. Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true, false);
  21. if (a == null) {
  22. mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
  23. return false;
  24. }
  25. //添加activity到owner.activities中
  26. owner.receivers.add(a);
  27. //service标签
  28. } else if (tagName.equals("service")) {
  29. //解析service标签
  30. Service s = parseService(owner, res, parser, attrs, flags, outError);
  31. if (s == null) {
  32. mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
  33. return false;
  34. }
  35. //添加service到owner.services中
  36. owner.services.add(s);
  37. //provider标签
  38. } else if (tagName.equals("provider")) {
  39. Provider p = parseProvider(owner, res, parser, attrs, flags, outError);
  40. if (p == null) {
  41. mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
  42. return false;
  43. }
  44. owner.providers.add(p);
  45. } else if (tagName.equals("activity-alias")) {
  46. } else if (parser.getName().equals("meta-data")) {
  47. } else if (tagName.equals("uses-library")) {
  48. } else if (tagName.equals("uses-package")) {
  49. }
  50. }
  51. }

在PackageParser扫描完一个APK后,此时系统已经根据APK中的AndroidMainifest.xml,创建了一个Package对象,下一步是将该Package加入到系统中。此时调用scanPackageDirtyLI方法,scanPackageDirtyLI首先会对packageName为“android”的apk做单独的处理,该apk其实就是framework-res.apk,它包含了几个常见的activity

  • ChooserActivity:当startActivity有多个Acitvity符合时,系统会弹出此Acitivity,由用户选择合适的应用来处理
  • ShutDownActivity:关机前弹出的选择对话框
  • RingtonePickerAcitivity:铃声选择Activity

scanPackageDirtyLI关键代码如下:

  1. private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags,
  2. int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
  3. final File scanFile = new File(pkg.codePath);
  4. if (pkg.applicationInfo.getCodePath() == null ||
  5. pkg.applicationInfo.getResourcePath() == null) {
  6. // Bail out. The resource and code paths haven't been set.
  7. throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
  8. "Code and resource paths haven't been set correctly");
  9. }
  10. if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
  11. pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
  12. } else {
  13. // Only allow system apps to be flagged as core apps.
  14. pkg.coreApp = false;
  15. }
  16. if ((parseFlags&PackageParser.PARSE_IS_PRIVILEGED) != 0) {
  17. pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
  18. }
  19. if (mCustomResolverComponentName != null &&
  20. mCustomResolverComponentName.getPackageName().equals(pkg.packageName)) {
  21. setUpCustomResolverActivity(pkg);
  22. }
  23. if (pkg.packageName.equals("android")) {
  24. synchronized (mPackages) {
  25. if (mAndroidApplication != null) {
  26. //...
  27. }
  28. //保存该package信息
  29. mPlatformPackage = pkg;
  30. pkg.mVersionCode = mSdkVersion;
  31. //保存该package的ApplicationInfo
  32. mAndroidApplication = pkg.applicationInfo;
  33. if (!mResolverReplaced) {
  34. //mResolveActivity为ChooserActivity信息的ActivityInfo
  35. mResolveActivity.applicationInfo = mAndroidApplication;
  36. mResolveActivity.name = ResolverActivity.class.getName();
  37. mResolveActivity.packageName = mAndroidApplication.packageName;
  38. mResolveActivity.processName = "system:ui";
  39. mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
  40. mResolveActivity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NEVER;
  41. mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
  42. mResolveActivity.theme = R.style.Theme_Holo_Dialog_Alert;
  43. mResolveActivity.exported = true;
  44. mResolveActivity.enabled = true;
  45. //mResolveInfo用于存储系统解析Intent后得到的结果信息,在从PKM查询满足某个Intent的
  46. //Activity时,返回的就是ResolveInfo,再根据ResolveInfo的activityInfo的信息得到 //Activity
  47. mResolveInfo.activityInfo = mResolveActivity;
  48. mResolveInfo.priority = 0;
  49. mResolveInfo.preferredOrder = 0;
  50. mResolveInfo.match = 0;
  51. mResolveComponentName = new ComponentName(
  52. mAndroidApplication.packageName, mResolveActivity.name);
  53. }
  54. }
  55. }
  56. }

“android“该Package与系统有非常重要的作用,这里保存特殊处理保存该Package的信息,主要是为了提高运行过程中的效率,例如ChooserActivity使用的地方非常多。
接下里scanPackageDirtyLI方法会对系统其它的Package做处理,关键源码如下:

  1. //mPackages用于保存系统内的所有Package,以packageName为key
  2. if (mPackages.containsKey(pkg.packageName)
  3. || mSharedLibraries.containsKey(pkg.packageName)) {
  4. //...
  5. }
  6. if ((scanFlags & SCAN_REQUIRE_KNOWN) != 0) {
  7. if (mExpectingBetter.containsKey(pkg.packageName)) {
  8. logCriticalInfo(Log.WARN,
  9. "Relax SCAN_REQUIRE_KNOWN requirement for package " + pkg.packageName);
  10. } else {
  11. PackageSetting known = mSettings.peekPackageLPr(pkg.packageName);
  12. if (known != null) {
  13. if (DEBUG_PACKAGE_SCANNING) {
  14. Log.d(TAG, "Examining " + pkg.codePath
  15. + " and requiring known paths " + known.codePathString
  16. + " & " + known.resourcePathString);
  17. }
  18. if (!pkg.applicationInfo.getCodePath().equals(known.codePathString)
  19. || !pkg.applicationInfo.getResourcePath().equals(known.resourcePathString)) {
  20. throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
  21. "Application package " + pkg.packageName
  22. + " found at " + pkg.applicationInfo.getCodePath()
  23. + " but expected at " + known.codePathString + "; ignoring.");
  24. }
  25. }
  26. }
  27. }
  28. // Initialize package source and resource directories
  29. File destCodeFile = new File(pkg.applicationInfo.getCodePath());
  30. File destResourceFile = new File(pkg.applicationInfo.getResourcePath());
  31. SharedUserSetting suid = null;
  32. PackageSetting pkgSetting = null;
  33. if (!isSystemApp(pkg)) {
  34. // Only system apps can use these features.
  35. pkg.mOriginalPackages = null;
  36. pkg.mRealPackage = null;
  37. pkg.mAdoptPermissions = null;
  38. }
  39. //...
  40. final String pkgName = pkg.packageName;
  41. final long scanFileTime = scanFile.lastModified();
  42. final boolean forceDex = (scanFlags & SCAN_FORCE_DEX) != 0;
  43. //确定运行该package的进程名,一般用package作为进程名
  44. pkg.applicationInfo.processName = fixProcessName(
  45. pkg.applicationInfo.packageName,
  46. pkg.applicationInfo.processName,
  47. pkg.applicationInfo.uid);
  48. File dataPath;
  49. if (mPlatformPackage == pkg) {
  50. // The system package is special.
  51. dataPath = new File(Environment.getDataDirectory(), "system");
  52. pkg.applicationInfo.dataDir = dataPath.getPath();
  53. } else {
  54. // This is a normal package, need to make its data directory.
  55. //该函数返回data/data/packageName
  56. dataPath = Environment.getDataUserPackageDirectory(pkg.volumeUuid,
  57. UserHandle.USER_OWNER, pkg.packageName);
  58. boolean uidError = false;
  59. if (dataPath.exists()) {
  60. //..
  61. } else {
  62. if (DEBUG_PACKAGE_SCANNING) {
  63. if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
  64. Log.v(TAG, "Want this data dir: " + dataPath);
  65. }
  66. //该方法调用installer发送install命令,其实就是在/data/data/目录下建立packageName目录
  67. //然后为系统所有的user安装此apk
  68. int ret = createDataDirsLI(pkg.volumeUuid, pkgName, pkg.applicationInfo.uid,
  69. pkg.applicationInfo.seinfo);
  70. //安装错误
  71. if (ret < 0) {
  72. // Error from installer
  73. throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
  74. "Unable to create data dirs [errorCode=" + ret + "]");
  75. }
  76. }
  77. pkgSetting.uidError = uidError;
  78. }
  79. final String path = scanFile.getPath();
  80. final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, pkgSetting);
  81. //在/data/data/pageName/lib下建立和CPU类型对应的目录,例如ARM平台的事arm/,MIP平台的事mips/
  82. if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
  83. derivePackageAbi(pkg, scanFile, cpuAbiOverride, true /* extract libs */);
  84. //系统package的native库统一放在/system/lib下,
  85. //所以系统不会提取系统package目录apk包中的native库
  86. if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp() &&
  87. pkg.applicationInfo.primaryCpuAbi == null) {
  88. setBundledAppAbisAndRoots(pkg, pkgSetting);
  89. setNativeLibraryPaths(pkg);
  90. }
  91. } else {
  92. if ((scanFlags & SCAN_MOVE) != 0) {
  93. //
  94. setNativeLibraryPaths(pkg);
  95. }
  96. //...
  97. if ((scanFlags & SCAN_NO_DEX) == 0) {
  98. //对该APK做dex优化
  99. int result = mPackageDexOptimizer.performDexOpt(pkg, null /* instruction sets */,
  100. forceDex, (scanFlags & SCAN_DEFER_DEX) != 0, false /* inclDependencies */);
  101. //如果该apk已经存在,要先杀掉该APK的进程
  102. if ((scanFlags & SCAN_REPLACING) != 0) {
  103. killApplication(pkg.applicationInfo.packageName,
  104. pkg.applicationInfo.uid, "replace pkg");
  105. }
  106. //在此之前,四大组件信息都是Package对象的私有的,在这里把它们注册到PKM内部的财产管理对象中。
  107. //这样,PKMS就可对外提供统一的组件信息。
  108. synchronized (mPackages) {
  109. ...
  110. //注册该Package中的provider到PKM的mProviders上
  111. int N = pkg.providers.size();
  112. StringBuilder r = null;
  113. int i;
  114. for (i=0; i<N; i++) {
  115. PackageParser.Provider p = pkg.providers.get(i);
  116. p.info.processName = fixProcessName(pkg.applicationInfo.processName,
  117. p.info.processName, pkg.applicationInfo.uid);
  118. mProviders.addProvider(p);
  119. p.syncable = p.info.isSyncable;
  120. if (p.info.authority != null) {
  121. //...
  122. }
  123. //注册该Package中的service到PKM的mServices上
  124. N = pkg.services.size();
  125. r = null;
  126. for (i=0; i<N; i++) {
  127. PackageParser.Service s = pkg.services.get(i);
  128. s.info.processName = fixProcessName(pkg.applicationInfo.processName,
  129. s.info.processName, pkg.applicationInfo.uid);
  130. mServices.addService(s);
  131. if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
  132. if (r == null) {
  133. r = new StringBuilder(256);
  134. } else {
  135. r.append(' ');
  136. }
  137. r.append(s.info.name);
  138. }
  139. }
  140. if (r != null) {
  141. if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Services: " + r);
  142. }
  143. //注册该Package中的receiver到PKM的mReceivers上
  144. N = pkg.receivers.size();
  145. r = null;
  146. for (i=0; i<N; i++) {
  147. PackageParser.Activity a = pkg.receivers.get(i);
  148. a.info.processName = fixProcessName(pkg.applicationInfo.processName,
  149. a.info.processName, pkg.applicationInfo.uid);
  150. mReceivers.addActivity(a, "receiver");
  151. if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
  152. if (r == null) {
  153. r = new StringBuilder(256);
  154. } else {
  155. r.append(' ');
  156. }
  157. r.append(a.info.name);
  158. }
  159. }
  160. if (r != null) {
  161. if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Receivers: " + r);
  162. }
  163. //注册该Package中的activity到PKM的mActivities上
  164. N = pkg.activities.size();
  165. r = null;
  166. for (i=0; i<N; i++) {
  167. PackageParser.Activity a = pkg.activities.get(i);
  168. a.info.processName = fixProcessName(pkg.applicationInfo.processName,
  169. a.info.processName, pkg.applicationInfo.uid);
  170. mActivities.addActivity(a, "activity");
  171. if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
  172. if (r == null) {
  173. r = new StringBuilder(256);
  174. } else {
  175. r.append(' ');
  176. }
  177. r.append(a.info.name);
  178. }
  179. }
  180. if (r != null) {
  181. if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Activities: " + r);
  182. }
  183. //...
  184. return pkg;
  185. }

5.3扫描非系统apk

在PackageManagerService构造函数扫描完系统apk后,接下来就是扫描非系统apk,这些apk在/data/app或者/data/app-private中。如下图:

下面是关键源码,scanDirLI已经在前面分析过了。跟系统apk的调用过程差不多。

  1. if (!mOnlyCore) {
  2. EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
  3. SystemClock.uptimeMillis());
  4. scanDirLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
  5. scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,
  6. scanFlags | SCAN_REQUIRE_KNOWN, 0);
  7. }

5.4扫描结果保存到文件中

在PackageManagerService构造函数收尾阶段,PMS将前面收集的信息再整理一次,将已安装的apk信息写到package.xml、pacakage.list和package-stopped.xml中

  1. //整理更新Permisssion的信息
  2. updatePermissionsLPw(null, null, updateFlags);
  3. //...
  4. //将信息写到package.xml,pacakage.list和package-stopped.xml中
  5. mSettings.writeLPr();
  6. EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
  7. SystemClock.uptimeMillis());
  8. mRequiredVerifierPackage = getRequiredVerifierLPr();
  9. mRequiredInstallerPackage = getRequiredInstallerLPr();
  10. } // synchronized (mPackages)
  11. } // synchronized (mInstallLock)
  12. //gc
  13. Runtime.getRuntime().gc();
  • packages.xml:系统对程序安装,卸载和更新等操作时会更新该文件,PMS扫描完目标文件夹后创建该文件,保存了Package相关的信息。
  • packages.list:保存着系统中所有的非系统自带的APK信息,程序安装,卸载和更新会更新该文件。
  • packages-stoped.xml:保存系统中被用户强制停止的Package的信息。

5.4扫描系统和非系统apk总结

PKM在这个过程中工作任务非常繁重,要创建很多的对象,所以它是一个耗时耗内存的操作,从流程来看,PKM在这个过程中无非是扫描XML或者APK文件,但是其中涉及的数据结构及它们的关系较为复杂。

android PakageManagerService启动流程分析的更多相关文章

  1. Cocos2d-x3.3RC0的Android编译Activity启动流程分析

    本文将从引擎源代码Jni分析Cocos2d-x3.3RC0的Android Activity的启动流程,以下是具体分析. 1.引擎源代码Jni.部分Java层和C++层代码分析 watermark/2 ...

  2. Android5 Zygote 与 SystemServer 启动流程分析

    Android5 Zygote 与 SystemServer 启动流程分析 Android5 Zygote 与 SystemServer 启动流程分析 前言 zygote 进程 解析 zygoterc ...

  3. android开机启动流程说明

    android开机启动流程说明 第一步:启动linux 1.Bootloader 2.Kernel 第二步android系统启动:入口为init.rc(system\core\rootdir) 1./ ...

  4. u-boot启动流程分析(2)_板级(board)部分

    转自:http://www.wowotech.net/u-boot/boot_flow_2.html 目录: 1. 前言 2. Generic Board 3. _main 4. global dat ...

  5. u-boot启动流程分析(1)_平台相关部分

    转自:http://www.wowotech.net/u-boot/boot_flow_1.html 1. 前言 本文将结合u-boot的“board—>machine—>arch—> ...

  6. Android WIFI 启动流程(TIP^^)

    前几天因为解决一堆Bug,没时间写.我不会每天都写,就是为了存档一些资料. 内容来源:工作中接触到的+高手博客+文档(Books)=自己理解 仅限参考^^ 此博客是上一个<<Android ...

  7. Netty 拆包粘包和服务启动流程分析

    Netty 拆包粘包和服务启动流程分析 通过本章学习,笔者希望你能掌握EventLoopGroup的工作流程,ServerBootstrap的启动流程,ChannelPipeline是如何操作管理Ch ...

  8. Uboot启动流程分析(转载)

    最近一段时间一直在做uboot移植相关的工作,需要将uboot-2016-7移植到单位设计的ARMv7的处理器上.正好元旦放假三天闲来无事,有段完整的时间来整理下最近的工作成果.之前在学习uboot时 ...

  9. Storm集群启动流程分析

    Storm集群启动流程分析 程序员 1.客户端运行storm nimbus时,会调用storm的python脚本,该脚本中为每个命令编写了一个方法,每个方法都可以生成一条相应的Java命令. 命令格式 ...

随机推荐

  1. 谷歌发布 TensorFlow Lite [官方网站,文档]

    机器学习社区:http://tensorflow123.com/ 简介 TensorFlow Lite TensorFlow Lite 是 TensorFlow 针对移动和嵌入式设备的轻量级解决方案. ...

  2. Windows 下 Ionic 开发环境搭建

    Ionic 介绍 首先,Ionic 是什么. Ionic 是一款基于 Cordova 及 Angular 开发 Hybrid/Web APP 的前端框架,类似的其他框架有:Intel XDK等. 简单 ...

  3. 构建纯TypeScript应用

    构建纯TypeScript应用 现在只有命令行应用的例子. 前言 现在,应用开发的趋势是命令行接口应用和Web应用. node.js 和 typescript的崛起所以,这里讨论如何创建纯的TypeS ...

  4. [ SSH框架 ] Hibernate框架学习之二

    一.Hibernate持久化类的编写规范 1.什么是持久化类 Hibernate是持久层的ORM影射框架,专注于数据的持久化工作.所谓持久化,就是将内存中的数据永久存储到关系型数据库中.那么知道了什么 ...

  5. 安装redis 执行make命令时报错解决方法

    一.未安装GCC 解决方法:执行yum install gcc-c++命令安装GCC,完成后再次执行make命令 yum install gcc-c++ Linux无法连接网络 http://www. ...

  6. textarea不能使用maxlength

    知道文本框有个maxlength属性,有次开发项目中使用了textarea标签,没去看文档,直接加了maxlength属性,且有效果没有报错,喜滋滋的用了,结果没两天就测试出了bug 问题描述:文本域 ...

  7. Web API对application/json内容类型的CORS支持

    假设有一简单架构分为前后两部分,其一是Angular构成的前端页面站点,另一个则是通过ASP.NET Web API搭建的后端服务站点.两个站点因为分别布署,所有会有CORS(Cross-Origin ...

  8. PHP 5 Filesystem 函数

    PHP Filesystem 简介 Filesystem 函数允许您访问和操作文件系统. 安装 Filesystem 函数是 PHP 核心的组成部分.无需安装即可使用这些函数. Runtime 配置 ...

  9. 让你的代码量减少3倍!使用kotlin开发Android(一)

    让你的代码量减少3倍!使用kotlin开发Android(一) 创建Kotlin工程 本文同步自博主的私人博客:wing的地方酒馆 写在前面 使用kotlin开发android已经两周多了.得到的好处 ...

  10. Dynamics CRM2013 导入解决方案(快速视图窗体)SystemForm With Id Does Not Exist的解决方法

    在CRM2013的环境下导入解决方案报错,具体报错截图如下 根据id去数据库中查找这个id的systemform,确认是存在的,而且通过第二条记录我们也可以看到这个systemform属于哪个实体,我 ...