前面学习到了PackageManagerService构造函数的前四步,第一步创建全局设置对象mSettings,协助PMS保存系统中安装APP包名,权限,四大组件等相关信息的存储,并且创建packages.xml和packages.list文件保存所有apk的权限和信息等等。然后第二步通过HandlerThread线程创建了PackageHandler的实例mHandler来处理消息,然后第三步是通过mSettings调用readLPw()方法,读取和解析配置更新packages.xml文件,这一步会将packages.xml文件中的package解析出来以PackageSetting对象的形式放进mPackages集合中,然后是第四步,遍历了前面解析出来的所有PackageSetting对象,然后去检查是否有被误删的apk,或者是已经卸载的apk,有的话就从mSettings中移除相应的PackageSetting对象。然后接下来就是我们需要重点讨论的包扫描过程了。
final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
scanDirTracedLI(privilegedAppDir,
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_PRIVILEGED,
0);
// Collect ordinary system packages.
final File systemAppDir = new File(Environment.getRootDirectory(), "app");
scanDirTracedLI(systemAppDir,
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM,
0);
首先是调用scanDirTracedLI()扫描/system/目录下的app和priv-app目录,然后还有其他的目录,像framewotk/、vendor/、odm/、product/等。在scanDirTracedLI()方法中,调用scanDirLI()方法进行扫描。
private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime) {
// 1、获取目录下的文件集合,即所有的apk列表,然后逐个解析
final File[] files = scanDir.listFiles();
...
// 2、创建ParallelPackageParser对象
try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(
mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir,
mParallelPackageParserCallback)) {
int fileCount = 0;
for (File file : files) {
//3、遍历files集合,使用parallelPackageParser提交每个apk文件
parallelPackageParser.submit(file, parseFlags);
fileCount++;
}
//4、取出每个File的扫描结果,执行scan
for (; fileCount > 0; fileCount--) {
ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags,currentTime, null);
...
}
...
}
scanDirLI()方法主要功能如下:
1.遍历文件目录下的所有文件;
2.创建ParallelPackageParser对象,调用submit()方法提交请求,执行解析每个apk文件;
3.调用parallelPackageParser.take()逐个取出每个解析的结果;
到这里我们知道PMS是对多个目录中所有apk文件进行解析,在之前的版本中会直接创建PackageParser对象执行解析,在Androip P版本中引入ParallelPackageParser类,使用线程池和队列执行程序的解析;
ParallelPackageParser:内部使用线程池和队列执行文件目录中apk扫描解析
// 1、保存请求结果的队列,阻塞线程
private final BlockingQueue<ParseResult> mQueue = new ArrayBlockingQueue<>(QUEUE_CAPACITY);
// 2、创建线程池
private final ExecutorService mService = ConcurrentUtils.newFixedThreadPool(MAX_THREADS,
"package-parsing-thread", Process.THREAD_PRIORITY_FOREGROUND);
// 3、线程池提交任务
public void submit(File scanFile, int parseFlags) {
mService.submit(() -> {
ParseResult pr = new ParseResult();
try {
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setOnlyCoreApps(mOnlyCore);
pp.setDisplayMetrics(mMetrics);
pp.setCacheDir(mCacheDir);
pp.setCallback(mPackageParserCallback);
pr.scanFile = scanFile;
pr.pkg = parsePackage(pp, scanFile, parseFlags); // 执行文件的解析扫描,将结果封装在ParseResult中
}
//4、将解析的结果添加到队列中
try {
mQueue.put(pr); //
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
mInterruptedInThread = Thread.currentThread().getName();
}
});
}
public ParseResult take() {
try {
return mQueue.take(); // 从队列中取出解析结果
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IllegalStateException(e);
}
}
ParallelPackageParser中利用线程池处理并发问题,执行多个apk文件的解析,并使用阻塞队列的方式同步线程的数据,在submit提交的任务run()方法中,创建了PackageParser对象并调用parserPackage()方法解析apk,之后将解析的结果封装在ParseResult中,最后添加到mQueue队列中,PMS中在依次调用take()方法从mQueue队列中获取执行的结果;
public Package parsePackage(File packageFile, int flags, boolean useCaches)
throws PackageParserException {
// 1、从缓存中获取解析结果
Package parsed = useCaches ? getCachedResult(packageFile, flags) : null;
if (parsed != null) {
return parsed;
}
// 2、判断是文件还是文件夹,分别执行不同的方法;
if (packageFile.isDirectory()) {
parsed = parseClusterPackage(packageFile, flags); // 执行文件夹解析
} else {
parsed = parseMonolithicPackage(packageFile, flags); // 执行单个apk文件解析,单个安装apk文件时执行
}
3、缓存解析的结果parser
cacheResult(packageFile, flags, parsed); //
return parsed;
}
parserPackage()方法中会根据传入的文件的类型夹执行不同的方法,如果是文件夹,执行parseClusterPackage(),是单个apk则执行 parseMonolithicPackage()。另外ParserPackage类中还有一个包解析的方法parsePackageLite(),此方法只对apk做轻量级解析,然后返回一个含有apk少量信息的PackageLite对象。刚刚看到PackageParser中parserPackage()方法调用parseMonolithicPackage()解析apk;
public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
//1.获取apk的少量信息
final PackageLite lite = parseMonolithicPackageLite(apkFile, flags);
// 2、使用AssetManager加载资源文件
final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
try {
// 3.执行parseBaseAPk
final Package pkg = parseBaseApk(apkFile, assetLoader.getBaseAssetManager(), flags);
pkg.setCodePath(apkFile.getCanonicalPath());
pkg.setUse32bitAbi(lite.use32bitAbi);
return pkg;
}
}
parseMonolithicPackage()中先调用parseMonolithicPackageLite()获取apk的少量信息,其中parseApkLite()将File先简单的解析以下,这里的解析只是获取注册清单中的基础信息,并将信息保存在ApkLite对象中,然后将ApkLite和文件路径封装在PackageLite对象中;
private static PackageLite parseMonolithicPackageLite(File packageFile, int flags) throws PackageParserException {
final ApkLite baseApk = parseApkLite(packageFile, flags);
final String packagePath = packageFile.getAbsolutePath();
return new PackageLite(packagePath, baseApk, null, null, null, null, null, null);
}
//先轻量级解析apk的基本信息
private static ApkLite parseApkLite(String codePath, XmlPullParser parser, AttributeSet attrs,SigningDetails signingDetails)
throws IOException, XmlPullParserException, PackageParserException {
final Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs);
for (int i = 0; i < attrs.getAttributeCount(); i++) {
final String attr = attrs.getAttributeName(i);
if (attr.equals("installLocation")) {
installLocation = attrs.getAttributeIntValue(i,
PARSE_DEFAULT_INSTALL_LOCATION);
} else if (attr.equals("versionCode")) {
versionCode = attrs.getAttributeIntValue(i, 0);
} else if (attr.equals("versionCodeMajor")) {
versionCodeMajor = attrs.getAttributeIntValue(i, 0);
} else if (attr.equals("revisionCode")) {
revisionCode = attrs.getAttributeIntValue(i, 0);
}
.....
}
......
return new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit,
configForSplit, usesSplitName, versionCode, versionCodeMajor, revisionCode,
installLocation, verifiers, signingDetails, coreApp, debuggable,
multiArch, use32bitAbi, extractNativeLibs, isolatedSplits);
}
接着回到parseMonolithicPackage()方法中,
DefaultSplitAssetLoader:内部使用AssestManager加载apk文件的资源,并缓存AssestManager对象信息,主要针对split APK;
parseBaseApk():解析每个apk文件,parseBaseApk(File apkFile, AssetManager assets, int flags)中从apkFile对象中获取apk文件路径,然后使用assmgr加载apk文件中的资源,从文件中读取注册清单文件,然后调用重载的parseBaseApk()解析注册清单;
private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
throws PackageParserException {// 参数:apk文件File、apk文件路径destCodePath
// 1、获取apk文件路径
final String apkPath = apkFile.getAbsolutePath();
mArchiveSourcePath = sourceFile.getPath();
// 2、将apk的路径添加到AssetManager中加载资源
int cookie = assmgr.findCookieForPath(mArchiveSourcePath);
// 3、解析xml注册清单文件
parser = assmgr.openXmlResourceParser(cookie, "AndroidManifest.xml");
// 4、创建Resource对象
Resources res = new Resources(assmgr, metrics, null);
pkg = parseBaseApk(res, parser, flags, errorText); // 解析parser中的信息,保存在Package对象中
pkg.setVolumeUuid(volumeUuid);
pkg.setApplicationVolumeUuid(volumeUuid);
pkg.setBaseCodePath(apkPath);
pkg.setSigningDetails(SigningDetails.UNKNOWN);
return pkg;
}
parseBaseApk(String apkPath, Resources res, XmlResourceParser parser, int flags,
String[] outError):从Parser对象中获取解析的信息,保存在Package对象中,parseBaseApk()中从parser中提取注册清单中的基础信息,并封装保存在Pakage对象中,然后调用parseBaseApkCommon()方法继续解析清单文件中内容。
private Package parseBaseApk(String apkPath,Resources res, XmlResourceParser parser, int flags, String[] outError){
//1、从parser中解析出“package”设置的包名pkgName
Pair<String, String> packageSplit = parsePackageSplitNames(parser, parser);
pkgName = packageSplit.first;
splitName = packageSplit.second;
// 2、创建Package对象,保存apk的包名
Package pkg = new Package(pkgName);
//从Resource中获取各种信息,并保存在Package的属性中
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifest);
pkg.mVersionCode = sa.getInteger(…...
pkg.mVersionName = sa.getNonConfigurationString(…...
pkg.mSharedUserId = str.intern(…...);
pkg.mSharedUserLabel = sa.getResourceId(…...
pkg.installLocation = sa.getInteger(…...
pkg.applicationInfo.installLocation = pkg.installLocation;
// 3、调用parseBaseApkCommon()继续解析文件
return parseBaseApkCommon(pkg, null, res, parser, flags, outError);
}
//parseBaseApkCommon()从Parser对象中解析数据信息
private Package parseBaseApkCommon(Package pkg, Set<String> acceptedTags, Resources res,
XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException,
IOException {
...
int outerDepth = parser.getDepth(); // 获取parser的深度
while ((type=parser.next()) != parser.END_DOCUMENT. // 1、循环解析parser对象
&& (type != parser.END_TAG || parser.getDepth() > outerDepth)) {
if (tagName.equals("application")) {
if (!parseBaseApplication(pkg, res, parser, attrs, flags, outError)) { // 2、解析application标签
return null;
}else if (tagName.equals("permission")) { // 3、解析权限标签
if (parsePermission(pkg, res, parser, attrs, outError) == null) {
return null;
}
} else if (tagName.equals("uses-feature")) { // 4、解析使用的user-feature标签,并保存在Package的集合中
FeatureInfo fi = new FeatureInfo();
…….
pkg.reqFeatures.add(fi);
}else if (tagName.equals("uses-sdk")) { // 解析user-sdk标签
}else if (tagName.equals("supports-screens")) { // 解析support-screens标签
}
}
}
parseBaseApkCommon()中主要负责解析清单文件中的各种标签信息,其中最主要的就是解析标签下的四大组件的信息,在遇到applicaiton标签时直接调用了parseBaseApplication()执行解析;
private boolean parseBaseApplication(Package owner, Resources res,
XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException, IOException {
String tagName = parser.getName();
final ApplicationInfo ai = owner.applicationInfo;
ai.theme = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestApplication_theme, 0);
ai.descriptionRes = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestApplication_description, 0);
ai.maxAspectRatio = sa.getFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, 0);
// 分别解析四大组件,将解析结果保存在Package对应的集合中
if (tagName.equals("activity")) {
Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false);
owner.activities.add(a);
} else if (tagName.equals("receiver")) {
Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true);
owner.receivers.add(a);
} else if (tagName.equals("service")) {
Service s = parseService(owner, res, parser, attrs, flags, outError);
owner.services.add(s);
} else if (tagName.equals("provider")) {
Provider p = parseProvider(owner, res, parser, attrs, flags, outError);
owner.providers.add(p);
}
清单文件解析共分两部分:
1.解析出application标签下设置的name类名、icon、theme、targetSdk、processName等属性标签并保存在ApplicationInfo对象中
2.循环解析activity、receiver、service、provider四个标签,并将信息到保存在Package中对应的集合中
接下来看四大组件是如何解析保存的
1.parserActivity():解析Activity和Receiver标签并返回Activity对象封装所有属性;
private Activity parseActivity(Package owner,...)
throws XmlPullParserException, IOException {
TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestActivity);
/ 1、判断为activity或receiver
cachedArgs.mActivityArgs.tag = receiver ? "<receiver>" : "<activity>"; /
cachedArgs.mActivityArgs.sa = sa;
cachedArgs.mActivityArgs.flags = flags;
// 2、创建Activity实例,并初始化系列属性
Activity a = new Activity(cachedArgs.mActivityArgs, new ActivityInfo());
a.info.theme = sa.getResourceId(R.styleable.AndroidManifestActivity_theme, 0);
a.info.taskAffinity = buildTaskAffinityName(owner.applicationInfo.packageName,
owner.applicationInfo.taskAffinity, str, outError);
a.info.launchMode = ...
// 3、解析intent-filter
if (parser.getName().equals("intent-filter")) {
ActivityIntentInfo intent = new ActivityIntentInfo(a);
if (!parseIntent(res, parser, true /*allowGlobs*/, true /*allowAutoVerify*/,
intent, outError)) {
return null;
}
a.order = Math.max(intent.getOrder(), a.order);
a.intents.add(intent); // 将intent设置到Activity
}
return a;
}
在解析Activity和Receiver标签时,当标签设置intent-filter时则创建一个ActivityIntentInfo对象,并调用parseIntent()将intent-filter标签下的信息解析到ActivityIntentInfo中,并将ActivityIntentInfo对象保存在a.intents的集合中,简单的说一个intent-filter对应一个ActivityIntentInfo对象,一个Activity和Receiver可以包好多个intent-filter;
1.1 parseIntent():解析每个intent-filter标签下的action、name等属性值,并将所有的属性值保存在outInfo对象中,这里的outInfo是ActivityIntentInfo对象;
private Activity parseActivity(Package owner,...)
throws XmlPullParserException, IOException {
...
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
String nodeName = parser.getName(); // 1、获取节点名称
if (nodeName.equals("action")) { //2、处理action节点
String value = parser.getAttributeValue( // 3、获取action节点的name属性,并保存在outInfo属性中
ANDROID_RESOURCES, "name");
outInfo.addAction(value); //
} else if (nodeName.equals("category")) {
String value = parser.getAttributeValue( // 4、获取category属性,保存在outInfo属性中
ANDROID_RESOURCES, "name");
outInfo.addCategory(value); // 添加到Category集合中
} else if (nodeName.equals("data")) {
sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestData); // 解析parser为sa属性
String str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestData_mimeType, 0);
outInfo.addDataType(str); // 获取并保存mimeType中
str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestData_scheme, 0);
outInfo.addDataScheme(str); // 获取并保存scheme中
String host = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestData_host, 0); //
String port = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestData_port, 0);
outInfo.addDataAuthority(host, port); // 获取并保存host、port属性
outInfo.hasDefault = outInfo.hasCategory(Intent.CATEGORY_DEFAULT); // 设置Default属性
}
1.2 ActivityIntentInfo 继承 IntentInfo ,IntentInfo继承IntentFilter属性,所以outInfo保存的数据都保存在IntentFilter中,在IntentFilter中有标签对应的集合,如actions、mDataSchemes等,所以遍历的所有intent-filter中设置的数据都保存在其中;
public final static class ActivityIntentInfo extends IntentInfo {}
public static abstract class IntentInfo extends IntentFilter {}
public final void addAction(String action) {
if (!mActions.contains(action)) {
mActions.add(action.intern()); // 在Intent-Filter中保存action
}
}
public final void addCategory(String category) {
if (mCategories == null) mCategories = new ArrayList<String>();
if (!mCategories.contains(category)) {
mCategories.add(category.intern()); //
}
}
public final void addDataScheme(String scheme) {
if (mDataSchemes == null) mDataSchemes = new ArrayList<String>();
if (!mDataSchemes.contains(scheme)) {
mDataSchemes.add(scheme.intern()); //
}
}
2.parserService():解析Service标签并返回Service对象,对应service标签下的信息保存在ServiceIntentInfo对象中,ServiceIntentInfo的作用和保存和ActivityIntentInfo一样。
private Service parseService(Package owner, Resources res,
XmlResourceParser parser, int flags, String[] outError,
CachedComponentArgs cachedArgs)
throws XmlPullParserException, IOException {
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestService);
cachedArgs.mServiceArgs.sa = sa;
cachedArgs.mServiceArgs.flags = flags;
Service s = new Service(cachedArgs.mServiceArgs, new ServiceInfo()); // 创建Service对象
if (parser.getName().equals("intent-filter")) { // 解析intent-filter节点
ServiceIntentInfo intent = new ServiceIntentInfo(s);
if (!parseIntent(res, parser, true /*allowGlobs*/, false /*allowAutoVerify*/,
intent, outError)) {
return null;
}
s.order = Math.max(intent.getOrder(), s.order);
s.intents.add(intent);
} else if (parser.getName().equals("meta-data")) { // 解析meta-data数据,保存在bundle中
if ((s.metaData=parseMetaData(res, parser, s.metaData,
outError)) == null) {
return null;
}
}
3.parserProvider():解析provider标签并返回provider对象
private Provider parseProvider(Package owner, Resources res,
XmlResourceParser parser, int flags, String[] outError,
CachedComponentArgs cachedArgs)
throws XmlPullParserException, IOException {
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestProvider);
cachedArgs.mProviderArgs.tag = "<provider>";
cachedArgs.mProviderArgs.sa = sa;
cachedArgs.mProviderArgs.flags = flags;
Provider p = new Provider(cachedArgs.mProviderArgs, new ProviderInfo()); // 创建Provider对象
p.info.exported = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestProvider_exported,
providerExportedDefault);
String permission = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestProvider_permission, 0);
p.info.multiprocess = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestProvider_multiprocess,
false);
p.info.authority = cpname.intern();
if (!parseProviderTags( // 调用parseProvider解析provider下的标签
res, parser, visibleToEphemeral, p, outError)) {
return null;
}
return p;
}
在解析provider标签时,创建ProviderInfo对象保存设置的属性信息,如export、permission等,然后调用parseProviderTags解析provider标签中使用的其他标签信息。
3.1 parseProviderTags():
private boolean parseProviderTags(Resources res, XmlResourceParser parser,
boolean visibleToEphemeral, Provider outInfo, String[] outError){
int outerDepth = parser.getDepth();
int type;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
|| parser.getDepth() > outerDepth)) {
if (parser.getName().equals("intent-filter")) {
ProviderIntentInfo intent = new ProviderIntentInfo(outInfo);
if (!parseIntent(res, parser, true /*allowGlobs*/, false /*allowAutoVerify*/,
intent, outError)) {
return false;
}
outInfo.order = Math.max(intent.getOrder(), outInfo.order);
outInfo.intents.add(intent);
}
if (parser.getName().equals("meta-data")) {
if ((outInfo.metaData=parseMetaData(res, parser,
outInfo.metaData, outError)) == null) {
return false;
}
}
if (parser.getName().equals("grant-uri-permission")) {
String str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestGrantUriPermission_path, 0);
PatternMatcher pa = new PatternMatcher(str, PatternMatcher.PATTERN_LITERAL); // 创建PatterMatcher匹配权限
PatternMatcher[] newp = new PatternMatcher[N+1];
newp[N] = pa;
outInfo.info.uriPermissionPatterns = newp; // 保存临时权限数组
}
if (parser.getName().equals("path-permission")) { // 保存路径权限
newp[N] = pa;
outInfo.info.pathPermissions = newp;
}
}
}
主要解析如下:
1.对于intent-filter标签,调用parserIntent()解析保存在ProviderIntentInfo对象中,并添加到intent集合中
2.解析设置的meta-data值
3.解析grant-uri-permission和path-permission等权限的匹配状况保存在Provider对象中的数组中;
到此apk文件已经解析完成,相应的文件和属性都封装在Package对象中,并都保存在PMS属性集合mPackages集合中。
原文链接:https://blog.csdn.net/Alexwll/article/details/102777742