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();