当前位置: 代码迷 >> Android >> Android应用程序包扫描历程源码分析
  详细解决方案

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

热度:21   发布时间:2016-04-28 08:08:33.0
Android应用程序包扫描过程源码分析

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

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中的设置信息。

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文件。

	//更新系统中的安装包信息	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文件的解析过程

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文件。

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文件,并匹配对应的标签来读取指定标签的属性值,各个标签属性读取方式如下:

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




  相关解决方案