今天简单讲解一下PackageInstaller
文件路径:
packages/apps/PackageInstaller
frameworks/base/core/java/android/content/pm&res
下面开始讲解:
首先,我们说一下安装apk的几种方式,整体上可以分为2类,一类是有界面安装,一类是无界面安装。无界面安装分为内置apk开机安装和命令安装,命令安装又分为两类,一类电脑安装也就是adb命令,另一类是手机安装也就是pm命令。今天我们主要介绍有界面安装。
当然,我们从这个安装界面说起,这个界面是那个呢?就是PackageInstallerActivity这个acitvity。它是如何启动起来的呢?我们去看看它在AndroidManifest是如何定义的
- <activity android:name=".PackageInstallerActivity"
- android:configChanges="orientation|keyboardHidden|screenSize"
- android:excludeFromRecents="true"
- android:screenOrientation="unspecified">
- <intent-filter>
- <action android:name="android.intent.action.VIEW" />
- <action android:name="android.intent.action.INSTALL_PACKAGE" />
- <category android:name="android.intent.category.DEFAULT" />
- <data android:scheme="content" />
- <data android:scheme="file" />
- <data android:mimeType="application/vnd.android.package-archive" />
- intent-filter>
- <intent-filter>
- <action android:name="android.intent.action.INSTALL_PACKAGE" />
- <category android:name="android.intent.category.DEFAULT" />
- <data android:scheme="content" />
- <data android:scheme="file" />
- intent-filter>
- activity>
很明显了,我们可以通过android.intent.action.INSTALL_PACKAGE这个action启动,也可以通过android.intent.action.VIEW这个action加上"application/vnd.android.package-archive"这个type启动,当然不加这个type也能启动,但是会找到很多这样的activity哦。另外,通过类名或包名启动也未尝不可的。所以,大部分启动是这样的
- String apkFileString = Environment.getExternalStorageDirectory().getAbsolutePath()+"/.../packageName.pac";
- File apkFile = new File(apkFileString);
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
- mContext.startActivity(intent);
这里我们传进去一个数据就是pakFile的Uri,然后我们去PackageInstallerActivity的onCreate中看看
- final Intent intent = getIntent();
- mPackageURI = intent.getData();
- mPm = getPackageManager();
- mPkgInfo = PackageUtil.getPackageInfo(mPackageURI);
获取到,我们刚才传进来的apkFile的Uri给了mPackageURI,接着获取到PackageManager,然后生成一个mPkgInfo也就是PackageParser.Package,这个很重要。我们看看PackageParser.Package是如何生成的,PackageParser.Package里面都包含了什么东西。那我们就要去PackageUtil.getPackageInfo中了
- public static PackageParser.Package getPackageInfo(Uri packageURI) {
- final String archiveFilePath = packageURI.getPath();
- PackageParser packageParser = new PackageParser(archiveFilePath);
- File sourceFile = new File(archiveFilePath);
- DisplayMetrics metrics = new DisplayMetrics();
- metrics.setToDefaults();
- PackageParser.Package pkg = packageParser.parsePackage(sourceFile,
- archiveFilePath, metrics, 0);
- // Nuke the parser reference.
- packageParser = null;
- return pkg;
- }
生成一个Package解析器,通过这个解析器来获取到PackageParser.Package中需要的数据,生成一个PackageParser.Package。我们看看PackageParser.parsePackage是如何生成一个PackageParser.Package的,这里传进去四个参数,一个Source File,apk文件,一个apk路径,一个屏幕信息,最后一个0,具体做什么的,进去之后就能明白了
- 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) {
- // We expect to have non-.apk files in the system dir,
- // so don't warn about them.
- Slog.w(TAG, "Skipping non-package file: " + mArchiveSourcePath);
- }
- mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
- return null;
- }
- if (DEBUG_JAR)
- Slog.d(TAG, "Scanning package: " + mArchiveSourcePath);
- 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);
- 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 {
- // XXXX todo: need to figure out correct configuration.
- pkg = parsePackage(res, parser, flags, errorText);
- } catch (Exception e) {
- errorException = e;
- mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
- }
- if (pkg == null) {
- // If we are only parsing core apps, then a null with INSTALL_SUCCEEDED
- // just means to skip this app so don't make a fuss about it.
- 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();
- // Set code and resource paths
- pkg.mPath = destCodePath;
- pkg.mScanPath = mArchiveSourcePath;
- //pkg.applicationInfo.sourceDir = destCodePath;
- //pkg.applicationInfo.publicSourceDir = destRes;
- pkg.mSignatures = null;
- return pkg;
- }
首先sourceFile.isFile()判断一下是不是文件,如果不是,返回;接着isPackageFilename(sourceFile.getName())判断是不是apk文件,如果不是,返回;接着去获取三个关键变量,也就是
- XmlResourceParser parser = null;
- AssetManager assmgr = null;
- Resources res = null;
这三个是什么呢?这里简单说一下,AssetManager资产管理器,用来管理包中获取到的资源
- assmgr = new AssetManager();
- int cookie = assmgr.addAssetPath(mArchiveSourcePath);
通过addAssetPath可以获取到唯一标识该apk包资产的关键字cookie,也就是通过cookie可以找到该包的资源信息。Resources就是资源了,包括图片,color,xml等资源
- res = new Resources(assmgr, metrics, null);
当然Resources信息也是通过AssetManager获取到的。XmlResourceParser顾名思义就是Xml资源文件解析器了,用来解析我们xml文件的
- parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
ANDROID_MANIFEST_FILENAME也就是
- private static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
这样就很明显了,这里生成的xml文件资源解析器是用来解析AndroidManifest文件的了。接下来就是关键了
- String[] errorText = new String[1];
- Package pkg = null;
- Exception errorException = null;
- try {
- // XXXX todo: need to figure out correct configuration.
- pkg = parsePackage(res, parser, flags, errorText);
- } catch (Exception e) {
- errorException = e;
- mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
- }
这里才是我们Package真正生成的地方了,也就是pkg = parsePackage(res, parser, flags, errorText)了。parsePackage是同构函数,一个是以File为首个参数,就是我们现在分析的这个,一个是以Resources为首个参数,就是我们接下来要讲的了,由于这个函数比较大,所以不再全部列出,只选取主要的
- String pkgName = parsePackageName(parser, attrs, flags, outError);
获取包名。
- final Package pkg = new Package(pkgName);
- boolean foundApp = false;
- 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);
- if (pkg.mVersionName != null) {
- pkg.mVersionName = pkg.mVersionName.intern();
- }
- String str = sa.getNonConfigurationString(
- com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);
- if (str != null && str.length() > 0) {
- String nameError = validateName(str, true);
- if (nameError != null && !"android".equals(pkgName)) {
- 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);
- }
- sa.recycle();
- pkg.installLocation = sa.getInteger(
- com.android.internal.R.styleable.AndroidManifest_installLocation,
- PARSE_DEFAULT_INSTALL_LOCATION);
- pkg.applicationInfo.installLocation = pkg.installLocation;
解析获取,我们AndroidManifest的attrs,也就是frameworks/base/core/res/res/values下的attrs_manifest.xml中定义的
"AndroidManifest"> -
"versionCode" /> -
"versionName" /> -
"sharedUserId" /> -
"sharedUserLabel" /> -
"installLocation" />
这些变量信息。接下来就是一个大循环了,这里解析的内容比较多了,我们举几个常见的例子,如"application"也就是
"@string/app_name">
这里面包含的信息,例如这里的lable等等,还有以其为父的"activity","receiver","service","provider"等等,这里以"activity"为例,还是去frameworks/base/core/res/res/values下的attrs_manifest.xml中,也就是
- <declare-styleable name="AndroidManifestActivity" parent="AndroidManifestApplication">