PKMS和AMS一样是Android系统的核心服务,它主要负责系统中Package的管理,应用程序的安装、卸载、信息查询等工作。PKMS也是由system_server调用PKMS的main函数启动的:



// Start the package manager.
        Slog.i(TAG, "Package Manager");
        mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
                mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);



  PKMS的main函数代码如下:



1     public static final PackageManagerService main(Context context, Installer installer,
2             boolean factoryTest, boolean onlyCore) {
3         //调用PKMS的构造函数,factoryTest和onlyCore都是false
4         PackageManagerService m = new PackageManagerService(context, installer,
5                 factoryTest, onlyCore);
6         //向ServiceManager中注册PKMS
7         ServiceManager.addService("package", m);
8         return m;
9     }



  main函数很简单,但是执行时间却很长,主要原因是PKMS在其构造函数中做了很多“体力活”,这也是Android启动速度慢的主要原因之一。其构造函数的主要功能是:
  扫描Android系统中几个目标文件夹中的APK,从而建立合适的数据结构以管理诸如Package信息、四大组件信息、权限信息等各种信息。PKMS通过解析APK包中的AndroidManifest.xml文件,并根据其中声明的Activity标签来创建与此对应的对象并加以保管。

  PKMS的工作流程相对简单,复杂的是其中用于保存各种信息的数据结构和它们之间的关系,以及影响最终结果的策略控制(如onlycore变量,用于判断是否只扫描系统目录)。

 一:PKMS构造函数扫描文件夹之前的准备工作



1         //mSdkVersion即为系统编译的SDK版本
 2         if (mSdkVersion <= 0) {
 3             Slog.w(TAG, "**** ro.build.version.sdk not set!");
 4         }
 5         
 6         mContext = context;
 7         mFactoryTest = factoryTest;//是否运行在工厂模式下
 8         mOnlyCore = onlyCore;//false为普通模式
 9         //如果此系统是eng版,则扫描Package之后,不对package做dex优化
10         mLazyDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
11         //用于存储与显示屏相关的一些属性,例如屏幕的宽、高、分辨率信息
12         mMetrics = new DisplayMetrics();
13         mSettings = new Settings(context);
14         mSettings.addSharedUserLPw("android.uid.system", //字符串
15                 Process.SYSTEM_UID,//系统进程使用的用户id,为1000
16                 ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED//标志着系统Package
17         );
18         mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,//1001
19                 ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);
20         mSettings.addSharedUserLPw("android.uid.log", LOG_UID,//1007
21                 ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);
22         mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,//1025
23                 ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);
24         mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,//2000
25                 ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);
26         mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,//2000
27                 ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);



  刚进入构造函数,就会遇到第一个较为复杂的数据结构Setting及它的addSharedUserLPw函数,来看看addSharedUserLPw函数的代码:



1     SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags) {
 2         SharedUserSetting s = mSharedUsers.get(name);//mSharedUsers是一个HashMap,key为字符串,值为SharedUserSetting对象
 3         if (s != null) {
 4             if (s.userId == uid) {
 5                 return s;
 6             }
 7             PackageManagerService.reportSettingsProblem(Log.ERROR,
 8                     "Adding duplicate shared user, keeping first: " + name);
 9             return null;
10         }
11         //把key为name,值为uid的SharedUserSetting对象添加到mSharedUsers中保存
12         s = new SharedUserSetting(name, pkgFlags);
13         s.userId = uid;
14         if (addUserIdLPw(uid, s, name)) {
15             mSharedUsers.put(name, s);
16             return s;
17         }
18         return null;
19     }



  这一步有什么用呢?我们都看过system应用的Manifest文件标签头,举个例子:



1 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2         package="com.android.settings"
3         coreApp="true"
4         android:sharedUserId="android.uid.system">



  在xml中,声明了一个名为android:sharedUserId的属性,其值为“android.uid.system”,它有两个作用:

  1.两个或多个声明了同一种android:sharedUserId的APK可以共享彼此的数据,并且可运行在同一进程中。

  2.通过声明特定的android:sharedUserId,该APK所在进程将被赋予指定的UID,例如本例中setting声明了system的uid,运行setting的进程就可享有system用户所在对应的权限了。

  接下来就是xml文件解析的环节啦



1         String separateProcesses = SystemProperties.get("debug.separate_processes");
 2         if (separateProcesses != null && separateProcesses.length() > 0) {
 3             ...
 4         } else {
 5             mDefParseFlags = 0;
 6             mSeparateProcesses = null;
 7         }
 8 
 9         mInstaller = installer;
10 
11         getDefaultDisplayMetrics(context, mMetrics);
12 
13         SystemConfig systemConfig = SystemConfig.getInstance();
14         mGlobalGids = systemConfig.getGlobalGids();
15         mSystemPermissions = systemConfig.getSystemPermissions();
16         mAvailableFeatures = systemConfig.getAvailableFeatures();
17 
18         synchronized (mInstallLock) {
19         // writer
20         synchronized (mPackages) {
21             //创建一个ThreadHander,实际就是创建了一个带消息循环处理的线程,该线程的工作就是:程序的安装和卸载等
22             mHandlerThread = new ServiceThread(TAG,
23                     Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
24             mHandlerThread.start();
25             mHandler = new PackageHandler(mHandlerThread.getLooper());
26             Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
27             //创建file对象指向用户的package安装包目录
28             File dataDir = Environment.getDataDirectory();
29             mAppDataDir = new File(dataDir, "data");
30             mAppInstallDir = new File(dataDir, "app");
31             mAppLib32InstallDir = new File(dataDir, "app-lib");
32             mAsecInternalPath = new File(dataDir, "app-asec").getPath();
33             mUserAppDataDir = new File(dataDir, "user");
34             mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
35             //创建一个UserManager,与多用户相关,不同的用户的Package目录是不一样的
36             sUserManager = new UserManagerService(context, this,
37                     mInstallLock, mPackages);
38 
39             // Propagate permission configuration in to package manager.读取权限
40             ArrayMap<String, SystemConfig.PermissionEntry> permConfig
41                     = systemConfig.getPermissions();
42             for (int i=0; i<permConfig.size(); i++) {
43                 SystemConfig.PermissionEntry perm = permConfig.valueAt(i);
44                 BasePermission bp = mSettings.mPermissions.get(perm.name);
45                 if (bp == null) {
46                     bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN);
47                     mSettings.mPermissions.put(perm.name, bp);
48                 }
49                 if (perm.gids != null) {
50                     bp.gids = appendInts(bp.gids, perm.gids);
51                 }
52             }
53 
54             ...
55 
56             mRestoredSettings = mSettings.readLPw(this, sUserManager.getUsers(false),
57                     mSdkVersion, mOnlyCore);
58             ...
59 
60             long startTime = SystemClock.uptimeMillis();



  总结就是PKMS的扫描准备工作是,扫描并解析XML 文件,并将其中的信息保存到特定的数据结构中

二:构造函数之扫描系统Package

  PKMS的构造函数第二阶段工作就是扫描系统中APK,由于需要逐个扫描文件,因此手机上装的程序越多,PKMS的工作量就越大,启动就越慢。

  1.系统库的dex优化



1 // The list of "shared libraries" we have at this point is优化需要优化的jar包
2 if (dexoptRequired == DexFile.DEXOPT_NEEDED) {
3     mInstaller.dexopt(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet);
4 } else {
5     mInstaller.patchoat(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet);
6 }



1             // Gross hack for now: we know this file doesn't contain any
2             // code, so don't dexopt it to avoid the resulting log spew.framework-res.apk定义了系统常用的资源,还有几个重要的Activity,如长按Power键后弹出的对话框
3             alreadyDexOpted.add(frameworkDir.getPath() + "/framework-res.apk");
4 
5             // Gross hack for now: we know this file is only part of
6             // the boot class path for art, so don't dexopt it to
7             // avoid the resulting log spew.core-libart.jar跟ART核心有关
8             alreadyDexOpted.add(frameworkDir.getPath() + "/core-libart.jar");



  2.扫描系统Package



1             // Collected privileged system packages.
 2             final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
 3             scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM
 4                     | PackageParser.PARSE_IS_SYSTEM_DIR
 5                     | PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);
 6 
 7             // Collect ordinary system packages.
 8             final File systemAppDir = new File(Environment.getRootDirectory(), "app");
 9             scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM
10                     | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
11 
12             // Collect all vendor packages.
13             File vendorAppDir = new File("/vendor/app");
14             try {
15                 vendorAppDir = vendorAppDir.getCanonicalFile();
16             } catch (IOException e) {
17                 // failed to look up canonical path, continue with original one
18             }
19             scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
20                     | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);



  我们可以看到PKMS 将扫描以下几个目录:
    priv-app:该目录下都是默认的系统应用,settings,contact等

    vendor/app:该目录下都是厂商提供的APK文件

    app:系统app

  这一步最重要的函数是scanDirLI(),该函数会调用PackageParser对APK文件的AndroidManifest.xml文件进行解析。PackageParser完成了从物理文件到对应数据结构的转换。我们来看看该数据结构的组成:



Package
-------
+applicationInfo:ApplicationInfo
+permission:    ArrayList<Permission>
+permissionGroups:    ArrayList<PermissionGroup>
+activities:    ArrayList<Activity>
+receivers:    ArrayList<BroadcastReceiver>
+providers:    ArrayList<Provider>
+requestedPermissions:    ArrayList<String>
+services:    ArrayList<Service>
+instrumentation:    ArrayList<Instrumentation>
+usesLibraries:    ArrayList<String>



  我们可以看到Android的四大组件,Permission,应用的Lib,applicationinfo等信息都已解析出来了。
  在PackageParser扫描完一个APK之后,系统根据该APK的manifest文件创建了一个完整的Package对象,下一步就是将该Package加入到系统中,此时调用的函数是另一个scanPackageLI,主要代码如下:



1         if (pkg.packageName.equals("android")) {
 2             synchronized (mPackages) {
 3                 if (mAndroidApplication != null) {
 4                     ...
 5                 }
 6 
 7                 // Set up information for our fall-back user intent resolution activity.
 8                 mPlatformPackage = pkg;
 9                 pkg.mVersionCode = mSdkVersion;
10                 mAndroidApplication = pkg.applicationInfo;
11 
12                 if (!mResolverReplaced) {
13                     mResolveActivity.applicationInfo = mAndroidApplication;
14                     mResolveActivity.name = ResolverActivity.class.getName();
15                     mResolveActivity.packageName = mAndroidApplication.packageName;
16                     mResolveActivity.processName = "system:ui";
17                     mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
18                     mResolveActivity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NEVER;
19                     mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
20                     mResolveActivity.theme = R.style.Theme_Holo_Dialog_Alert;
21                     mResolveActivity.exported = true;
22                     mResolveActivity.enabled = true;
23                     mResolveInfo.activityInfo = mResolveActivity;
24                     mResolveInfo.priority = 0;
25                     mResolveInfo.preferredOrder = 0;
26                     mResolveInfo.match = 0;
27                     mResolveComponentName = new ComponentName(
28                             mAndroidApplication.packageName, mResolveActivity.name);
29                 }
30             }
31         }



  这边对Packagename为“android”的Package进行了单独的处理。和该Package对应的APK就是framework-res.apk,这个apk除了系统资源之外还负责几个Activity:



1 ChooserActivity:当多个Activity符合某个Intent时,系统弹出此Activity,由用户选择合适的应用来处理。
2 RingtonePickerActivity:铃声选择
3 ShutdownActivity:关机前弹出的选择对话框



  接下来就是对所有系统应用的处理,该段代码过于庞大,就不贴了,主要是对Package的私有财进行公有化改造。

三、扫描非系统Package

  和系统应用扫描基本一致,不详述,主要是扫描/data/app 和 /data/app-private的目录Package。

四、扫尾工作

  扫尾工作主要是将扫描信息再集中整理一次,比如将有些信息保存到文件中:



1             mSettings.mInternalSdkPlatform = mSdkVersion;
 2             //汇总并更新和Permission相关的信息
 3             updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL
 4                     | (regrantPermissions
 5                             ? (UPDATE_PERMISSIONS_REPLACE_PKG|UPDATE_PERMISSIONS_REPLACE_ALL)
 6                             : 0));
 7 
 8             // 将信息写到Package.xml/ package.list 及 package-stopped.xml 文件中
 9             mSettings.writeLPr();
10 
11             EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
12                     SystemClock.uptimeMillis());
13 
14             mRequiredVerifierPackage = getRequiredVerifierLPr();