第三章:PMS(PackageManagerService )
简述
该服务主要管理设备上的应用程序,在开机启动SystemServer系统服务之后,PMS会扫描特定目录下的apk后缀名文件,将应用安装到系统中。这个流程主要的工作:
- 将存放在磁盘上的静态应用程序文件进行解析,并将相关信息注册到系统中,具体就是解析应用的配置清单文件manifest.xml,将we你文件中配置的组件(Activity,Service,BroadcastRecevier,ContentProvider),权限等信息注册到PackageManagerService中。
1. PMS服务启动初始化
1.1 在framework层启动
frameworks/base/services/java/com/android/server/SystemServer.java .startBootstrapServices()方法中
// Start the package manager.
...
mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
mFirstBoot = mPackageManagerService.isFirstBoot();
mPackageManager = mSystemContext.getPackageManager();
...
1.2 创建PMS并注册到SystemServer系统服务
class PackageManagerService extends IPackageManager.Stub {
public static PackageManagerService main(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
// Self-check for initial settings.
PackageManagerServiceCompilerMapping.checkProperties();
PackageManagerService m = new PackageManagerService(context, installer,
factoryTest, onlyCore);
m.enableSystemUserPackages();
ServiceManager.addService("package", m);
return m;
}
}
创建PMS服务完成,并注册到ServiceManager服务管理者中方便应用(Client端)调用该服务;
1.3 初始化一系列工作
构造函数
class PackageManagerService extends IPackageManager.Stub {
......
public PackageManagerService(Context context, boolean factoryTest) {
......
synchronized (mInstallLock) {
synchronized (mPackages) {
......
//1. 用于创建data/system目录和一下xml文件,并配置相应的权限
mSettings = new Settings(mPackages);
//添加特殊用户名称和UID并关联 例如:在app清单文件中 android:sharedUserId="android.uid.system" 表示该应用是系统进程, 相关的权限会和普通应用不一样
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
......
//2.重点:这里主要是对apk中的dex进行优化,针对dvm和art不同虚拟机进行优化生成odex文件,普通应用放在/data/app/包名/oat目录下,系统应用已经在系统编译的时候进行了优化;
mInstaller = installer;
mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,
"*dexopt*");
mDexManager = new DexManager(this, mPackageDexOptimizer, installer, mInstallLock);
......
//3.开始扫描本地apk进行解析安装
File dataDir = Environment.getDataDirectory();
mAppInstallDir = new File(dataDir, "app");
mAppLib32InstallDir = new File(dataDir, "app-lib");
mAsecInternalPath = new File(dataDir, "app-asec").getPath();
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
......
File frameworkDir = new File(Environment.getRootDirectory(), "framework");
mCacheDir = preparePackageParserCache(mIsUpgrade);
// Find base frameworks (resource packages without code).
scanDirTracedLI(frameworkDir, mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED,
scanFlags | SCAN_NO_DEX, 0);
......
// Collected privileged system packages.
final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
// Collected privileged system packages.
final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
scanDirTracedLI(privilegedAppDir, mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);
......
// Collect ordinary system packages.
final File systemAppDir = new File(Environment.getRootDirectory(), "app");
......
// Collect all vendor packages.
File vendorAppDir = new File("/vendor/app");
......
}
}
}
}
可以看到,每次系统开机都会去扫码下面目录中的apk;在构造函数中,PackageManagerService(PMS)会扫描特定目录下的APK文件,然后调用scanDirTracedLI函数进行相关的加载工作,这些目录包括:
/system/framework
/system/app
/vendor/app
/data/app
/data/app-private
PackageManagerService 在启动时会扫描所有APK文件和Jar包,然后把它们的信息读取出来,保存在内存中,这样系统运行时就能迅速找到各种应用和组件的信息。扫描过程中如果遇到没有优化过的文件,还要执行转化工作。(Android 5.0是odex格式,Android 5.0之后是oat格式,保存在data/app/包名/oat目录下)。启动后,PackageManagerService将提供安装包的信息查询服务以及应用的安装和卸载服务。
1.3.1 认识Settings
//-----------------第一部分:构造函数
Settings(File dataDir, Object lock) {
mLock = lock;
mRuntimePermissionsPersistence = new RuntimePermissionPersistence(mLock);
//初始化mSystemDir 并创建data/system目录
mSystemDir = new File(dataDir, "system");
mSystemDir.mkdirs();
//设置/data/system目录的权限
FileUtils.setPermissions(mSystemDir.toString(),
FileUtils.S_IRWXU|FileUtils.S_IRWXG
|FileUtils.S_IROTH|FileUtils.S_IXOTH,
-1, -1);
//初始化文件对象,用于记录apk包的相关信息
mSettingsFilename = new File(mSystemDir, "packages.xml");
mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");
mPackageListFilename = new File(mSystemDir, "packages.list");
//设置packages.list文件的权限
FileUtils.setPermissions(mPackageListFilename, 0640, SYSTEM_UID, PACKAGE_INFO_GID);
final File kernelDir = new File("/config/sdcardfs");
mKernelMappingFilename = kernelDir.exists() ? kernelDir : null;
// Deprecated: Needed for migration
mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
}
//------------------第二部分
SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
// 获取SharedUserSetting对象,其中mSharedUsers是ArrayMap<String, SharedUserSetting> 来存储的
SharedUserSetting s = mSharedUsers.get(name);
if (s != null) {
if (s.userId == uid) {
return s;
}
PackageManagerService.reportSettingsProblem(Log.ERROR,
"Adding duplicate shared user, keeping first: " + name);
return null;
}
//如果在ArrayMap中没有找到对应的SharedUserSetting
s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
s.userId = uid;
if (addUserIdLPw(uid, s, name)) {
// 将 name和SharedUserSetting对象保存到mShareUsers的一个ArrayMap中
mSharedUsers.put(name, s);
return s;
}
return null;
}
文件理解:
packages.xml文件用来记录所有安装了的apk的相关信息;
packages_backup.xml文件则是备份文件;
packages-stopped.xml记录被用户强制停止的应用的package信息
packages-stopped-backup.xml是packages-stopped.xml的备份文件;
packages.list文件记录非系统自带的apk数据信息
Settings.addSharedUserLPw函数功能:
SharedUserSetting存储uid对应名称以及包的标记,并通过ArrayMap<String, SharedUserSetting> 集合类存储归类;
举例:
在SystemUI的AndroidManifest.xml里面,如下所示
<manifestxmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.systemui"
coreApp="true"
android:sharedUserId="android.uid.system"
android:process="system">
在xml中,声明了一个名为android:sharedUserId的属性,其值为"android.uid.system"。shareUserId看起来和UID有关,确实如此,它有两个作用:
- 两个或者多个声明了同一种shareUserIds的APK可以共享彼此的数据,并且可以运行在同一个进程中。
- 通过声明特定的shareUserId,该APK所在进程将被赋予指定的UID。例如,本例中的SystemUI声明了system的uid,运行SystemUI进程就可共享system用户所对应的权限(实际上就是将该进程的uid设置为system的uid)
除了在AndroidManifest.xml中声明shareUserId外,APK在编译时还必须使用对应的证书进行签名。
看图理解:

1.3.2 dex优化
dex的优化最终还是调用Installer.java
)这个类执行,PackageDexOptimizer对其进行了管理封装,想要了解Installer调用底层服务可以到第一章学习;
@GuardedBy("mInstallLock")
private int dexOptPath(PackageParser.Package pkg, String path, String isa,
String compilerFilter, boolean profileUpdated, String sharedLibrariesPath,
int dexoptFlags, int uid, CompilerStats.PackageStats packageStats) {
......
//* 调用Installer.java进行AIDL进程通信通知root底层InstalldNativeService安装服务执行dex优化
mInstaller.dexopt(path, uid, pkg.packageName, isa, dexoptNeeded, oatDir, dexoptFlags,
compilerFilter, pkg.volumeUuid, sharedLibrariesPath, pkg.applicationInfo.seInfo);
......
}
1.3.3 解析APK文件manifest文件信息存储到PackageParser对象
scanDirTracedLI函数扫描对应的目录文件,如果是apk文件,就找到apk文件中的manifest文件,并解析信息传递到PackageParser.ParseResult中
public class PackageParser {
......
private Package parseBaseApkCommon(Package pkg, Set<String> acceptedTags, Resources res,
XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException,
IOException {
......
int type;
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifest);
......
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if (acceptedTags != null && !acceptedTags.contains(tagName)) {
Slog.w(TAG, "Skipping unsupported element under <manifest>: "
+ tagName + " at " + mArchiveSourcePath + " "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
if (tagName.equals(TAG_APPLICATION)) {
......
} else if (tagName.equals(TAG_OVERLAY)) {
......
} else if (tagName.equals(TAG_KEY_SETS)) {
......
} else if (tagName.equals(TAG_PERMISSION_GROUP)) {
......
} else if (tagName.equals(TAG_PERMISSION)) {
......
} else if (tagName.equals(TAG_PERMISSION_TREE)) {
......
} else if (tagName.equals(TAG_USES_PERMISSION)) {
......
} else if (tagName.equals(TAG_USES_PERMISSION_SDK_M)
|| tagName.equals(TAG_USES_PERMISSION_SDK_23)) {
......
} else if (tagName.equals(TAG_USES_CONFIGURATION)) {
......
return pkg;
}
......
}
这部分代码就是对AndroidManifest.xml文件中的application标签进行解析了,我们常用到的标签就有activity、service、receiver和provider,这里解析完成后,一层层返回,调用另一个版本的scanPackageLI函数把来解析后得到的应用程序信息保存下来。
2. 应用分类
Android 中应用可以简单地分成两大类:"系统应用"和"普通应用"
- 1、系统应用是指位于/system/app 或者 /System/priv-app 目录下的应用。priv-app目录是从Android 4.4开始出现的目录,它存放的是一些系统底层的应用,如Settin、SystemUI等。/system/app中存放的是一些系统级别的应用,如:Phone、Contact等。在PackageManagerService 中,所谓的system 应用包括这两个目录下的应用,而所谓的private应用就是指 /priv-app 目录下的应用。
- 2、普通应用就是用户安装的应用,位于目录/data/app下。普通应用也可以安装在SD上,但是系统应用不可以。
3. APk安装流程图解
