Android系統服務PMS 包的解析過程(三)
一、背景
包的形式有各種各樣的包,如 xx.apk、xxx.jar、xx.so。PMS
作為包的管理中心,管理Android中各種各樣的包。
而管理是需要在記憶體中才能進行的,因此,如何把靜態形式的資料轉變為記憶體中對應的資料結構,這就需要通過掃描來完成。
二、 包的掃描過程
我們關心apk的掃描過程。本質我們關心從靜態apk檔案 轉變成記憶體中的資料結構是什麼樣的!
不管是 system/framework分割槽
還是 data/分割槽
。都會呼叫這個方法來進行掃描。
2.1 scanDirTracedLI()
xxxLI結尾的方法表明需要 mInstaller 鎖。 xxxLP結尾的方法表明需要 mPackages 鎖。
private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags, long currentTime) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
try {
scanDirLI(scanDir, parseFlags, scanFlags, currentTime);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
這個方法僅僅追加了trace日誌。
``` private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime) { final File[] files = scanDir.listFiles(); ....... try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser( mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir, mParallelPackageParserCallback)) { // Submit files for parsing in parallel int fileCount = 0; // 遍歷檔案 for (File file : files) { final boolean isPackage = (isApkFile(file) || file.isDirectory()) && !PackageInstallerService.isStageName(file.getName()); if (!isPackage) { // Ignore entries which are not packages continue; } //1 解析 parallelPackageParser.submit(file, parseFlags); fileCount++; }
// Process results one by one 2 解析完成後,處理結果
for (; fileCount > 0; fileCount--) {
ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
Throwable throwable = parseResult.throwable;
int errorCode = PackageManager.INSTALL_SUCCEEDED;
if (throwable == null) {
// TODO(toddke): move lower in the scan chain
// Static shared libraries have synthetic package names
if (parseResult.pkg.applicationInfo.isStaticSharedLibrary()) {
renameStaticSharedLibraryPackage(parseResult.pkg);
}
try {
scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags,
currentTime, null);
} catch (PackageManagerException e) {
errorCode = e.error;
Slog.w(TAG, "Failed to scan " + parseResult.scanFile + ": " + e.getMessage());
}
} else if (throwable instanceof PackageParser.PackageParserException) {
PackageParser.PackageParserException e = (PackageParser.PackageParserException)
throwable;
errorCode = e.error;
Slog.w(TAG, "Failed to parse " + parseResult.scanFile + ": " + e.getMessage());
} else {
throw new IllegalStateException("Unexpected exception occurred while parsing "
+ parseResult.scanFile, throwable);
}
// Delete invalid userdata apps
if ((scanFlags & SCAN_AS_SYSTEM) == 0 &&
errorCode != PackageManager.INSTALL_SUCCEEDED) {
logCriticalInfo(Log.WARN,
"Deleting invalid package at " + parseResult.scanFile);
removeCodePathLI(parseResult.scanFile);
}
}
}
}
``
遍歷給到目錄下檔案,開始解析。我們看看
ParallelPackageParse` 類的submit()方法。
2.2 ParallelPackageParse.submit()
public void submit(File scanFile, int parseFlags) {
//mService是執行緒池
mService.submit(() -> {
ParseResult pr = new ParseResult();
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parallel parsePackage [" + scanFile + "]");
try {
// 1
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setOnlyCoreApps(mOnlyCore);
pp.setDisplayMetrics(mMetrics);
pp.setCacheDir(mCacheDir);
pp.setCallback(mPackageParserCallback);
pr.scanFile = scanFile; // 解析的檔案
// 2
pr.pkg = parsePackage(pp, scanFile, parseFlags);
} catch (Throwable e) {
pr.throwable = e;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
try {
mQueue.put(pr); // 3
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
// Propagate result to callers of take().
// This is helpful to prevent main thread from getting stuck waiting on
// ParallelPackageParser to finish in case of interruption
mInterruptedInThread = Thread.currentThread().getName();
}
});
}
1. 構建解析物件,設定解析引數
2. 解析得到 package 物件 ,賦值給result返回
3. 把解析結果加入佇列
具體看看第二步的解析過程,如何得到package物件的。
@VisibleForTesting
protected PackageParser.Package parsePackage(PackageParser packageParser, File scanFile,
int parseFlags) throws PackageParser.PackageParserException {
return packageParser.parsePackage(scanFile, parseFlags, true /* useCaches */);
}
最終呼叫了 packageParser類的 parsePackage()
方法。
``` @UnsupportedAppUsage public Package parsePackage(File packageFile, int flags, boolean useCaches) throws PackageParserException { // 如果useCaches=true,優先獲取快取 Package parsed = useCaches ? getCachedResult(packageFile, flags) : null; if (parsed != null) { return parsed; }
long parseTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
if (packageFile.isDirectory()) {
// cluster 多個splits apk 情況
parsed = parseClusterPackage(packageFile, flags);
} else {
// monolithic 完整包。只要一個apk
parsed = parseMonolithicPackage(packageFile, flags);
}
long cacheTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
cacheResult(packageFile, flags, parsed);
if (LOG_PARSE_TIMINGS) {
parseTime = cacheTime - parseTime;
cacheTime = SystemClock.uptimeMillis() - cacheTime;
if (parseTime + cacheTime > LOG_PARSE_TIMINGS_THRESHOLD_MS) {
Slog.i(TAG, "Parse times for '" + packageFile + "': parse=" + parseTime
+ "ms, update_cache=" + cacheTime + " ms");
}
}
return parsed;
}
``` 如果是資料夾,則對應的是splits包,否則是完整包。
splits包: 對應一個baseApk和一個或多個的aplits apk。
完整包,就是單個完整的apk檔案。
先看完整包的解析。
2.2 parseMonolithicPackage()
PackageLite
:單個包輕量級的資訊 Lightweight parsed details about a single package.ApkLite
:單個apk檔案的一些簡單的資訊 Lightweight parsed details about a single APK file.
``` public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException { // 1 final PackageLite lite = parseMonolithicPackageLite(apkFile, flags); if (mOnlyCoreApps) { if (!lite.coreApp) { throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, "Not a coreApp: " + apkFile); } }
final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
try {// 2
final Package pkg = parseBaseApk(apkFile, assetLoader.getBaseAssetManager(), flags);
pkg.setCodePath(apkFile.getCanonicalPath());
pkg.setUse32bitAbi(lite.use32bitAbi);
return pkg;
} catch (IOException e) {
throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
"Failed to get path: " + apkFile, e);
} finally {
IoUtils.closeQuietly(assetLoader);
}
}
``
1. 解析清單檔案,得到 PackageLite 物件,它包含了一些包的輕量級的資訊。如 安裝位置、程式碼路徑、ApkLite的基本資訊等。
2. 最終呼叫 核心介面
parseBaseApk(),得到
Package` 物件。
2.2.1 parseBaseApk()
``` private Package parseBaseApk(File apkFile, AssetManager assets, int flags) throws PackageParserException { final String apkPath = apkFile.getAbsolutePath();
......
// 1
parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
// 2
final Resources res = new Resources(assets, mMetrics, null);
final String[] outError = new String[1];
// 3
final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);
// 4
pkg.setVolumeUuid(volumeUuid);
pkg.setApplicationVolumeUuid(volumeUuid);
pkg.setBaseCodePath(apkPath);
pkg.setSigningDetails(SigningDetails.UNKNOWN);
return pkg;
}
``
1. 通過
AssetManager得到清單檔案的解析器 parser
2. 新建一個 resources 資源物件
3. 呼叫過載方法同名方法
parseBaseApk()`
4. 賦值pkg一些資訊
2.2.2 parseBaseApk() 重名方法
``` private Package parseBaseApk(String apkPath, Resources res, XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException, IOException { final String splitName; final String pkgName;
try {
Pair<String, String> packageSplit = parsePackageSplitNames(parser, parser);
pkgName = packageSplit.first;
splitName = packageSplit.second;
if (!TextUtils.isEmpty(splitName)) { //1
outError[0] = "Expected base APK, but found split " + splitName;
mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
return null;
}
} catch (PackageParserException e) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
return null;
}
......
// 2
final Package pkg = new Package(pkgName);
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifest);
pkg.mVersionCode = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
pkg.mVersionCodeMajor = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_versionCodeMajor, 0);
pkg.applicationInfo.setVersionCode(pkg.getLongVersionCode());
pkg.baseRevisionCode = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_revisionCode, 0);
pkg.mVersionName = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifest_versionName, 0);
if (pkg.mVersionName != null) {
pkg.mVersionName = pkg.mVersionName.intern();
}
......
pkg.mCompileSdkVersion = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_compileSdkVersion, 0);
pkg.applicationInfo.compileSdkVersion = pkg.mCompileSdkVersion;
pkg.mCompileSdkVersionCodename = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifest_compileSdkVersionCodename, 0);
if (pkg.mCompileSdkVersionCodename != null) {
pkg.mCompileSdkVersionCodename = pkg.mCompileSdkVersionCodename.intern();
}
pkg.applicationInfo.compileSdkVersionCodename = pkg.mCompileSdkVersionCodename;
sa.recycle();
// 3
return parseBaseApkCommon(pkg, null, res, parser, flags, outError);
}
``
1. 這裡只解析 baseApk,如果是splitApk,則返回
2. 建立package 物件,並賦值基礎的資訊
3. 呼叫
parseBaseApkCommon()`
2.2.3 parseBaseApkCommon()
```
private Package parseBaseApkCommon(Package pkg, Set
String tagName = parser.getName();
......
if (tagName.equals(TAG_APPLICATION)) {
......
foundApp = true;
// 2
if (!parseBaseApplication(pkg, res, parser, flags, outError)) {
return null;
}
} else if (tagName.equals(TAG_KEY_SETS)) {
if (!parseKeySets(pkg, res, parser, outError)) {
return null;
}
// 3
} else if (tagName.equals(TAG_PERMISSION_GROUP)) { //許可權組
if (!parsePermissionGroup(pkg, flags, res, parser, outError)) {
return null;
}
} else if (tagName.equals(TAG_PERMISSION)) { //許可權
if (!parsePermission(pkg, res, parser, outError)) {
return null;
}
} else if (tagName.equals(TAG_PERMISSION_TREE)) { //許可權相關
if (!parsePermissionTree(pkg, res, parser, outError)) {
return null;
}
} else if (tagName.equals(TAG_USES_PERMISSION)) {//使用者許可權
if (!parseUsesPermission(pkg, res, parser)) {
return null;
}
} else if (tagName.equals(TAG_PROTECTED_BROADCAST)) {
......
}
return
}
``
1.
遍歷清單檔案的xml節點
2. 解析
application` 標籤節點
3. 解析許可權相關的節點
4. 其他節點
方法很長,我們只關注重點部分: application標籤的解析。
2.2.4 parseBaseApplication()
``` private boolean parseBaseApplication(Package owner, Resources res, XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException, IOException { final ApplicationInfo ai = owner.applicationInfo; final String pkgName = owner.applicationInfo.packageName;
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestApplication);
......
int type;
boolean hasActivityOrder = false;
boolean hasReceiverOrder = false;
boolean hasServiceOrder = false;
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();
// 1
if (tagName.equals("activity")) {
Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs, false,
owner.baseHardwareAccelerated);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
hasActivityOrder |= (a.order != 0);
owner.activities.add(a);
// 2
} else if (tagName.equals("receiver")) {
Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs,
true, false);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
hasReceiverOrder |= (a.order != 0);
owner.receivers.add(a);
// 3
} else if (tagName.equals("service")) {
Service s = parseService(owner, res, parser, flags, outError, cachedArgs);
if (s == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
hasServiceOrder |= (s.order != 0);
owner.services.add(s);
// 4
} else if (tagName.equals("provider")) {
Provider p = parseProvider(owner, res, parser, flags, outError, cachedArgs);
if (p == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.providers.add(p);
} else if (tagName.equals("activity-alias")) {
Activity a = parseActivityAlias(owner, res, parser, flags, outError, cachedArgs);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
hasActivityOrder |= (a.order != 0);
owner.activities.add(a);
} else if (parser.getName().equals("meta-data")) {
// note: application meta-data is stored off to the side, so it can
// remain null in the primary copy (we like to avoid extra copies because
// it can be large)
if ((owner.mAppMetaData = parseMetaData(res, parser, owner.mAppMetaData,
outError)) == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
}else
......
}
}
retur true;
}
```
- 解析得到 Activity標籤對應的記憶體中
Activity物件
。 Activity a = new Activity(cachedArgs.mActivityArgs, new ActivityInfo()); - 解析 receiver標籤,
- 解析 service 標籤,得到 Service s = new Service(cachedArgs.mServiceArgs, new ServiceInfo());
- 解析 provider 標籤,得到 Provider p = new Provider(cachedArgs.mProviderArgs, new ProviderInfo());
- 解析 meta-data、activity-alias等標籤
至此,我們得到了 application和四大元件在記憶體中的資訊
。 至於在記憶體中的資料結構是怎麼樣的,放到最後總結。
再看看splits
包的解析。
2.3 parseClusterPackage()
``` private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
final PackageLite lite = parseClusterPackageLite(packageDir, 0); // 1
if (mOnlyCoreApps && !lite.coreApp) {
throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"Not a coreApp: " + packageDir);
}
// Build the split dependency tree.
SparseArray<int[]> splitDependencies = null;
final SplitAssetLoader assetLoader;
if (lite.isolatedSplits && !ArrayUtils.isEmpty(lite.splitNames)) {
try {
splitDependencies = SplitAssetDependencyLoader.createDependenciesFromPackage(lite);
assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags);
} catch (SplitAssetDependencyLoader.IllegalDependencyException e) {
throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, e.getMessage());
}
} else {
assetLoader = new DefaultSplitAssetLoader(lite, flags);
}
try {
final AssetManager assets = assetLoader.getBaseAssetManager();
final File baseApk = new File(lite.baseCodePath);
// 2
final Package pkg = parseBaseApk(baseApk, assets, flags);
if (pkg == null) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
"Failed to parse base APK: " + baseApk);
}
if (!ArrayUtils.isEmpty(lite.splitNames)) {
final int num = lite.splitNames.length;
pkg.splitNames = lite.splitNames;
pkg.splitCodePaths = lite.splitCodePaths;
pkg.splitRevisionCodes = lite.splitRevisionCodes;
pkg.splitFlags = new int[num];
pkg.splitPrivateFlags = new int[num];
pkg.applicationInfo.splitNames = pkg.splitNames;
pkg.applicationInfo.splitDependencies = splitDependencies;
pkg.applicationInfo.splitClassLoaderNames = new String[num];
for (int i = 0; i < num; i++) {
final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);
// 3
parseSplitApk(pkg, i, splitAssets, flags);
}
}
pkg.setCodePath(packageDir.getCanonicalPath());
pkg.setUse32bitAbi(lite.use32bitAbi);
return pkg;
} catch (IOException e) {
throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
"Failed to get path: " + lite.baseCodePath, e);
} finally {
IoUtils.closeQuietly(assetLoader);
}
}
``
1. 也是先解析得到包的輕量級資訊 packageLite,裡面包含了一個 apkLite物件。
2. 解析
baseApk,流程跟解析完整包一樣
3. 解析
splits apk,內部也是通過
解析清單檔案`來構建記憶體中對應的資料結構
綜上, 包掃描的最終目的就是得到記憶體中包對應的資料結構,那麼這些資料結構是什麼關係呢?
三、解析後的資料結構
3.1 Package
PackageParser
中的內部類。Package
物件在記憶體中表示一個完整apk
檔案。
- 包含一個baseApk以及0個或者多個splits Apks。
- 包含了所有四大元件的集合。
- 其他基礎資訊
此外還有 PackageLite
、ApkLite
等值包含輕量級資訊(如 包名、版本號、程式碼路徑等)的物件。
```
public final static class Package implements Parcelable {
@UnsupportedAppUsage
public String packageName;
public String manifestPackageName;
/** Names of any split APKs, ordered by parsed splitName */
public String[] splitNames;
// TODO: work towards making these paths invariant
public String volumeUuid;
/**
* Path where this package was found on disk. For monolithic packages
* this is path to single base APK file; for cluster packages this is
* path to the cluster directory.
*/
public String codePath;
/** Path of base APK */
public String baseCodePath;
/** Paths of any split APKs, ordered by parsed splitName */
public String[] splitCodePaths;
/** Revision code of base APK */
public int baseRevisionCode;
/** Revision codes of any split APKs, ordered by parsed splitName */
public int[] splitRevisionCodes;
/** Flags of any split APKs; ordered by parsed splitName */
public int[] splitFlags;
/**
* Private flags of any split APKs; ordered by parsed splitName.
*
* {@hide}
*/
public int[] splitPrivateFlags;
public boolean baseHardwareAccelerated;
// For now we only support one application per package.
@UnsupportedAppUsage
public ApplicationInfo applicationInfo = new ApplicationInfo();
@UnsupportedAppUsage
public final ArrayList<Permission> permissions = new ArrayList<Permission>(0);
@UnsupportedAppUsage
public final ArrayList<PermissionGroup> permissionGroups = new ArrayList<PermissionGroup>(0);
@UnsupportedAppUsage
public final ArrayList<Activity> activities = new ArrayList<Activity>(0);
@UnsupportedAppUsage
public final ArrayList<Activity> receivers = new ArrayList<Activity>(0);
@UnsupportedAppUsage
public final ArrayList<Provider> providers = new ArrayList<Provider>(0);
@UnsupportedAppUsage
public final ArrayList<Service> services = new ArrayList<Service>(0);
@UnsupportedAppUsage
public final ArrayList<Instrumentation> instrumentation = new ArrayList<Instrumentation>(0);
@UnsupportedAppUsage
public final ArrayList<String> requestedPermissions = new ArrayList<String>();
/** Permissions requested but not in the manifest. */
public final ArrayList<String> implicitPermissions = new ArrayList<>();
@UnsupportedAppUsage
public ArrayList<String> protectedBroadcasts;
......
}
```
3.2 Component
PackageParser
中的內部類。 這是一個抽象類,具體的子類為四大元件以及許可權。如Activity、Service、Permission
等
每個元件子類
都有一個info
成員變數,對應一個ComponentInfo
結構。 元件中真正的資料存放在info中。此外,每個元件可能還配置有一個或多個 IntentFilter
資訊,因此,內部有一個intents
集合,對應記憶體中 IntentInfo。
3.3 ComponentInfo
PackageParser中的內部類。 真正存放資料的地方。 ActivityInfo、ServiceInfo
等。
3.4 IntentInfo
PackageParser中的內部類。Component中有個 intents
成員集合,用來存放intent相關的資訊。如:ActivityIntentInfo、ServiceIntentInfo
和ProviderIntentInfo。
四、總結
靜態包的檔案形式,通過 PackageParser
解析最終得以轉變為記憶體中的資料結構 Package
,它包含了整個apk包的資訊,如 Application、Activity、Service等。至此,PMS就可以愉快的管理起來了!
五、感謝
http://liuwangshu.cn/framework/pms/5-packageparser.html
http://blog.csdn.net/yiranfeng/article/details/103961304
http://duanqz.github.io/2017-01-04-Package-Manage-Mechanism
- Android系統服務PMS apk安裝的過程(四) 完結
- Android系統服務PMS 包的解析過程(三)
- Android系統服務 AMS 啟動流程
- Binder 解析之 getService() 理解(五)
- Binder解析之 addService() 過程理解(四)
- Binder 解析之 Parcel 理解(三)
- Android系統啟動(四)-ServiceManager啟動
- Android系統啟動(二)- Zygote篇
- [重識 Java] Map集合-HashMap原始碼解析 我學廢了!
- [Matrix系列-3]: TracePlugin 之 FrameTrace 原始碼分析
- Android 10 和Android 11 適配採坑 實踐篇
- [Matrix系列-1]: matrix 啟動流程原始碼解析
- [Matrix系列-2]: TracePlugin 之 LooperAnrTrace原始碼分析
- [演算法題] 二分查詢之-愛吃香蕉的珂珂
- Android Q (10) 分割槽儲存 適配心得