首页 » 网站建设 » phpsystempdir技巧_工作日报 2022419 PackageManagerService 扫描APK目录

phpsystempdir技巧_工作日报 2022419 PackageManagerService 扫描APK目录

duote123 2024-12-18 0

扫一扫用手机浏览

文章目录 [+]

PackageManagerService的布局函数中调用了scanDirTracedLI方法来扫描某个目录的apk文件。

Android 10.0中,PKMS紧张扫描以下路径的APK信息:

phpsystempdir技巧_工作日报 2022419 PackageManagerService 扫描APK目录

/vendor/overlay

phpsystempdir技巧_工作日报 2022419 PackageManagerService 扫描APK目录
(图片来自网络侵删)

/product/overlay

/product_services/overlay

/odm/overlay

/oem/overlay

/system/framework

/system/priv-app

/system/app

/vendor/priv-app

/vendor/app

/odm/priv-app

/odm/app

/oem/app

/oem/priv-app

/product/priv-app

/product/app

/product_services/priv-app

/product_services/app

/product_services/priv-app

我们就scanDirTracedLI()为入口来进行剖析:

6.1 [ParallelPackageParser.java] scanDirTracedLI()

从下面的函数可见,scanDirTracedLI的入口很大略,首先加入了一些systtrace的日志追踪,然后调用scanDirLI()进行剖析

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);

}

}

6.2 [ParallelPackageParser.java] scanDirLI()

scanDirLI()中利用了ParallelPackageParser的工具,ParallelPackageParser是一个行列步队,我们这里手机所有系统的apk,然后从这些行列步队里面取出apk,再调用PackageParser 解析进行解析

private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime) {

final File[] files = scanDir.listFiles();

if (ArrayUtils.isEmpty(files)) {

Log.d(TAG, "No files in app dir " + scanDir);

return;

}

if (DEBUG_PACKAGE_SCANNING) {

Log.d(TAG, "Scanning app dir " + scanDir + " scanFlags=" + scanFlags

+ " flags=0x" + Integer.toHexString(parseFlags));

}

//parallelPackageParser是一个行列步队,网络系统 apk 文件,

//然后从这个行列步队里面一个个取出 apk ,调用 PackageParser 解析

try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(

mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir,

mParallelPackageParserCallback)) {

// Submit files for parsing in parallel

int fileCount = 0;

for (File file : files) {

//是Apk文件,或者是目录

final boolean isPackage = (isApkFile(file) || file.isDirectory())

&& !PackageInstallerService.isStageName(file.getName());

过滤掉非 apk 文件,如果不是则跳过连续扫描

if (!isPackage) {

// Ignore entries which are not packages

continue;

}

//把APK信息存入parallelPackageParser中的工具mQueue,PackageParser()函数赋给了行列步队中的pkg成员

//参考[6.3]

parallelPackageParser.submit(file, parseFlags);

fileCount++;

}

// Process results one by one

for (; fileCount > 0; fileCount--) {

//从parallelPackageParser中取出行列步队apk的信息

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 方法扫描一个特定的 apk 文件

// 该类的实例代表一个 APK 文件,以是它便是和 apk 文件对应的数据构造。

//参考[6.4]

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

//如果是非系统 apk 并且解析失落败

if ((scanFlags & SCAN_AS_SYSTEM) == 0 &&

errorCode != PackageManager.INSTALL_SUCCEEDED) {

logCriticalInfo(Log.WARN,

"Deleting invalid package at " + parseResult.scanFile);

// 非系统 Package 扫描失落败,删除文件

removeCodePathLI(parseResult.scanFile);

}

}

}

}

6.3 [ParallelPackageParser.java] submit

把扫描路径中的APK等内容,放入行列步队mQueue,并把parsePackage()赋给ParseResult,用于后面的调用

public void submit(File scanFile, int parseFlags) {

mService.submit(() -> {

ParseResult pr = new ParseResult();

Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parallel parsePackage [" + scanFile + "]");

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);

} catch (Throwable e) {

pr.throwable = e;

} finally {

Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);

}

try {

mQueue.put(pr);

} 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();

}

});

}

通过parsePackage 进行apk解析,如果传入的packageFile是目录,调用parseClusterPackage()解析,

如果传入的是APK文件,就调用parseMonolithicPackage()解析

public Package parsePackage(File packageFile, int flags, boolean useCaches)

throws PackageParserException {

...

if (packageFile.isDirectory()) {

//如果传入的packageFile是目录,调用parseClusterPackage()解析

parsed = parseClusterPackage(packageFile, flags);

} else {

//如果是APK文件,就调用parseMonolithicPackage()解析

parsed = parseMonolithicPackage(packageFile, flags);

}

...

return parsed;

}

我们先来看看parseClusterPackage()

浸染:解析给定目录中包含的所有apk,将它们视为单个包。
这还可以实行完全性检讨,比如须要相同的包名和版本代码、单个基本APK和惟一的拆分名称

首先通过parseClusterPackageLite()对目录下的apk文件进行初步剖析,紧张差异是核心运用还是非核心运用。
核心运用只有一个,非核心运用可以没有,或者多个,非核心运用的浸染紧张用来保存资源和代码。
然后对核心运用调用parseBaseApk剖析并天生Package。
对非核心运用调用parseSplitApk,剖析结果放在前面的Package工具中

private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {

//获取运用目录的PackageLite工具,这个工具分开保存了目录下的核心运用以及非核心运用的名称

final PackageLite lite = parseClusterPackageLite(packageDir, 0);

//如果lite中没有核心运用,退出

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);

//对核心运用解析

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.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);

}

}

再看parseMonolithicPackage(),它的浸染是解析给定的APK文件,将其作为单个单块包处理。

终极也是调用parseBaseApk()进行解析,我们接下来看下parseBaseApk()

public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {

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 {

//对核心运用解析

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);

}

}

parseBaseApk()紧张是对AndroidManifest.xml进行解析,解析后所有的信息放在Package工具中。

private Package parseBaseApk(File apkFile, AssetManager assets, int flags)

throws PackageParserException {

final String apkPath = apkFile.getAbsolutePath();

...

XmlResourceParser parser = null;

...

final int cookie = assets.findCookieForPath(apkPath);

if (cookie == 0) {

throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,

"Failed adding asset path: " + apkPath);

}

//得到一个 XML 资源解析工具,该工具解析的是 APK 中的 AndroidManifest.xml 文件。

parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);

final Resources res = new Resources(assets, mMetrics, null);

final String[] outError = new String[1];

//再调用重载函数parseBaseApk()终极到parseBaseApkCommon(),解析AndroidManifest.xml 后得到一个Package工具

final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);

...

pkg.setVolumeUuid(volumeUuid);

pkg.setApplicationVolumeUuid(volumeUuid);

pkg.setBaseCodePath(apkPath);

pkg.setSigningDetails(SigningDetails.UNKNOWN);

return pkg;

...

}

从AndroidManifest.xml中获取标署名,解析标签中的各个item的内容,存入Package工具中

例如获取标签"application"、"permission"

private Package parseBaseApkCommon(Package pkg, Set<String> acceptedTags, Resources res,

XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException,

IOException {

TypedArray sa = res.obtainAttributes(parser,

com.android.internal.R.styleable.AndroidManifest);

//拿到AndroidManifest.xml 中的sharedUserId, 一样平常情形下有“android.uid.system”等信息

String str = sa.getNonConfigurationString(

com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);

while ((type = parser.next()) != XmlPullParser.END_DOCUMENT

&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {

//从AndroidManifest.xml中获取标署名

String tagName = parser.getName();

//如果读到AndroidManifest.xml中的tag是"application",实行parseBaseApplication()进行解析

if (tagName.equals(TAG_APPLICATION)) {

if (foundApp) {

...

}

foundApp = true;

//解析"application"的信息,赋值给pkg

if (!parseBaseApplication(pkg, res, parser, flags, outError)) {

return null;

}

...

//如果标签是"permission"

else if (tagName.equals(TAG_PERMISSION)) {

//进行"permission"的解析

if (!parsePermission(pkg, res, parser, outError)) {

return null;

}

....

}

}

}

}

上面解析AndroidManifest.xml,

会得到"application"、"overlay"、"permission"、"uses-permission"等信息

我们下面就针对"application"进行展开剖析一下,进入parseBaseApplication()函数

private boolean parseBaseApplication(Package owner, Resources res,

XmlResourceParser parser, int flags, String[] outError)

while ((type = parser.next()) != XmlPullParser.END_DOCUMENT

&& (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {

//获取"application"子标签的标签内容

String tagName = parser.getName();

//如果标签是"activity"

if (tagName.equals("activity")) {

//解析Activity的信息,把activity加入Package工具

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);

} else if (tagName.equals("receiver")) {

//如果标签是"receiver",获取receiver信息,加入Package工具

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);

}else if (tagName.equals("service")) {

//如果标签是"service",获取service信息,加入Package工具

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);

}else if (tagName.equals("provider")) {

//如果标签是"provider",获取provider信息,加入Package工具

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);

}

...

}

}

在 PackageParser 扫描完一个 APK 后,此时系统已经根据该 APK 中 AndroidManifest.xml,创建了一个完全的 Package 工具,

下一步便是将该 Package 加入到系统中。
此时调用的函数便是其余一个 scanPackageChildLI

6.4 [PackageManagerService.java] scanPackageChildLI()

调用addForInitLI()在platform初始化时,把Package内容加入到内部数据构造

private PackageParser.Package scanPackageChildLI(PackageParser.Package pkg,

final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,

@Nullable UserHandle user)

throws PackageManagerException {

...

// Scan the parent

PackageParser.Package scannedPkg = addForInitLI(pkg, parseFlags,

scanFlags, currentTime, user);

// Scan the children

final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;

for (int i = 0; i < childCount; i++) {

PackageParser.Package childPackage = pkg.childPackages.get(i);

//在平台初始化期间向内部数据构造添加新包。

//在platform初始化时,把Package内容加入到内部数据构造,

addForInitLI(childPackage, parseFlags, scanFlags,

currentTime, user);

}

if ((scanFlags & SCAN_CHECK_ONLY) != 0) {

return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user);

}

}

在addForInitLI()中,进行安装包校验、署名检讨、apk更新等操作,把Package加入系统

private PackageParser.Package addForInitLI(PackageParser.Package pkg,

@ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,

@Nullable UserHandle user)

throws PackageManagerException {

// 判断系统运用是否须要更新

synchronized (mPackages) {

// 更新子运用

if (isSystemPkgUpdated) {

...

}

if (isSystemPkgBetter) {

// 更新安装包到 system 分区中

synchronized (mPackages) {

// just remove the loaded entries from package lists

mPackages.remove(pkgSetting.name);

}

...

// 创建安装参数 InstallArgs

final InstallArgs args = createInstallArgsForExisting(

pkgSetting.codePathString,

pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));

args.cleanUpResourcesLI();

synchronized (mPackages) {

mSettings.enableSystemPackageLPw(pkgSetting.name);

}

}

// 安装包校验

collectCertificatesLI(pkgSetting, pkg, forceCollect, skipVerify);

...

try (PackageFreezer freezer = freezePackage(pkg.packageName,

"scanPackageInternalLI")) {

// 如果两个 apk 署名不匹配,则调用 deletePackageLIF 方法打消 apk 文件及其数据

deletePackageLIF(pkg.packageName, null, true, null, 0, null, false, null);

}

...

// 更新系统 apk 程序

InstallArgs args = createInstallArgsForExisting(

pkgSetting.codePathString,

pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));

synchronized (mInstallLock) {

args.cleanUpResourcesLI();

}

}

// 如果新安装的系统APP 会被旧的APP 数据覆盖,以是须要隐蔽隐蔽系统运用程序,并重新扫描 /data/app 目录

if (shouldHideSystemApp) {

synchronized (mPackages) {

mSettings.disableSystemPackageLPw(pkg.packageName, true);

}

}

}

回顾一下全体APK的扫描过程:

按照core app >system app > other app 优先级扫描APK,解析AndroidManifest.xml文件,得到各个标签内容

解析XML文件得到的信息由 Package 保存。
从该类的成员变量可看出,和 Android 四大组件干系的信息分别由 activites、receivers、providers、services 保存。
由于一个 APK 可声明多个组件,因此 activites 和 receivers等均声明为 ArrayList。

在 PackageParser 扫描完一个 APK 后,此时系统已经根据该 APK 中 AndroidManifest.xml,创建了一个完全的 Package 工具,下一步便是将该 Package 加入到系统中

非系统 Package 扫描失落败,删除文件

标签:

相关文章

丹东,东北亚地区的重要门户与活力城市

丹东,这座位于中国东北边境的城市,拥有着得天独厚的地理位置和丰富的历史文化底蕴。作为东北亚地区的重要门户,丹东不仅是中国对外开放的...

网站建设 2024-12-23 阅读0 评论0

中文在JSP技术中的应用与发展

随着互联网技术的飞速发展,中文在Web开发中的应用日益广泛。JSP(JavaServer Pages)作为Java平台下的服务器端...

网站建设 2024-12-23 阅读0 评论0

乐陵网站建设,打造企业品牌新阵地

随着互联网的快速发展,网络已经成为企业展示形象、拓展市场的重要平台。乐陵,这座历史悠久的城市,正借助网站建设这股潮流,打造企业品牌...

网站建设 2024-12-23 阅读0 评论0

乐巍大数据,推动智慧城市建设的引擎力量

近年来,随着科技的飞速发展,大数据逐渐成为各行各业的重要驱动力。在我国,大数据产业得到了国家政策的扶持,市场规模不断扩大。其中,乐...

网站建设 2024-12-23 阅读0 评论0

东台网站建设,助力企业发展新引擎

随着互联网技术的飞速发展,网站已成为企业展示形象、拓展市场、提升品牌影响力的关键途径。东台作为我国江苏省的一个沿海城市,近年来在网...

网站建设 2024-12-23 阅读0 评论0