android PMS 如何解析 APK
文章来源:https://blog.csdn.net/shift_wwx/article/details/80430259
前言:
PMS 是android 系统管理的核心,这一篇主要就是分析PMS 是如何解析APK 中的所有信息。请大神不吝指教~~
scanPackageLI()
PMS 的构造中会通过 scanDirTracedLI() 对各个指定的目录进行扫描:
private void scanDirTracedLI(File dir, final int parseFlags, int scanFlags, long currentTime) {Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + dir.getAbsolutePath() + "]");try {scanDirLI(dir, parseFlags, scanFlags, currentTime);} finally {Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);}}
private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {final File[] files = dir.listFiles();if (ArrayUtils.isEmpty(files)) { //不能是空目录Log.d(TAG, "No files in app dir " + dir);return;}if (DEBUG_PACKAGE_SCANNING) {Log.d(TAG, "Scanning app dir " + dir + " scanFlags=" + scanFlags+ " flags=0x" + Integer.toHexString(parseFlags));}ParallelPackageParser parallelPackageParser = new ParallelPackageParser(mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir,mParallelPackageParserCallback);// Submit files for parsing in parallelint fileCount = 0;for (File file : files) {final boolean isPackage = (isApkFile(file) || file.isDirectory()) //判断是否为应用文件&& !PackageInstallerService.isStageName(file.getName());if (!isPackage) {// Ignore entries which are not packagescontinue; //忽略非应用文件}parallelPackageParser.submit(file, parseFlags);fileCount++;}// Process results one by onefor (; fileCount > 0; fileCount--) {ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();Throwable throwable = parseResult.throwable;int errorCode = PackageManager.INSTALL_SUCCEEDED;if (throwable == null) {// Static shared libraries have synthetic package namesif (parseResult.pkg.applicationInfo.isStaticSharedLibrary()) {renameStaticSharedLibraryPackage(parseResult.pkg);}try {if (errorCode == PackageManager.INSTALL_SUCCEEDED) {scanPackageLI(parseResult.pkg, parseResult.scanFile, 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 appsif ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&errorCode == PackageManager.INSTALL_FAILED_INVALID_APK) {logCriticalInfo(Log.WARN,"Deleting invalid package at " + parseResult.scanFile);removeCodePathLI(parseResult.scanFile);}}parallelPackageParser.close();}
最终会调用到scanPackageLI():
private PackageParser.Package scanPackageLI(PackageParser.Package pkg, File scanFile,final int policyFlags, int scanFlags, long currentTime, @Nullable UserHandle user)throws PackageManagerException {// If the package has children and this is the first dive in the function// we scan the package with the SCAN_CHECK_ONLY flag set to see whether all// packages (parent and children) would be successfully scanned before the// actual scan since scanning mutates internal state and we want to atomically// install the package and its children.if ((scanFlags & SCAN_CHECK_ONLY) == 0) {if (pkg.childPackages != null && pkg.childPackages.size() > 0) {scanFlags |= SCAN_CHECK_ONLY;}} else {scanFlags &= ~SCAN_CHECK_ONLY;}// Scan the parentPackageParser.Package scannedPkg = scanPackageInternalLI(pkg, scanFile, policyFlags,scanFlags, currentTime, user);// Scan the childrenfinal int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;for (int i = 0; i < childCount; i++) {PackageParser.Package childPackage = pkg.childPackages.get(i);scanPackageInternalLI(childPackage, scanFile, policyFlags, scanFlags,currentTime, user);}if ((scanFlags & SCAN_CHECK_ONLY) != 0) {return scanPackageLI(pkg, scanFile, policyFlags, scanFlags, currentTime, user);}return scannedPkg;}
这里不做过多的解析,后面另一篇博文会详细分析PMS 的扫描过程。
这里需要知道的是scanPackageInternalLI() 最终会进入:
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,long currentTime, UserHandle user) throws PackageManagerException {if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);PackageParser pp = new PackageParser();pp.setSeparateProcesses(mSeparateProcesses);pp.setOnlyCoreApps(mOnlyCore);pp.setDisplayMetrics(mMetrics);pp.setCallback(mPackageParserCallback);if ((scanFlags & SCAN_TRUSTED_OVERLAY) != 0) {parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY;}Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");final PackageParser.Package pkg;try {pkg = pp.parsePackage(scanFile, parseFlags); //这里就是我们所需要看的解析接口} catch (PackageParserException e) {throw PackageManagerException.from(e);} finally {Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);}// Static shared libraries have synthetic package namesif (pkg.applicationInfo.isStaticSharedLibrary()) {renameStaticSharedLibraryPackage(pkg);}return scanPackageLI(pkg, scanFile, parseFlags, scanFlags, currentTime, user);}
parsePackage()
code 路径:frameworks/base/core/java/android/content/pm/PackageParser.java
public Package parsePackage(File packageFile, int flags, boolean useCaches)throws PackageParserException {Package parsed = useCaches ? getCachedResult(packageFile, flags) : null;if (parsed != null) {return parsed;}long parseTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;if (packageFile.isDirectory()) {parsed = parseClusterPackage(packageFile, flags); //解析下面所有的apk} else {parsed = parseMonolithicPackage(packageFile, flags); //解析指定的apk}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;}
上面代码中,如果参数是packageFile 是一个目录,就会调用parseClusterPackage(),否则调用parseMonolithicPackage() 来处理。对于关联的多个apk 会放到一个目录下,来看下parseClusterPackage():
private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {//获取应用目录的PackageLite 对象,这个对象中分开保存了目录下的核心应用名称以及其他非核心应用的名称final PackageLite lite = parseClusterPackageLite(packageDir, 0);if (mOnlyCoreApps && !lite.coreApp) { //如果没有核心应用就会给出exceptionthrow new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,"Not a coreApp: " + packageDir);}// Build the split dependency tree.SparseArray 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(); //需要AssetManager 对象final File baseApk = new File(lite.baseCodePath);//对于apk 进行分析,等到Package 对象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);parseSplitApk(pkg, i, splitAssets, flags);}}pkg.setCodePath(packageDir.getAbsolutePath());pkg.setUse32bitAbi(lite.use32bitAbi);return pkg;} finally {IoUtils.closeQuietly(assetLoader);}}
parseClusterPackage() 方法中先调用parseClusterPackageLite() 方法对目录下的apk 文件进行初步分析,主要是区别出核心应用和非核心应用。核心应用只有一个,非核心应用可以没有或者多个。非核心应用的作用是用来保存资源和代码。
接下来调用parseBaseApk() 方法对核心应用进行分析,并生成Package 对象,对非核心的应用调用parseSpliteApk() 方法来分析,分析的结果会放到前面的 Package 对象中。
parseBaseApk() 方法实际上是分析AndroidManifest.xml 文件。下面来分析下parseBaseApk():
private Package parseBaseApk(File apkFile, AssetManager assets, int flags)throws PackageParserException {final String apkPath = apkFile.getAbsolutePath();String volumeUuid = null;if (apkPath.startsWith(MNT_EXPAND)) {final int end = apkPath.indexOf('/', MNT_EXPAND.length());volumeUuid = apkPath.substring(MNT_EXPAND.length(), end);}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);parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);final String[] outError = new String[1];final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);if (pkg == null) {throw new PackageParserException(mParseError,apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);}pkg.setVolumeUuid(volumeUuid);pkg.setApplicationVolumeUuid(volumeUuid);pkg.setBaseCodePath(apkPath);pkg.setSignatures(null);return pkg;} catch (PackageParserException e) {throw e;} catch (Exception e) {throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,"Failed to read manifest from " + apkPath, e);} finally {IoUtils.closeQuietly(parser);}}
主要看XmlResourceParser 对象,通过:
parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
而这里的ANDROID_MANIFEST_FILENAME 为:
private static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
继续分析,最终会调用到
final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);
private Package parseBaseApk(String apkPath, Resources res, XmlResourceParser parser, int flags,String[] outError) throws XmlPullParserException, IOException {final String splitName;final String pkgName;try {Pair packageSplit = parsePackageSplitNames(parser, parser);pkgName = packageSplit.first;splitName = packageSplit.second;if (!TextUtils.isEmpty(splitName)) {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;}if (mCallback != null) {String[] overlayPaths = mCallback.getOverlayPaths(pkgName, apkPath);if (overlayPaths != null && overlayPaths.length > 0) {for (String overlayPath : overlayPaths) {res.getAssets().addOverlayPath(overlayPath);}}}final Package pkg = new Package(pkgName);TypedArray sa = res.obtainAttributes(parser,com.android.internal.R.styleable.AndroidManifest);pkg.mVersionCode = pkg.applicationInfo.versionCode = sa.getInteger(com.android.internal.R.styleable.AndroidManifest_versionCode, 0);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.coreApp = parser.getAttributeBooleanValue(null, "coreApp", false);sa.recycle();return parseBaseApkCommon(pkg, null, res, parser, flags, outError);}
这个函数是Package 对象创建的地方,最开始的手会对xml 中的manifest 项进行解析,如:
继续分析,最终函数会调用到parseBaseApkCommon(),这个方法的source code 太长,我们来分布解读。
parseBaseApkCommon()
第1步:
TypedArray sa = res.obtainAttributes(parser,com.android.internal.R.styleable.AndroidManifest);String str = sa.getNonConfigurationString(com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);if (str != null && str.length() > 0) {if ((flags & PARSE_IS_EPHEMERAL) != 0) {outError[0] = "sharedUserId not allowed in ephemeral application";mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID;return null;}String nameError = validateName(str, true, false);if (nameError != null && !"android".equals(pkg.packageName)) {outError[0] = " specifies bad sharedUserId name \""+ str + "\": " + nameError;mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID;return null;}pkg.mSharedUserId = str.intern();pkg.mSharedUserLabel = sa.getResourceId(com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0);}
这里是继续Package 对象的解析,获取到SharedUserId 和ShareUserLabel
第2步:
pkg.installLocation = sa.getInteger(com.android.internal.R.styleable.AndroidManifest_installLocation,PARSE_DEFAULT_INSTALL_LOCATION);pkg.applicationInfo.installLocation = pkg.installLocation;final int targetSandboxVersion = sa.getInteger(com.android.internal.R.styleable.AndroidManifest_targetSandboxVersion,PARSE_DEFAULT_TARGET_SANDBOX);pkg.applicationInfo.targetSandboxVersion = targetSandboxVersion;/* Set the global "forward lock" flag */if ((flags & PARSE_FORWARD_LOCK) != 0) {pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK;}/* Set the global "on SD card" flag */if ((flags & PARSE_EXTERNAL_STORAGE) != 0) {pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE;}if (sa.getBoolean(com.android.internal.R.styleable.AndroidManifest_isolatedSplits, false)) {pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING;}
这里一系列flag 的初始化地方,主要是针对applicationInfo
第3步:解析xml 中Application 项。
if (tagName.equals(TAG_APPLICATION)) {if (foundApp) {if (RIGID_PARSER) {outError[0] = " has more than one ";mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;return null;} else {Slog.w(TAG, " has more than one ");XmlUtils.skipCurrentTag(parser);continue;}}foundApp = true;if (!parseBaseApplication(pkg, res, parser, flags, outError)) {return null;}
}
第4步:解析permission 相关的信息。
} 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_USES_PERMISSION_SDK_M)|| tagName.equals(TAG_USES_PERMISSION_SDK_23)) {if (!parseUsesPermission(pkg, res, parser)) {return null;}}
第5步:解析use-configuration 项
} else if (tagName.equals(TAG_USES_CONFIGURATION)) {ConfigurationInfo cPref = new ConfigurationInfo();sa = res.obtainAttributes(parser,com.android.internal.R.styleable.AndroidManifestUsesConfiguration);cPref.reqTouchScreen = sa.getInt(com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqTouchScreen,Configuration.TOUCHSCREEN_UNDEFINED);cPref.reqKeyboardType = sa.getInt(com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqKeyboardType,Configuration.KEYBOARD_UNDEFINED);if (sa.getBoolean(com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqHardKeyboard,false)) {cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD;}cPref.reqNavigation = sa.getInt(com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqNavigation,Configuration.NAVIGATION_UNDEFINED);if (sa.getBoolean(com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqFiveWayNav,false)) {cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV;}sa.recycle();pkg.configPreferences = ArrayUtils.add(pkg.configPreferences, cPref);XmlUtils.skipCurrentTag(parser);}
第6步:解析uses-sdk 项
else if (tagName.equals(TAG_USES_SDK)) {if (SDK_VERSION > 0) {sa = res.obtainAttributes(parser,com.android.internal.R.styleable.AndroidManifestUsesSdk);int minVers = 1;String minCode = null;int targetVers = 0;String targetCode = null;TypedValue val = sa.peekValue(com.android.internal.R.styleable.AndroidManifestUsesSdk_minSdkVersion);if (val != null) {if (val.type == TypedValue.TYPE_STRING && val.string != null) {targetCode = minCode = val.string.toString();} else {// If it's not a string, it's an integer.targetVers = minVers = val.data;}}val = sa.peekValue(com.android.internal.R.styleable.AndroidManifestUsesSdk_targetSdkVersion);if (val != null) {if (val.type == TypedValue.TYPE_STRING && val.string != null) {targetCode = val.string.toString();if (minCode == null) {minCode = targetCode;}} else {// If it's not a string, it's an integer.targetVers = val.data;}}sa.recycle();final int minSdkVersion = PackageParser.computeMinSdkVersion(minVers, minCode,SDK_VERSION, SDK_CODENAMES, outError);if (minSdkVersion < 0) {mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;return null;}final int targetSdkVersion = PackageParser.computeTargetSdkVersion(targetVers,targetCode, SDK_VERSION, SDK_CODENAMES, outError);if (targetSdkVersion < 0) {mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;return null;}pkg.applicationInfo.minSdkVersion = minSdkVersion;pkg.applicationInfo.targetSdkVersion = targetSdkVersion;}XmlUtils.skipCurrentTag(parser);}
通过相应的计算获取在最终的minSdkVersion 和targetSdkVersion
第7步:解析protected_broadcast 项
else if (tagName.equals(TAG_PROTECTED_BROADCAST)) {sa = res.obtainAttributes(parser,com.android.internal.R.styleable.AndroidManifestProtectedBroadcast);// Note: don't allow this value to be a reference to a resource// that may change.String name = sa.getNonResourceString(com.android.internal.R.styleable.AndroidManifestProtectedBroadcast_name);sa.recycle();if (name != null && (flags&PARSE_IS_SYSTEM) != 0) {if (pkg.protectedBroadcasts == null) {pkg.protectedBroadcasts = new ArrayList();}if (!pkg.protectedBroadcasts.contains(name)) {pkg.protectedBroadcasts.add(name.intern());}}XmlUtils.skipCurrentTag(parser);}
至此,parseBaseApkCommon() 基本解析完,其中还省略了很多,例如key-sets 项、uses-feature项、feature-group项、supports-screens项、instrumentation项等等。详细可以阅读source code。
manifest 下面的子项上面基本解析完,下面来看下manifest 中applicaion 下面的子项,例如activity、receiver、service 等等都会注册在application 项里。
parseBaseApplication()
第1步:解析获取ApplicationInfo
final ApplicationInfo ai = owner.applicationInfo;
这里的owner 就是上面Package 对象,Package 里面的applicationInfo 就是在这里进一步解析。
其中需要注意的是:
if ((flags&PARSE_IS_SYSTEM) != 0) {if (sa.getBoolean(com.android.internal.R.styleable.AndroidManifestApplication_persistent,false)) {// Check if persistence is based on a feature being presentfinal String requiredFeature = sa.getNonResourceString(com.android.internal.R.styleable.AndroidManifestApplication_persistentWhenFeatureAvailable);if (requiredFeature == null || mCallback.hasFeature(requiredFeature)) {ai.flags |= ApplicationInfo.FLAG_PERSISTENT;}}}
如果不是系统应用,会解析persistent 项,并将其置到flag 里。
ai.processName = buildProcessName(ai.packageName, null, pname,flags, mSeparateProcesses, outError);
processName 是需要比较确认
第2步:activity
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;}owner.activities.add(a);}
第3步:receiver
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;}owner.receivers.add(a);}
第4步:service
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;}owner.services.add(s);}
第5步:provider
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);}
还有activity-alias、static-library、library、meta-data、uses-library等等,详细可以看source code
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
