一、惯例BB
新的一年又到了,2018也要加油啊~距离写上一篇文章也有一个月了,今天我们就来看看PackageManagerService(以下简称PMS)。
PMS和AMS、WMS一样,也是一个系统服务,他的主要作用就是解析APK信息,并保存下来。我们平时写在Manifest里的信息是如何被解析的呢?没错都是他干的。还记得我在前面一篇文章《 VirtualAPK插件化方案原理探索》里留下了一个坑:VirtualAPK初始化时有这么一段代码:
//这里解析APK,并保存到LoadedPlugin对象中
LoadedPlugin plugin = LoadedPlugin.create(this, this.mContext, apk);
LoadedPlugin 这个类就是负责解析并保存APK信息的,因为主题原因当时我就一笔带过并没有深入详谈,今天就让他做一回主角。
其实这也无可厚非,插件化的基本思想就是需要利用插件APK来搞事情。所以这里究竟是如何实现的呢,让我们带着疑问往下看。
二、PackageManagerService
当一个Android手机开机时,系统会将所有已安装过的应用安装一遍,然后显示在桌面上。所以,PMS在开机时就会启动,首先我们来看看他的构造方法
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
//省略巨量代码
//找到framework所在
File frameworkDir = new File(Environment.getRootDirectory(), "framework");
alreadyDexOpted.add(frameworkDir.getPath() + "/framework-res.apk");
alreadyDexOpted.add(frameworkDir.getPath() + "/core-libart.jar");
//省略部分解析代码
// Find base frameworks (resource packages without code):扫描framework磁盘
scanDirLI(frameworkDir, 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");
scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);
// Collect ordinary system packages.扫描系统app磁盘
final File systemAppDir = new File(Environment.getRootDirectory(), "app");
scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
//省略其他扫描及无关代码...
if (!mOnlyCore) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
SystemClock.uptimeMillis());
//扫描已下载app磁盘
scanDirLI(mAppInstallDir, 0, scanFlags, 0);
scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,
scanFlags, 0);
//.....
Runtime.getRuntime().gc();
}
代码注释已经很清楚了,首先扫描framework及核心代码,再扫描已安装应用目录,我们就来看看sacnDirLI这个方法
private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {
final File[] files = dir.listFiles();
//....
//循环扫描每一个应用
for (File file : files) {
final boolean isPackage = (isApkFile(file) || file.isDirectory())
&& !PackageInstallerService.isStageName(file.getName());
if (!isPackage) {
// Ignore entries which are not packages
continue;
}
try {
//扫描
scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
scanFlags, currentTime, null);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());
//...
}
}
}
调用scanPackageLI扫描每个文件,直接看代码
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
long currentTime, UserHandle user) throws PackageManagerException {
//利用PackageParser解析APK并返回Package对象
final PackageParser.Package pkg;
try {
pkg = pp.parsePackage(scanFile, parseFlags);
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
}
PackageSetting ps = null;
PackageSetting updatedPkg;
//省略了巨量关于保存及验证签名、applicationInfo等代码
// Note that we invoke the following method only if we are about to unpack an application
//保存了APK信息
PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanFlags
| SCAN_UPDATE_SIGNATURE, currentTime, user);
//.....
return scannedPkg;
}
就如同注释所说:
1、利用PackageParser解析APK并返回Package对象,这个package是PackageParser的内部类,保存了APK信息,具体如何解析,我们下文再讲。
2、将解析出来的APK信息保存起来,我们就先来看看这个scanPackageLI方法。由于这个方法代码太多了,我就放上我们关心的部分:
//保存Service
for (i=0; i<N; i++) {
PackageParser.Service s = pkg.services.get(i);
s.info.processName = fixProcessName(pkg.applicationInfo.processName,
s.info.processName, pkg.applicationInfo.uid);
mServices.addService(s);
if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
if (r == null) {
r = new StringBuilder(256);
} else {
r.append(' ');
}
r.append(s.info.name);
}
}
if (r != null) {
if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Services: " + r);
}
N = pkg.receivers.size();
r = null;
//保存receiver
for (i=0; i<N; i++) {
PackageParser.Activity a = pkg.receivers.get(i);
a.info.processName = fixProcessName(pkg.applicationInfo.processName,
a.info.processName, pkg.applicationInfo.uid);
mReceivers.addActivity(a, "receiver");
if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
if (r == null) {
r = new StringBuilder(256);
} else {
r.append(' ');
}
r.append(a.info.name);
}
}
if (r != null) {
if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Receivers: " + r);
}
N = pkg.activities.size();
r = null;
//保存Activity
for (i=0; i<N; i++) {
PackageParser.Activity a = pkg.activities.get(i);
a.info.processName = fixProcessName(pkg.applicationInfo.processName,
a.info.processName, pkg.applicationInfo.uid);
mActivities.addActivity(a, "activity");
if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
if (r == null) {
r = new StringBuilder(256);
} else {
r.append(' ');
}
r.append(a.info.name);
}
}
很简单的代码,就是把APK里的四大组件信息分别保存到mActivities、mReceivers、mServices、mProviders等,当然,实际上还保存了很多信息,这里就不管了。
// All available activities, for your resolving pleasure.
final ActivityIntentResolver mActivities =
new ActivityIntentResolver();
// All available receivers, for your resolving pleasure.
final ActivityIntentResolver mReceivers =
new ActivityIntentResolver();
// All available services, for your resolving pleasure.
final ServiceIntentResolver mServices = new ServiceIntentResolver();
// All available providers, for your resolving pleasure.
final ProviderIntentResolver mProviders = new ProviderIntentResolver();
都保存到IntentResolver里了。
我们拿activity来说吧,当我们启动activity时,会到mInstrumentation的execStartActivity方法跨进程调用AMS的startActivity,最后会转到startActivityMayWait方法里,这个方法会调用PMS的resolveIntent方法,这就是我们PMS发挥作用的时候了!(不了解Activity启动流程的可以参考我的这篇文章)
@Override
public ResolveInfo resolveIntent(Intent intent, String resolvedType,
int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "resolve intent");
List<ResolveInfo> query = queryIntentActivities(intent, resolvedType, flags, userId);
return chooseBestActivity(intent, resolvedType, flags, query, userId);
}
这段代码很清晰,调用queryIntentActivities在刚刚mActivities里找到对应的activity信息,具体我就不贴了,有兴趣的童鞋可以看源码。
终于知道manifest文件里的信息是如何取得的了,我们通过intent简单设置class就能找到对应的Activity及其信息。
阿勒,不好意思,人老容易忘事,PackageParser还没说。
三、parsePackage
刚刚说到parsePackage的parsePackage方法来解析APK信息,直接上代码
public Package parsePackage(File packageFile, int flags) throws PackageParserException {
if (packageFile.isDirectory()) {
return parseClusterPackage(packageFile, flags);
} else {
return parseMonolithicPackage(packageFile, flags);
}
}
如果是文件夹就执行parseClusterPackage,如果是文件就执行parseMonolithicPackage,这里我们直接看后者就好了
@Deprecated
public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
if (mOnlyCoreApps) {
final PackageLite lite = parseMonolithicPackageLite(apkFile, flags);
if (!lite.coreApp) {
throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"Not a coreApp: " + apkFile);
}
}
final AssetManager assets = new AssetManager();
try {
final Package pkg = parseBaseApk(apkFile, assets, flags);
pkg.codePath = apkFile.getAbsolutePath();
return pkg;
} finally {
IoUtils.closeQuietly(assets);
}
}
很清晰,先构建了AssetManager对象,然后直接调用parseBaseApk进行解析
private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
throws PackageParserException {
final String apkPath = apkFile.getAbsolutePath();
mParseError = PackageManager.INSTALL_SUCCEEDED;
mArchiveSourcePath = apkFile.getAbsolutePath();
if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);
final int cookie = loadApkIntoAssetManager(assets, apkPath, flags);
Resources res = null;
XmlResourceParser parser = null;
try {
res = new Resources(assets, mMetrics, null);
assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Build.VERSION.RESOURCES_SDK_INT);
//1
parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
final String[] outError = new String[1];
//2
final Package pkg = parseBaseApk(res, parser, flags, outError);
pkg.baseCodePath = apkPath;
pkg.mSignatures = null;
return pkg;
} catch (PackageParserException e) {
throw e;
} catch (Exception e) {
} finally {
IoUtils.closeQuietly(parser);
}
}
1、首先使用XmlResourceParser打开manifest文件
2、调用parseBaseApk解析manifest,在这个方法里就真正开始解析application、activity等信息了,就是普通的XML解析,我只贴上application解析的代码
if (tagName.equals("application")) {
//......
foundApp = true;
if (!parseBaseApplication(pkg, res, parser, attrs, flags, outError)) {
return null;
}
}
跟进parseBaseApplication方法
private boolean parseBaseApplication(Package owner, Resources res,
XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
throws XmlPullParserException, IOException {
//......
//解析icon等信息
ai.icon = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestApplication_icon, 0);
ai.logo = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestApplication_logo, 0);
ai.banner = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestApplication_banner, 0);
ai.theme = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestApplication_theme, 0);
ai.descriptionRes = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestApplication_description, 0);
//循环解析application节点
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if (tagName.equals("activity")) {
Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false,
owner.baseHardwareAccelerated);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.activities.add(a);
} else if (tagName.equals("receiver")) {
Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true, false);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.receivers.add(a);
} else if (tagName.equals("service")) {
Service s = parseService(owner, res, parser, attrs, flags, outError);
if (s == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.services.add(s);
} else if (tagName.equals("provider")) {
Provider p = parseProvider(owner, res, parser, attrs, flags, outError);
if (p == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.providers.add(p);
}
//省略其他解析代码
}
一目了然了,我就不多说了。
到这本文终于快结束了,我们回头来看看开篇所说的virtualAPK的LoadedPlugin,在他的构造方法里有这么一句话
this.mPackage = PackageParserCompat.parsePackage(context, apk, PackageParser.PARSE_MUST_BE_APK);
PackageParserCompat的作用就是解析APK,parsePackage方法做了适配,本文的源码基于API22,所以我们直接看相关方法
static final PackageParser.Package parsePackage(final Context context, final File apk, final int flags) throws PackageParser.PackageParserException {
PackageParser parser = new PackageParser();
PackageParser.Package pkg = parser.parsePackage(apk, flags);
try {
parser.collectCertificates(pkg, flags);
} catch (Throwable e) {
// ignored
}
return pkg;
}
果然是利用系统的PackageParser 去解析APK信息
四、总结
1、开机启动时PMS扫描framework及相关核心代码
2、浏览已安装应用目录,一一扫描并利用PackageParser解析APK信息
3、将扫描好的APK信息保存在IntentResolver里面备用
4、当四大组件启动时,去 IntentResolver找到相应信息,比如系统判定是否在manifest里注册也是通过这里保存的信息。