Android应用程序包扫描过程源码分析

深入探讨Android系统中PackageManagerService服务的启动源码,详细分析Apk文件的扫描过程,包括解析AndroidManifest.xml文件和更新安装包信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android服务之PackageManagerService启动源码分析中介绍了PackageManagerService服务的整个启动过程,启动过程相对来说较为简单,就是构造一个PackageManagerService对象,然后注册到ServiceManager进程中,只是PackageManagerService对象的构造过程比较复杂,任务比较繁重,在前面介绍PackageManagerService构造过程中,介绍到PackageManagerService会扫描系统指定目录下的Apk文件,本文就结合源代码详细介绍PackageManagerService对Apk的整个扫描过程。

  1. private void scanDirLI(File dir, int flags, int scanMode, long currentTime) {  
  2.     String[] files = dir.list();  
  3.     if (files == null) {  
  4.         Log.d(TAG, "No files in app dir " + dir);  
  5.         return;  
  6.     }  
  7.     int i;  
  8.     for (i=0; i<files.length; i++) {  
  9.         File file = new File(dir, files[i]);  
  10.         //如果不是apk文件,则跳过继续扫描   
  11.         if (!isPackageFilename(files[i])) {  
  12.             continue;  
  13.         }  
  14.         //解析扫描到的apk文件   
  15.         PackageParser.Package pkg = scanPackageLI(file,  
  16.                 flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime);  
  17.         //如果是系统分区内的无效安装包,则删除该apk文件   
  18.         if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 &&  
  19.                 mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) {  
  20.             Slog.w(TAG, "Cleaning up failed install of " + file);  
  21.             file.delete();  
  22.         }  
  23.     }  
  24. }  
private void scanDirLI(File dir, int flags, int scanMode, long currentTime) {
	String[] files = dir.list();
	if (files == null) {
		Log.d(TAG, "No files in app dir " + dir);
		return;
	}
	int i;
	for (i=0; i<files.length; i++) {
		File file = new File(dir, files[i]);
		//如果不是apk文件,则跳过继续扫描
		if (!isPackageFilename(files[i])) {
			continue;
		}
		//解析扫描到的apk文件
		PackageParser.Package pkg = scanPackageLI(file,
				flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime);
		//如果是系统分区内的无效安装包,则删除该apk文件
		if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
				mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) {
			Slog.w(TAG, "Cleaning up failed install of " + file);
			file.delete();
		}
	}
}
该函数用于扫描指定目录下的Apk文件,函数实现比较简单,就是遍历目录下的以.apk为后缀的安装包文件,然后调用另一个scanDirLI函数来对每一个Apk文件进行解析。扫描每个Apk包括两个步骤:

首先是解析Apk中的AndroidManifest.xml文件,然后是更新每一个安装包在PackageManagerService中的设置信息。

  1. private PackageParser.Package scanPackageLI(File scanFile,  
  2.         int parseFlags, int scanMode, long currentTime) {  
  3.     mLastScanError = PackageManager.INSTALL_SUCCEEDED;  
  4.     String scanPath = scanFile.getPath();  
  5.     parseFlags |= mDefParseFlags;  
  6.     //使用PackageParser对象来解析apk安装包   
  7.     PackageParser pp = new PackageParser(scanPath);  
  8.     pp.setSeparateProcesses(mSeparateProcesses);  
  9.     pp.setOnlyCoreApps(mOnlyCore);  
  10.     //解析当前扫描的apk文件,得到描述apk信息的PackageParser.Package对象   
  11.     final PackageParser.Package pkg = pp.parsePackage(scanFile,  
  12.             scanPath, mMetrics, parseFlags);  
  13.     if (pkg == null) {  
  14.         mLastScanError = pp.getParseError();  
  15.         return null;  
  16.     }  
  17.     // /data/app/.delrecord文件用于记录删除的安装包   
  18.     File delRecord = new File("/data/app/.delrecord");  
  19.     //如果当前apk文件位于"/system/preloadapp"目录下,并且安装包删除记录文件存在   
  20.     if(isPreloadApp(scanFile.getParent()) && delRecord.exists()){  
  21.         //如果当前apk安装包曾经安装过,但已经被删除,则直接返回该apk的描述对象   
  22.         if(isDeleteApp(pkg.packageName))return pkg;  
  23.     }  
private PackageParser.Package scanPackageLI(File scanFile,
		int parseFlags, int scanMode, long currentTime) {
	mLastScanError = PackageManager.INSTALL_SUCCEEDED;
	String scanPath = scanFile.getPath();
	parseFlags |= mDefParseFlags;
	//使用PackageParser对象来解析apk安装包
	PackageParser pp = new PackageParser(scanPath);
	pp.setSeparateProcesses(mSeparateProcesses);
	pp.setOnlyCoreApps(mOnlyCore);
	//解析当前扫描的apk文件,得到描述apk信息的PackageParser.Package对象
	final PackageParser.Package pkg = pp.parsePackage(scanFile,
			scanPath, mMetrics, parseFlags);
	if (pkg == null) {
		mLastScanError = pp.getParseError();
		return null;
	}
	// /data/app/.delrecord文件用于记录删除的安装包
	File delRecord = new File("/data/app/.delrecord");
	//如果当前apk文件位于"/system/preloadapp"目录下,并且安装包删除记录文件存在
	if(isPreloadApp(scanFile.getParent()) && delRecord.exists()){
		//如果当前apk安装包曾经安装过,但已经被删除,则直接返回该apk的描述对象
		if(isDeleteApp(pkg.packageName))return pkg;
	}
这部分内容就是对每一个Apk文件的扫描过程,使用PackageParser对象来解析每一个Apk拥有的AndroidManifest.xml文件。

  1. //更新系统中的安装包信息   
  2. PackageSetting ps = null;  
  3. PackageSetting updatedPkg;  
  4. synchronized (mPackages) {  
  5.     //mSettings.mRenamedPackages中保存了所有安装包的名字变更,以键值对的形式保存安装包新的包名和原始包名: newName-oldName   
  6.     String oldName = mSettings.mRenamedPackages.get(pkg.packageName);  
  7.     if (pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(oldName)) {  
  8.         //mSettings.mPackages中保存了所有安装包的设置信息,以键值对的形式保存每个apk包的包名和设置:name-PackageSetting,这里通过旧包名从mSettings.mPackages中取出该apk原始的PackageSetting对象   
  9.         ps = mSettings.peekPackageLPr(oldName);  
  10.     }  
  11.     //如果没有原始包设置PackageSetting,则使用apk的当前包名来获取其对应的PackageSetting   
  12.     if (ps == null) {  
  13.         ps = mSettings.peekPackageLPr(pkg.packageName);  
  14.     }       //mSettings.mDisabledSysPackages中保存了所有已替换的安装包的设置信息,以键值对的形式保存每个apk包的包名和设置:name-PackageSetting,使用包名从mSettings.mDisabledSysPackages中取得对应的PackageSetting对象   
  15.     updatedPkg = mSettings.getDisabledSystemPkgLPr(ps != null ? ps.name : pkg.packageName);  
  16. }  
  17. //如果是系统安装包   
  18. if (updatedPkg != null && (parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {  
  19.     //如果apk的路径已经更改   
  20.     if (ps != null && !ps.codePath.equals(scanFile)) {  
  21.         //判断当前apk版本号是否小于原始版本   
  22.         if (pkg.mVersionCode < ps.versionCode) {  
  23.             // 系统包已经是最新版本,且安装路径不匹配,忽略   
  24.             mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;  
  25.             return null;  
  26.         } else {  
  27.             //更新安装包到系统分区中   
  28.             synchronized (mPackages) {  
  29.                 // 从PackageManagerService的安装包列表中删除该包   
  30.                 mPackages.remove(ps.name);  
  31.             }  
  32.             //创建安装参数InstallArgs   
  33.             InstallArgs args = createInstallArgs(packageFlagsToInstallFlags(ps),  
  34.                     ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString);  
  35.             synchronized (mInstaller) {  
  36.                 //清空dex文件及安装包的挂载点   
  37.                 args.cleanUpResourcesLI();  
  38.             }  
  39.             synchronized (mPackages) {  
  40.                 mSettings.enableSystemPackageLPw(ps.name);  
  41.             }  
  42.         }  
  43.     }  
  44. }  
  45. if (updatedPkg != null) {  
  46.     parseFlags |= PackageParser.PARSE_IS_SYSTEM;  
  47. }  
  48. //安装包校验   
  49. if (!collectCertificatesLI(pp, ps, pkg, scanFile, parseFlags)) {  
  50.     Slog.w(TAG, "Failed verifying certificates for package:" + pkg.packageName);  
  51.     return null;  
  52. }  
  53. //先前安装过相同包名的apk,但现在作为系统apk来安装   
  54. boolean shouldHideSystemApp = false;  
  55. if (updatedPkg == null && ps != null  
  56.         && (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0 && !isSystemApp(ps)) {  
  57.      //apk签名匹配,如果失败,清空apk文件及其数据   
  58.     if (compareSignatures(ps.signatures.mSignatures, pkg.mSignatures)  
  59.             != PackageManager.SIGNATURE_MATCH) {  
  60.         deletePackageLI(pkg.packageName, true0nullfalse);  
  61.         ps = null;  
  62.     } else {//签名匹配成功   
  63.          //如果新增的系统apk版本低于先前安装的apk版本   
  64.         if (pkg.mVersionCode < ps.versionCode) {  
  65.             shouldHideSystemApp = true;  
  66.         } else {  
  67.             //更新系统apk程序,但保持应用数据不变   
  68.             InstallArgs args = createInstallArgs(packageFlagsToInstallFlags(ps),  
  69.                     ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString);  
  70.             synchronized (mInstaller) {  
  71.                 args.cleanUpResourcesLI();  
  72.             }  
  73.         }  
  74.     }  
  75. }  
  76. if (ps != null && !ps.codePath.equals(ps.resourcePath)) {  
  77.     parseFlags |= PackageParser.PARSE_FORWARD_LOCK;  
  78. }  
  79. String codePath = null;  
  80. String resPath = null;  
  81. if ((parseFlags & PackageParser.PARSE_FORWARD_LOCK) != 0) {  
  82.     if (ps != null && ps.resourcePathString != null) {  
  83.         resPath = ps.resourcePathString;  
  84.     } else {  
  85.         // Should not happen at all. Just log an error.   
  86.         Slog.e(TAG, "Resource path not set for pkg : " + pkg.packageName);  
  87.     }  
  88. else {  
  89.     resPath = pkg.mScanPath;  
  90. }  
  91. codePath = pkg.mScanPath;  
  92. // Set application objects path explicitly.   
  93. setApplicationInfoPaths(pkg, codePath, resPath);  
  94. //调用另一个scanPackageLI函数   
  95. PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode  
  96.         | SCAN_UPDATE_SIGNATURE, currentTime);  
  97. if (shouldHideSystemApp) {  
  98.     synchronized (mPackages) {  
  99.         grantPermissionsLPw(pkg, true);  
  100.         mSettings.disableSystemPackageLPw(pkg.packageName);  
  101.     }  
  102. }  
  103. return scannedPkg;  
	//更新系统中的安装包信息
	PackageSetting ps = null;
	PackageSetting updatedPkg;
	synchronized (mPackages) {
		//mSettings.mRenamedPackages中保存了所有安装包的名字变更,以键值对的形式保存安装包新的包名和原始包名: newName-oldName
		String oldName = mSettings.mRenamedPackages.get(pkg.packageName);
		if (pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(oldName)) {
			//mSettings.mPackages中保存了所有安装包的设置信息,以键值对的形式保存每个apk包的包名和设置:name-PackageSetting,这里通过旧包名从mSettings.mPackages中取出该apk原始的PackageSetting对象
			ps = mSettings.peekPackageLPr(oldName);
		}
		//如果没有原始包设置PackageSetting,则使用apk的当前包名来获取其对应的PackageSetting
		if (ps == null) {
			ps = mSettings.peekPackageLPr(pkg.packageName);
		}		//mSettings.mDisabledSysPackages中保存了所有已替换的安装包的设置信息,以键值对的形式保存每个apk包的包名和设置:name-PackageSetting,使用包名从mSettings.mDisabledSysPackages中取得对应的PackageSetting对象
		updatedPkg = mSettings.getDisabledSystemPkgLPr(ps != null ? ps.name : pkg.packageName);
	}
	//如果是系统安装包
	if (updatedPkg != null && (parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
		//如果apk的路径已经更改
		if (ps != null && !ps.codePath.equals(scanFile)) {
			//判断当前apk版本号是否小于原始版本
			if (pkg.mVersionCode < ps.versionCode) {
				// 系统包已经是最新版本,且安装路径不匹配,忽略
				mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
				return null;
			} else {
				//更新安装包到系统分区中
				synchronized (mPackages) {
					// 从PackageManagerService的安装包列表中删除该包
					mPackages.remove(ps.name);
				}
				//创建安装参数InstallArgs
				InstallArgs args = createInstallArgs(packageFlagsToInstallFlags(ps),
						ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString);
				synchronized (mInstaller) {
					//清空dex文件及安装包的挂载点
					args.cleanUpResourcesLI();
				}
				synchronized (mPackages) {
					mSettings.enableSystemPackageLPw(ps.name);
				}
			}
		}
	}
	if (updatedPkg != null) {
		parseFlags |= PackageParser.PARSE_IS_SYSTEM;
	}
	//安装包校验
	if (!collectCertificatesLI(pp, ps, pkg, scanFile, parseFlags)) {
		Slog.w(TAG, "Failed verifying certificates for package:" + pkg.packageName);
		return null;
	}
	//先前安装过相同包名的apk,但现在作为系统apk来安装
	boolean shouldHideSystemApp = false;
	if (updatedPkg == null && ps != null
			&& (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0 && !isSystemApp(ps)) {
		 //apk签名匹配,如果失败,清空apk文件及其数据
		if (compareSignatures(ps.signatures.mSignatures, pkg.mSignatures)
				!= PackageManager.SIGNATURE_MATCH) {
			deletePackageLI(pkg.packageName, true, 0, null, false);
			ps = null;
		} else {//签名匹配成功
			 //如果新增的系统apk版本低于先前安装的apk版本
			if (pkg.mVersionCode < ps.versionCode) {
				shouldHideSystemApp = true;
			} else {
				//更新系统apk程序,但保持应用数据不变
				InstallArgs args = createInstallArgs(packageFlagsToInstallFlags(ps),
						ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString);
				synchronized (mInstaller) {
					args.cleanUpResourcesLI();
				}
			}
		}
	}
	if (ps != null && !ps.codePath.equals(ps.resourcePath)) {
		parseFlags |= PackageParser.PARSE_FORWARD_LOCK;
	}
	String codePath = null;
	String resPath = null;
	if ((parseFlags & PackageParser.PARSE_FORWARD_LOCK) != 0) {
		if (ps != null && ps.resourcePathString != null) {
			resPath = ps.resourcePathString;
		} else {
			// Should not happen at all. Just log an error.
			Slog.e(TAG, "Resource path not set for pkg : " + pkg.packageName);
		}
	} else {
		resPath = pkg.mScanPath;
	}
	codePath = pkg.mScanPath;
	// Set application objects path explicitly.
	setApplicationInfoPaths(pkg, codePath, resPath);
	//调用另一个scanPackageLI函数
	PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode
			| SCAN_UPDATE_SIGNATURE, currentTime);
	if (shouldHideSystemApp) {
		synchronized (mPackages) {
			grantPermissionsLPw(pkg, true);
			mSettings.disableSystemPackageLPw(pkg.packageName);
		}
	}
	return scannedPkg;
}
这部分是Apk安装信息的更新过程。Apk文件的扫描过程就是对Apk包中的AndroidManifest文件的解析过程

  1. public Package parsePackage(File sourceFile, String destCodePath,  
  2.         DisplayMetrics metrics, int flags) {  
  3.     mParseError = PackageManager.INSTALL_SUCCEEDED;  
  4.     mArchiveSourcePath = sourceFile.getPath();  
  5.     if (!sourceFile.isFile()) {  
  6.         Slog.w(TAG, "Skipping dir: " + mArchiveSourcePath);  
  7.         mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;  
  8.         return null;  
  9.     }  
  10.     if (!isPackageFilename(sourceFile.getName())  
  11.             && (flags&PARSE_MUST_BE_APK) != 0) {  
  12.         if ((flags&PARSE_IS_SYSTEM) == 0) {  
  13.             Slog.w(TAG, "Skipping non-package file: " + mArchiveSourcePath);  
  14.         }  
  15.         mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;  
  16.         return null;  
  17.     }  
  18.     XmlResourceParser parser = null;  
  19.     AssetManager assmgr = null;  
  20.     Resources res = null;  
  21.     boolean assetError = true;  
  22.     try {  
  23.         //创建一个资源管理器对象   
  24.         assmgr = new AssetManager();  
  25.         int cookie = assmgr.addAssetPath(mArchiveSourcePath);  
  26.         if (cookie != 0) {  
  27.             res = new Resources(assmgr, metrics, null);  
  28.             assmgr.setConfiguration(00null0000000000000,  
  29.                     Build.VERSION.RESOURCES_SDK_INT);  
  30.             //使用XML解析器打开AndroidManifest.xml文件   
  31.             parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);  
  32.             assetError = false;  
  33.         } else {  
  34.             Slog.w(TAG, "Failed adding asset path:"+mArchiveSourcePath);  
  35.         }  
  36.     } catch (Exception e) {  
  37.         Slog.w(TAG, "Unable to read AndroidManifest.xml of "  
  38.                 + mArchiveSourcePath, e);  
  39.     }  
  40.     if (assetError) {  
  41.         if (assmgr != null) assmgr.close();  
  42.         mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;  
  43.         return null;  
  44.     }  
  45.     String[] errorText = new String[1];  
  46.     Package pkg = null;  
  47.     Exception errorException = null;  
  48.     try {  
  49.         //解析AndroidManifest.xml文件,得到Package对象   
  50.         pkg = parsePackage(res, parser, flags, errorText);  
  51.     } catch (Exception e) {  
  52.         errorException = e;  
  53.         mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;  
  54.     }  
  55.     if (pkg == null) {  
  56.         if (!mOnlyCoreApps || mParseError != PackageManager.INSTALL_SUCCEEDED) {  
  57.             if (errorException != null) {  
  58.                 Slog.w(TAG, mArchiveSourcePath, errorException);  
  59.             } else {  
  60.                 Slog.w(TAG, mArchiveSourcePath + " (at "  
  61.                         + parser.getPositionDescription()  
  62.                         + "): " + errorText[0]);  
  63.             }  
  64.             if (mParseError == PackageManager.INSTALL_SUCCEEDED) {  
  65.                 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;  
  66.             }  
  67.         }  
  68.         parser.close();  
  69.         assmgr.close();  
  70.         return null;  
  71.     }  
  72.     parser.close();  
  73.     assmgr.close();  
  74.     pkg.mPath = destCodePath;  
  75.     pkg.mScanPath = mArchiveSourcePath;  
  76.     pkg.mSignatures = null;  
  77.     return pkg;  
  78. }  
public Package parsePackage(File sourceFile, String destCodePath,
		DisplayMetrics metrics, int flags) {
	mParseError = PackageManager.INSTALL_SUCCEEDED;
	mArchiveSourcePath = sourceFile.getPath();
	if (!sourceFile.isFile()) {
		Slog.w(TAG, "Skipping dir: " + mArchiveSourcePath);
		mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
		return null;
	}
	if (!isPackageFilename(sourceFile.getName())
			&& (flags&PARSE_MUST_BE_APK) != 0) {
		if ((flags&PARSE_IS_SYSTEM) == 0) {
			Slog.w(TAG, "Skipping non-package file: " + mArchiveSourcePath);
		}
		mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
		return null;
	}
	XmlResourceParser parser = null;
	AssetManager assmgr = null;
	Resources res = null;
	boolean assetError = true;
	try {
		//创建一个资源管理器对象
		assmgr = new AssetManager();
		int cookie = assmgr.addAssetPath(mArchiveSourcePath);
		if (cookie != 0) {
			res = new Resources(assmgr, metrics, null);
			assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
					Build.VERSION.RESOURCES_SDK_INT);
			//使用XML解析器打开AndroidManifest.xml文件
			parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
			assetError = false;
		} else {
			Slog.w(TAG, "Failed adding asset path:"+mArchiveSourcePath);
		}
	} catch (Exception e) {
		Slog.w(TAG, "Unable to read AndroidManifest.xml of "
				+ mArchiveSourcePath, e);
	}
	if (assetError) {
		if (assmgr != null) assmgr.close();
		mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
		return null;
	}
	String[] errorText = new String[1];
	Package pkg = null;
	Exception errorException = null;
	try {
		//解析AndroidManifest.xml文件,得到Package对象
		pkg = parsePackage(res, parser, flags, errorText);
	} catch (Exception e) {
		errorException = e;
		mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
	}
	if (pkg == null) {
		if (!mOnlyCoreApps || mParseError != PackageManager.INSTALL_SUCCEEDED) {
			if (errorException != null) {
				Slog.w(TAG, mArchiveSourcePath, errorException);
			} else {
				Slog.w(TAG, mArchiveSourcePath + " (at "
						+ parser.getPositionDescription()
						+ "): " + errorText[0]);
			}
			if (mParseError == PackageManager.INSTALL_SUCCEEDED) {
				mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
			}
		}
		parser.close();
		assmgr.close();
		return null;
	}
	parser.close();
	assmgr.close();
	pkg.mPath = destCodePath;
	pkg.mScanPath = mArchiveSourcePath;
	pkg.mSignatures = null;
	return pkg;
}
该函数根据Apk包路径创建一个对应的资源管理器对象AssetManager,通过该对象来访问Apk包中的资源信息,包括AndroidManifest.xml文件。

  1. parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);  
parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
通过资源管理器对象打开AndroidManifest.xml文件并且得到XML解析器。接着调用parsePackage函数开始解析AndroidManifest文件。在了解整个解析过程前,先认识一下Android定义的一些用于管理Apk信息的数据结构。

Package用于描述一个Apk的信息,一个Apk应用程序可能包括一系列的Activity,Service,Provider等组件,在Package中同样定义了对应的数据结构来保存这些组件信息。

这里不在沾上整个解析过程的源代码,只介绍解析过程使用的方法及数据结构。在

frameworks/base/core/res/res/values/attrs_manifest.xml文件中定义了AndroidManifest文件中的各个标签或属性


通过遍历AndroidManifest文件,并匹配对应的标签来读取指定标签的属性值,各个标签属性读取方式如下:

  1. TypedArray sa = res.obtainAttributes(attrs,com.android.internal.R.styleable.AndroidManifest);  
  2. pkg.mVersionCode = sa.getInteger(com.android.internal.R.styleable.AndroidManifest_versionCode, 0);  
  3. pkg.mVersionName = sa.getNonConfigurationString(com.android.internal.R.styleable.AndroidManifest_versionName, 0);  
TypedArray sa = res.obtainAttributes(attrs,com.android.internal.R.styleable.AndroidManifest);
pkg.mVersionCode = sa.getInteger(com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
pkg.mVersionName = sa.getNonConfigurationString(com.android.internal.R.styleable.AndroidManifest_versionName, 0);
res是Resources对象,通过Resources对象的obtainAttributes方法来读取指定标签的属性内容,比如这里读取AndroidManifest标签的属性


最后根据属性名称从TypedArray中读取对应属性值,各个标签下的属性名称在frameworks/base/core/res/res/values/attrs_manifest.xml文件已经定义。下表为各个标签对应的解析函数:


 

 

 

application

parseApplication()

activity

parseActivity()

intent-filter

parseIntent()

meta-data

parseMetaData()

receiver

parseActivity()

service

parseService()

provider

parseProvider()

activity-alias

parseActivityAlias()

meta-data

parseMetaData()

uses-library

parseApplication()

uses-package

parseApplication()

permission-group

parsePermissionGroup()

permission

parsePermission()

permission-tree

parsePermissionTree()

uses-permission

parsePackage()

uses-configuration

parsePackage()

uses-feature

parsePackage()

uses-sdk

parsePackage()

supports-screens

parsePackage()

protected-broadcast

parsePackage()

instrumentation

parseInstrumentation()

original-package

parsePackage()

adopt-permissions

parsePackage()

uses-gl-texture

parsePackage()

compatible-screens

parsePackage()

eat-comment

parsePackage()


在Package类中为每个标签定义了对应的数据结构来保存数据信息,解析XML的目的就是读取AndroidManifest文件中的内容到Package对象中。

Package成员

定义的属性

XML中的标签

mVersionCode

AndroidManifest

versionCode

versionName

sharedUserId

sharedUserLabel

installLocation

 

 

manifest

mVersionName

mSharedUserId

mSharedUserLabel

installLocation

applicationInfo

AndroidManifestApplication

application

创建PermissionGroup对象并将信息保存该对象的成员Info中,同时将创建的PermissionGroup对象添加到permissionGroups中

AndroidManifestPermissionGroup

 

permission-group

创建Permission对象并将信息保存该对象的成员Info中,同时将创建的Permission对象添加到permissions中

AndroidManifestPermission

permission

创建Permission对象并将信息保存该对象的成员Info中,同时将创建的Permission对象添加到permissions中

AndroidManifestPermissionTree

 

permission-tree

requestedPermissions 保存权限名称

AndroidManifestUsesPermission

 

uses-permission

requestedPermissionsRequired

创建ConfigurationInfo对象,并将信息保存到该对象中,同时将该对象添加到configPreferences中

AndroidManifestUsesConfiguration

uses-configuration

创建FeatureInfo对象,并将信息保存到该对象中,同时将该对象添加到reqFeatures中

AndroidManifestUsesFeature

uses-feature

applicationInfo.targetSdkVersion

AndroidManifestUsesSdk

uses-sdk

applicationInfo

AndroidManifestSupportsScreens

supports-screens

protectedBroadcasts

AndroidManifestProtectedBroadcast

protected-broadcast

instrumentation

AndroidManifestInstrumentation

instrumentation

mOriginalPackages

AndroidManifestOriginalPackage

original-package

mAdoptPermissions

AndroidManifestOriginalPackage

adopt-permissions

mAppMetaData

AndroidManifestMetaData

meta-data

usesLibraries

usesOptionalLibraries

AndroidManifestUsesLibrary

uses-library

activities

AndroidManifestActivityAlias

activity-alias

AndroidManifestActivity

activity

receivers

AndroidManifestActivity

receiver

services

AndroidManifestService

service

providers

 

provider



<think>我们正在讨论的是Android应用中使用生物识别(括人脸识别)的权限问题。用户已经知道需要在build.gradle中添加依赖和在AndroidManifest.xml中添加权限,但想知道是否还需要额外的用户授权(即运行时权限)。 根据Android的生物识别框架设计: 1. **USE_BIOMETRIC权限**:这是一个普通权限(normal permission),在AndroidManifest.xml中声明后,系统会在安装时自动授予,不需要运行时请求[^1][^3]。 2. **用户授权**:虽然不需要运行时权限,但每次使用生物识别功能时,都需要通过系统的BiometricPrompt对话框与用户交互,用户必须主动进行生物特征验证(如按压指纹或面部扫描)才能完成认证[^4][^5]。 因此,答案是:**不需要额外的运行时权限授权(因为USE_BIOMETRIC是普通权限),但每次使用生物识别功能时,必须通过系统提供的BiometricPrompt界面获取用户的实时生物特征验证**。 ### 详细解释: #### 1. 权限配置 - 在`AndroidManifest.xml`中添加: ```xml <uses-permission android:name="android.permission.USE_BIOMETRIC" /> ``` 此权限级别为`normal`,安装即授予[^3]。 #### 2. 依赖配置 - 在`build.gradle`中添加Jetpack Biometric库: ```groovy dependencies { implementation 'androidx.biometric:biometric:1.2.0-alpha04' // 推荐使用最新版本 } ``` 该库兼容Android 6.0+设备,并提供统一的BiometricPrompt API[^1][^2]。 #### 3. 用户交互流程 当应用调用`BiometricPrompt.authenticate()`时: 1. 系统显示认证对话框(不可定制UI,保证安全)[^5]。 2. **用户必须主动执行生物特征扫描**(如指纹按压或面部识别)。 3. 认证结果通过回调返回应用: ```kotlin val promptInfo = BiometricPrompt.PromptInfo.Builder() .setTitle("生物识别登录") .setNegativeButtonText("取消") .build() biometricPrompt.authenticate(promptInfo) ``` 4. 若用户拒绝或认证失败,应用无法绕过此步骤[^4][^5]。 #### 4. 安全等级控制 开发者可指定认证强度: ```java // 要求强生物特征(如3D人脸或指纹) .setAllowedAuthenticators(BIOMETRIC_STRONG) // 或允许设备凭证(PIN/图案/密码) .setAllowedAuthenticators(BIOMETRIC_WEAK or DEVICE_CREDENTIAL) ``` 不同等级对应不同的安全要求[^4]。 ### 总结流程: ```mermaid graph LR A[应用声明USE_BIOMETRIC权限] --> B[集成Jetpack Biometric库] B --> C[创建BiometricPrompt实例] C --> D[调用authenticate显示系统对话框] D --> E[用户生物特征验证] E --> F{验证结果} F -->|成功| G[执行应用逻辑] F -->|失败| H[提示错误] ``` > **关键结论**:USE_BIOMETRIC权限只需声明一次,但每次生物识别操作都需要用户实时验证,这是Android生物识别框架的核心安全机制[^4][^5]。 §§相关问题§§ 1. 如何在Android中检测设备是否支持人脸识别? 2. BiometricPrompt支持哪些类型的生物识别技术? 3. 应用能否自定义生物识别认证对话框的UI? 4. 生物识别模板在Android系统中是如何存储的? 5. 如何处理生物识别认证过程中的错误码? [^1]: 引用1 [^2]: 引用2 [^3]: 引用3 [^4]: 引用4 [^5]: 引用5</think>是的,即使应用已在 `build.gradle` 中配置了生物识别依赖并在 `AndroidManifest.xml` 中添加了 `USE_BIOMETRIC` 权限,**仍需用户明确授权才能使用生物识别功能**。以下是详细说明: ### 权限与授权流程解析 1. **静态权限声明(安装时)** 在 `AndroidManifest.xml` 中添加的权限仅表示应用声明了能力: ```xml <uses-permission android:name="android.permission.USE_BIOMETRIC" /> ``` 此权限级别为 `normal`[^3],安装时自动授予,**不涉及用户交互**。 2. **动态用户授权(运行时)** 实际使用生物识别功能时,必须通过系统弹窗获取用户实时授权: ```kotlin biometricPrompt.authenticate(promptInfo) // 触发系统级生物识别弹窗 ``` - 用户必须在弹窗中主动执行生物识别操作(如扫描指纹/人脸) - 用户可随时通过弹窗的"取消"按钮拒绝授权[^5] ### 完整授权流程 ```mermaid graph TD A[声明USE_BIOMETRIC权限] --> B[集成Jetpack Biometric库] B --> C[初始化BiometricPrompt] C --> D[调用authenticate方法] D --> E{系统弹窗} E -->|用户执行生物识别| F[认证成功] E -->|用户取消/失败| G[认证终止] ``` ### 关键限制说明 1. **不可绕过系统弹窗** 所有生物识别操作**必须**通过 `BiometricPrompt` 系统弹窗完成[^4][^5],开发者无法自定义或跳过此界面。 2. **授权时效性** 每次生物识别操作都需要实时授权: - 单次认证:每次调用 `authenticate()` 都需要新授权 - 持续认证:通过 `setConfirmationRequired(false)` 可减少弹窗频率[^4],但首次仍需授权 3. **安全等级控制** 开发者可指定认证强度,但用户始终拥有最终控制权: ```java // 用户可能拒绝强认证,转而使用密码 .setAllowedAuthenticators(BIOMETRIC_STRONG | DEVICE_CREDENTIAL) [^4] ``` ### 实际代码验证 当用户未授权时,回调会触发错误: ```kotlin override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { when (errorCode) { BiometricPrompt.ERROR_USER_CANCELED -> // 用户主动取消 BiometricPrompt.ERROR_NEGATIVE_BUTTON -> // 用户点击"使用密码" } } [^5] ``` ### 总结 | 阶段 | 是否需要用户操作 | 实现方式 | |------------|------------------|------------------------------| | 权限声明 | ❌ 自动授予 | AndroidManifest.xml | | 功能使用 | ✅ 必须授权 | BiometricPrompt 系统弹窗 | | 持续认证 | ✅ 首次必须授权 | setConfirmationRequired(false) | > 因此,**`USE_BIOMETRIC` 权限仅声明应用能力,实际生物识别操作仍需用户通过系统弹窗实时授权**。这是Android生物识别框架的核心安全设计[^4][^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值