APK安裝流程系列文章整體內(nèi)容如下:
- APK安裝流程詳解0——前言
- APK安裝流程詳解1——有關(guān)"安裝ing"的實(shí)體類概述
- APK安裝流程詳解2——PackageManager簡介
- APK安裝流程詳解3——PackageManager與PackageManagerService
- APK安裝流程詳解4——安裝中關(guān)于so庫的哪些事
- APK安裝流程詳解5——PackageInstallerService和Installer
- APK安裝流程詳解6——PackageManagerService啟動(dòng)前奏
- APK安裝流程詳解7——PackageManagerService的啟動(dòng)流程(上)
- APK安裝流程詳解8——PackageManagerService的啟動(dòng)流程(下)
- APK安裝流程詳解9——PackageParser解析APK(上)
- APK安裝流程詳解10——PackageParser解析APK(下)
- APK安裝流程詳解11——普通應(yīng)用安裝簡介
- APK安裝流程詳解12——PackageManagerService中的新安裝流程上(拷貝)
- APK安裝流程詳解13——PackageManagerService中的新安裝流程下(裝載)
- APK安裝流程詳解14——PMS中的新安裝流程上(拷貝)補(bǔ)充
- APK安裝流程詳解15——PMS中的新安裝流程下(裝載)補(bǔ)充
- APK安裝流程詳解16——Android包管理總結(jié)(尚未完結(jié)請(qǐng)期待)
本片文章的主要內(nèi)容如下:
- 1拷呆、裝載代碼的入口
- 2、PackageManagerService#processPendingInstall(InstallArgs,int)方法
- 3、PackageManagerService#installPackageLI(InstallArgs,PackageInstalledInfo)方法解析
- 4差凹、PackageManagerService#installNewPackageLI(PackageParser.Package, int, int, UserHandle, String, String,PackageInstalledInfo)方法解析
- 5性穿、PackageManagerService#scanPackageLI(File scanFile, int parseFlags, int scanFlags,long currentTime, UserHandle user) 方法解析
- 6慎菲、PackageManagerService#updateSettingsLIupdateSettingsLI(PackageParser.Package, String,String, int[], boolean[], PackageInstalledInfo,UserHandle)方法解析
- 7癣蟋、 PackageHandler的處理Message的what值為POST_INSTALL的情況解析
- 8辫狼、總結(jié)
一桂肌、裝載代碼的入口
上篇文章說到進(jìn)行完新安裝流程中的"拷貝代碼流程后"数焊,分兩種情況:
-
1、如果前面是走"驗(yàn)證流程"崎场,則會(huì)調(diào)用processPendingInstall(args, ret)方法佩耳,如下:
代碼在PackageManagerService.java 1537行
if (getDefaultVerificationResponse() == PackageManager.VERIFICATION_ALLOW) {
Slog.i(TAG, "Continuing with installation of " + originUri);
state.setVerifierResponse(Binder.getCallingUid(),
PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT);
broadcastPackageVerified(verificationId, originUri,
PackageManager.VERIFICATION_ALLOW,
state.getInstallArgs().getUser());
try {
ret = args.copyApk(mContainerService, true);
} catch (RemoteException e) {
Slog.e(TAG, "Could not contact the ContainerService");
}
} else {
broadcastPackageVerified(verificationId, originUri,
PackageManager.VERIFICATION_REJECT,
state.getInstallArgs().getUser());
}
processPendingInstall(args, ret);
mHandler.sendEmptyMessage(MCS_UNBIND);
-
2、如果不走"驗(yàn)證流程"谭跨,則會(huì)調(diào)用handleReturnCode()方法干厚,如下:
代碼在PackageManagerService.jav 10253行
final boolean startCopy() {
boolean res;
try {
if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
if (++mRetries > MAX_RETRIES) {
Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
mHandler.sendEmptyMessage(MCS_GIVE_UP);
handleServiceError();
return false;
} else {
handleStartCopy();
res = true;
}
} catch (RemoteException e) {
if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
mHandler.sendEmptyMessage(MCS_RECONNECT);
res = false;
}
handleReturnCode();
return res;
}
而在handleReturnCode()方法里面也是調(diào)用processPendingInstall(args, ret)方法,如下:
代碼在PackageManagerService.java 10253行
@Override
void handleReturnCode() {
// If mArgs is null, then MCS couldn't be reached. When it
// reconnects, it will try again to install. At that point, this
// will succeed.
if (mArgs != null) {
processPendingInstall(mArgs, mRet);
}
}
我們看到handleReturnCode()來不也是調(diào)用processPendingInstall(InstallArgs,int)方法的螃宙,所以我們說
"裝載代碼"的入口是processPendingInstall(InstallArgs,int)方法蛮瞄。
二、PackageManagerService#processPendingInstall(InstallArgs,int)方法
代碼在PackageManagerService.java 10156行
private void processPendingInstall(final InstallArgs args, final int currentStatus) {
// Queue up an async operation since the package installation may take a little while.
// 向mHandler中發(fā)送一個(gè)Runnable對(duì)象谆扎,這里是異步操作挂捅,因?yàn)榘惭b一個(gè)程序包可能需要一些時(shí)間
mHandler.post(new Runnable() {
public void run() {
// 第一部分
// 清除任務(wù)
mHandler.removeCallbacks(this);
// Result object to be returned
// 創(chuàng)建一個(gè)PackageInstalledInfo對(duì)象
PackageInstalledInfo res = new PackageInstalledInfo();
res.returnCode = currentStatus;
res.uid = -1;
res.pkg = null;
res.removedInfo = new PackageRemovedInfo();
// 第二部分
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
// 預(yù)安裝階段,主要是檢查安裝包的狀態(tài)堂湖,確保安裝環(huán)境正常闲先,如果安裝環(huán)境有問題會(huì)清理拷貝文件
args.doPreInstall(res.returnCode);
synchronized (mInstallLock) {
// 安裝階段,調(diào)用installPackageLI進(jìn)行安裝
installPackageLI(args, res);
}
// 安裝收尾
args.doPostInstall(res.returnCode, res.uid);
}
// A restore should be performed at this point if (a) the install
// succeeded, (b) the operation is not an update, and (c) the new
// package has not opted out of backup participation.
final boolean update = res.removedInfo.removedPackage != null;
final int flags = (res.pkg == null) ? 0 : res.pkg.applicationInfo.flags;
boolean doRestore = !update
&& ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0);
// Set up the post-install work request bookkeeping. This will be used
// and cleaned up by the post-install event handling regardless of whether
// there's a restore pass performed. Token values are >= 1.
// 第三部分
// 計(jì)算一個(gè)ID號(hào)
int token;
if (mNextInstallToken < 0) mNextInstallToken = 1;
token = mNextInstallToken++;
PostInstallData data = new PostInstallData(args, res);
// 保存到mRunningInstalls結(jié)構(gòu)中无蜂,以token為key伺糠,而mRunningInstalls是SparseArray結(jié)構(gòu)的
mRunningInstalls.put(token, data);
if (DEBUG_INSTALL) Log.v(TAG, "+ starting restore round-trip " + token);
// 第四部分
// 安裝成功,且需要備份的情況
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {
// Pass responsibility to the Backup Manager. It will perform a
// restore if appropriate, then pass responsibility back to the
// Package Manager to run the post-install observer callbacks
// and broadcasts.
IBackupManager bm = IBackupManager.Stub.asInterface(
ServiceManager.getService(Context.BACKUP_SERVICE));
if (bm != null) {
if (DEBUG_INSTALL) Log.v(TAG, "token " + token
+ " to BM for possible restore");
try {
if (bm.isBackupServiceActive(UserHandle.USER_OWNER)) {
bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);
} else {
doRestore = false;
}
} catch (RemoteException e) {
// can't happen; the backup manager is local
} catch (Exception e) {
Slog.e(TAG, "Exception trying to enqueue restore", e);
doRestore = false;
}
} else {
Slog.e(TAG, "Backup Manager not found!");
doRestore = false;
}
}
// 第五部分
if (!doRestore) {
// No restore possible, or the Backup Manager was mysteriously not
// available -- just fire the post-install work request directly.
if (DEBUG_INSTALL) Log.v(TAG, "No restore - queue post-install for " + token);
Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
mHandler.sendMessage(msg);
}
}
});
}
processPendingInstall()方法內(nèi)容不多酱讶,主要是post了一個(gè)消息退盯,這樣安裝過程將以異步的方式繼續(xù)執(zhí)行。
我將這個(gè)方法大致分為4個(gè)部分
- 第一部分:初始化階段
- 第二部分:安裝階段
這個(gè)階段有可以細(xì)分為3個(gè)階段如下:
- 預(yù)安裝階段:檢查當(dāng)前安裝包的狀態(tài)以及確保SDCARD的掛載,并返回狀態(tài)信息渊迁。在安裝前確保安裝環(huán)境的可靠
- 安裝階段:對(duì)mInstallLock加鎖慰照,表明同時(shí)只能由一個(gè)安裝包進(jìn)行安裝,然后調(diào)用installPackageLI方法完成具體的安裝操作琉朽。
- 安裝收尾階段:檢查狀態(tài)毒租,如果安裝失敗,刪除相關(guān)目錄文件箱叁。
- 第三部分:加入緩存
用InstallArgs和PackageInstalledInfo構(gòu)造一個(gè)PostInstallData對(duì)象墅垮,讓后把這個(gè)PostInstallData對(duì)方存放在mRunningInstalls里面,mRunningInstalls是一個(gè)SparseArray結(jié)構(gòu)耕漱,key是token算色,這樣后續(xù)查詢相關(guān)信息的東西可以直接訪問mRunningInstalls就可以了。- 第四部分:備份部分
如果需要備份螟够,則調(diào)用BackupManagerService來完成備份灾梦,這里注意備份完畢后,設(shè)置doRestore = false- 第五部分:安裝階段結(jié)束
無論該APK是否已經(jīng)安裝成功(失敗)妓笙,都會(huì)向mHandler發(fā)送一個(gè)what值為POST_INSTALL的Message消息若河。該Message消息的arg1為token,這樣它可以從mRunningInstalls中取得PostInstallData對(duì)象
這這方法里面涉及到四個(gè)重要的函數(shù)寞宫,即
- args.doPreInstall(res.returnCode)
- args.doPostInstall(res.returnCode, res.uid)
- installPackageLI(args, res)
- PackageHandler的處理Message的what值為POST_INSTALL的情況
其中installPackageLI(args, res)方法至關(guān)重要萧福,我們先在這里簡單的說下:installPackageLI方法進(jìn)行APK的裝載,該函數(shù)內(nèi)部將調(diào)用InstallArgs的doRename對(duì)臨時(shí)文件進(jìn)行改名辈赋。另外鲫忍,還需要掃描此APK文件。這樣實(shí)現(xiàn)了APK的信息映射到PackageManagerService內(nèi)部了炭庙。
我們知道args其實(shí)是FileInstallArgs饲窿,所以上面的args.doPreInstall(res.returnCode)方法和args.doPostInstall(res.returnCode, res.uid)方法其實(shí)對(duì)應(yīng)的都是FileInstallArgs的方法。
(一)焕蹄、FileInstallArgs#doPreInstall(int)方法
代碼在PackageManagerService.java 11108行
int doPreInstall(int status) {
if (status != PackageManager.INSTALL_SUCCEEDED) {
cleanUp();
}
return status;
}
我們看到這個(gè)方法里面判斷是否已經(jīng)成功安裝完成。如果沒有成功安裝完成則調(diào)用cleanUp()方法阀溶,但是我們知道安裝過程中的預(yù)安裝階段status一定是等于PackageManager.INSTALL_SUCCEEDED腻脏,因?yàn)橹挥械扔诓拍苓M(jìn)入到這個(gè)方法。
畢竟是如下的代碼
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
args.doPreInstall(res.returnCode);
}
(二)银锻、FileInstallArgs#doPostInstall(int,int)方法
代碼在PackageManagerService.java 11161行
int doPostInstall(int status, int uid) {
if (status != PackageManager.INSTALL_SUCCEEDED) {
cleanUp();
}
return status;
}
在調(diào)用這個(gè)的方法時(shí)候永品,當(dāng)然有一種可能就是安裝成功了,如果安裝成了击纬,則這個(gè)方法什么也不做鼎姐;還有一種可能就是安裝失敗了,安裝失敗了即status不等于PackageManager.INSTALL_SUCCEEDED,所以會(huì)調(diào)用 cleanUp()方法炕桨。
假設(shè)安裝失敗了饭尝,我們來看下cleanUp()方法里面做了什么操作,不過看方法名字献宫,我們猜測(cè)是"清理"操作钥平。
FileInstallArgs#cleanUp()方法
代碼在PackageManagerService.java 11178行
private boolean cleanUp() {
//判斷代碼文件是否存在,如果不存在姊途,直接返回涉瘾,不需要清理
if (codeFile == null || !codeFile.exists()) {
return false;
}
// 如果代碼文件是個(gè)文件夾,則調(diào)用mInstaller.rmPackageDir進(jìn)行清理
if (codeFile.isDirectory()) {
mInstaller.rmPackageDir(codeFile.getAbsolutePath());
} else {
// 如果代碼文件是文件捷兰,則直接刪除文件夾
codeFile.delete();
}
// 如果存在資源文件立叛,則刪除資源文件
if (resourceFile != null && !FileUtils.contains(codeFile, resourceFile)) {
resourceFile.delete();
}
// 返回清除成功
return true;
}
可見cleanUp方法就是清理代碼文件和資源文件。
這里有個(gè)方法就是代碼文件是個(gè)文件夾的時(shí)候贡茅,調(diào)用mInstaller.rmPackageDir(codeFile.getAbsolutePath())進(jìn)行刪除工作秘蛇,在前面文章APK安裝流程詳解5——Installer、InstallerConnection和Installd守護(hù)進(jìn)程我們知道m(xù)Installerq其實(shí)只是一個(gè)代理類友扰,具體制定的是Native的intalld彤叉。那我們來找下Installer的rmPackageDir方法對(duì)應(yīng)的intalld什么操作?
代碼在installd.cpp 218行
struct cmdinfo cmds[] = {
{ "ping", 0, do_ping },
{ "install", 5, do_install },
{ "dexopt", 10, do_dexopt },
{ "markbootcomplete", 1, do_mark_boot_complete },
{ "movedex", 3, do_move_dex },
{ "rmdex", 2, do_rm_dex },
{ "remove", 3, do_remove },
{ "rename", 2, do_rename },
{ "fixuid", 4, do_fixuid },
{ "freecache", 2, do_free_cache },
{ "rmcache", 3, do_rm_cache },
{ "rmcodecache", 3, do_rm_code_cache },
{ "getsize", 8, do_get_size },
{ "rmuserdata", 3, do_rm_user_data },
{ "cpcompleteapp", 6, do_cp_complete_app },
{ "movefiles", 0, do_movefiles },
{ "linklib", 4, do_linklib },
{ "mkuserdata", 5, do_mk_user_data },
{ "mkuserconfig", 1, do_mk_user_config },
{ "rmuser", 2, do_rm_user },
{ "idmap", 3, do_idmap },
{ "restorecondata", 4, do_restorecon_data },
{ "createoatdir", 2, do_create_oat_dir },
{ "rmpackagedir", 1, do_rm_package_dir },
{ "linkfile", 3, do_link_file }
};
大家注意下倒數(shù)第二行,rmpackagedir對(duì)應(yīng)著do_rm_package_dir
代碼在installd.cpp 176 行
static int do_rm_package_dir(char **arg, char reply[REPLY_MAX] __unused)
{
/* oat_dir */
return rm_package_dir(arg[0]);
}
那我們看下rm_package_dir函數(shù)
代碼在commands.cpp 1825 行
int rm_package_dir(const char* apk_path)
{
if (validate_apk_path(apk_path)) {
ALOGE("invalid apk path '%s' (bad prefix)\n", apk_path);
return -1;
}
return delete_dir_contents(apk_path, 1 /* also_delete_dir */ , NULL /* exclusion_predicate */);
}
首先通過validate_apk_path來檢驗(yàn)下路徑,然后調(diào)用delete_dir_contents函數(shù)來刪除作儿,這個(gè)函數(shù)在utils.cpp 291行歹鱼。里面的代碼很簡單,我就不講解了
下面我們來看下installPackageLI方法的具體實(shí)現(xiàn)
三竞慢、PackageManagerService#installPackageLI(InstallArgs,PackageInstalledInfo)方法解析
代碼在PackageManagerService.java 12224行
private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
// ******* 第一步 *******
//獲取 installFlags屬性,這個(gè)屬性表明APP安裝到哪里
final int installFlags = args.installFlags;
// 安裝包應(yīng)用程序的 包名
final String installerPackageName = args.installerPackageName;
// volume的Uuid,后續(xù)講解Android存儲(chǔ)系列的詳細(xì)講解
final String volumeUuid = args.volumeUuid;
//根據(jù)安裝包代碼的路徑生成一個(gè)文件
final File tmpPackageFile = new File(args.getCodePath());
// 是否需要鎖定
final boolean forwardLocked = ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
// 是否安裝到外部存儲(chǔ)
final boolean onExternal = (((installFlags & PackageManager.INSTALL_EXTERNAL) != 0)
|| (args.volumeUuid != null));
// 新安裝還是更新安裝的標(biāo)志位
boolean replace = false;
int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;
// 判斷是是否是移動(dòng)一個(gè)APP斑举,由于我們是講解的新安裝,所以args.move=null
if (args.move != null) {
// moving a complete application; perfom an initial scan on the new install location
scanFlags |= SCAN_INITIAL;
}
// Result object to be returned
res.returnCode = PackageManager.INSTALL_SUCCEEDED;
if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile);
// Retrieve PackageSettings and parse package
// 獲取解析包配置的標(biāo)志位
final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
| (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)
| (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
PackageParser pp = new PackageParser();
// 設(shè)置解析包的獨(dú)立進(jìn)程屬性
pp.setSeparateProcesses(mSeparateProcesses);
// 設(shè)置解析包的屏幕屬性
pp.setDisplayMetrics(mMetrics);
final PackageParser.Package pkg;
// ******* 第二步 *******
try {
// 解析APK病涨,主要是解析AndroidManifest.xml文件富玷,將結(jié)果記錄在PackageParser.Package中
pkg = pp.parsePackage(tmpPackageFile, parseFlags);
} catch (PackageParserException e) {
res.setError("Failed parse during installPackageLI", e);
return;
}
// ******* 第三步 *******
// Mark that we have an install time CPU ABI override.
pkg.cpuAbiOverride = args.abiOverride;
String pkgName = res.name = pkg.packageName;
// 如果這個(gè)待安裝的APP不是測(cè)試包,但是如果環(huán)境為僅允許測(cè)試包則返回
if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) {
if ((installFlags & PackageManager.INSTALL_ALLOW_TEST) == 0) {
res.setError(INSTALL_FAILED_TEST_ONLY, "installPackageLI");
return;
}
}
try {
//收集APK的簽名信息
// collectCertificates做簽名驗(yàn)證既穆,collectManifestDigest主要是做包的項(xiàng)目清單摘要的收集赎懦,主要適合用來比較兩個(gè)包的是否一樣
pp.collectCertificates(pkg, parseFlags);
pp.collectManifestDigest(pkg);
} catch (PackageParserException e) {
res.setError("Failed collect during installPackageLI", e);
return;
}
/** 如果安裝程序此前傳入了一個(gè)項(xiàng)目清單文件(manifest),那么將解析到項(xiàng)目清單文件與傳入的進(jìn)行對(duì)比幻工。
* 安裝器的確傳入了一個(gè)清單励两,PackageInstallerActivity中也解析出APK,那么記錄了這個(gè)清單囊颅,然后進(jìn)行對(duì)比当悔,判斷是否是同一個(gè)APK
**/
/* If the installer passed in a manifest digest, compare it now. */
if (args.manifestDigest != null) {
if (DEBUG_INSTALL) {
final String parsedManifest = pkg.manifestDigest == null ? "null"
: pkg.manifestDigest.toString();
Slog.d(TAG, "Comparing manifests: " + args.manifestDigest.toString() + " vs. "
+ parsedManifest);
}
if (!args.manifestDigest.equals(pkg.manifestDigest)) {
res.setError(INSTALL_FAILED_PACKAGE_CHANGED, "Manifest digest changed");
return;
}
} else if (DEBUG_INSTALL) {
final String parsedManifest = pkg.manifestDigest == null
? "null" : pkg.manifestDigest.toString();
Slog.d(TAG, "manifestDigest was not present, but parser got: " + parsedManifest);
}
// Get rid of all references to package scan path via parser.
pp = null;
String oldCodePath = null;
boolean systemApp = false;
synchronized (mPackages) {
// Check if installing already existing package
if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
// 如果安裝的升級(jí)應(yīng)用傅瞻,繼續(xù)使用以前的老包名
String oldName = mSettings.mRenamedPackages.get(pkgName);
// 如果要進(jìn)行安裝的應(yīng)用,已經(jīng)存在盲憎,將是替換安裝嗅骄,則設(shè)置replace=true
if (pkg.mOriginalPackages != null
&& pkg.mOriginalPackages.contains(oldName)
&& mPackages.containsKey(oldName)) {
// This package is derived from an original package,
// and this device has been updating from that original
// name. We must continue using the original name, so
// rename the new package here.
// 包名設(shè)置為老的包名
pkg.setPackageName(oldName);
pkgName = pkg.packageName;
replace = true;
if (DEBUG_INSTALL) Slog.d(TAG, "Replacing existing renamed package: oldName="
+ oldName + " pkgName=" + pkgName);
} else if (mPackages.containsKey(pkgName)) {
// This package, under its official name, already exists
// on the device; we should replace it.
replace = true;
if (DEBUG_INSTALL) Slog.d(TAG, "Replace existing pacakge: " + pkgName);
}
// Prevent apps opting out from runtime permissions
if (replace) {
// 如果是替換,即升級(jí)安裝
PackageParser.Package oldPackage = mPackages.get(pkgName);
// 分別獲取老焙畔、新版本的TargetSdk
final int oldTargetSdk = oldPackage.applicationInfo.targetSdkVersion;
final int newTargetSdk = pkg.applicationInfo.targetSdkVersion;
// 如果老的TargetSdk 大于android 5.1 而新的TargetSdk 小于5.1,
if (oldTargetSdk > Build.VERSION_CODES.LOLLIPOP_MR1
&& newTargetSdk <= Build.VERSION_CODES.LOLLIPOP_MR1) {
res.setError(PackageManager.INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE,
"Package " + pkg.packageName + " new target SDK " + newTargetSdk
+ " doesn't support runtime permissions but the old"
+ " target SDK " + oldTargetSdk + " does.");
return;
}
}
}
PackageSetting ps = mSettings.mPackages.get(pkgName);
// 如果 ps 不為null掸读,同樣說明,已經(jīng)存在一個(gè)具有相同安裝包包名的程序宏多,被安裝儿惫,所以還是處理覆蓋安裝的問題。
// 這里主要驗(yàn)證包名的簽名伸但,不一致的話肾请,是不能覆蓋安裝的,另外版本號(hào)也不能比安裝的地更胖,否則不能替換安裝
if (ps != null) {
if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);
// Quick sanity check that we're signed correctly if updating;
// we'll check this again later when scanning, but we want to
// bail early here before tripping over redefined permissions.
//檢查密鑰集合是否一致
if (shouldCheckUpgradeKeySetLP(ps, scanFlags)) {
if (!checkUpgradeKeySetLP(ps, pkg)) {
res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
+ pkg.packageName + " upgrade keys do not match the "
+ "previously installed version");
return;
}
} else {
try {
verifySignaturesLP(ps, pkg);
} catch (PackageManagerException e) {
res.setError(e.error, e.getMessage());
return;
}
}
// 判斷安裝的應(yīng)用是否存在同名的應(yīng)用铛铁,如果存在,判斷應(yīng)用是否帶有系統(tǒng)應(yīng)用的標(biāo)志
oldCodePath = mSettings.mPackages.get(pkgName).codePathString;
if (ps.pkg != null && ps.pkg.applicationInfo != null) {
systemApp = (ps.pkg.applicationInfo.flags &
ApplicationInfo.FLAG_SYSTEM) != 0;
}
res.origUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
}
// ******* 第四步 *******
// Check whether the newly-scanned package wants to define an already-defined perm
// 檢查APK中定義的所有的權(quán)限是否已經(jīng)被其他應(yīng)用定義了却妨,如果重定義的是系統(tǒng)應(yīng)用定義的權(quán)限饵逐,那么忽略本app定義的這個(gè)權(quán)限。如果重定義的是非系統(tǒng)引用的權(quán)限彪标,那么本次安裝就以失敗返回倍权。
int N = pkg.permissions.size();
for (int i = N-1; i >= 0; i--) {
PackageParser.Permission perm = pkg.permissions.get(i);
BasePermission bp = mSettings.mPermissions.get(perm.info.name);
if (bp != null) {
// If the defining package is signed with our cert, it's okay. This
// also includes the "updating the same package" case, of course.
// "updating same package" could also involve key-rotation.
final boolean sigsOk;
if (bp.sourcePackage.equals(pkg.packageName)
&& (bp.packageSetting instanceof PackageSetting)
&& (shouldCheckUpgradeKeySetLP((PackageSetting) bp.packageSetting,
scanFlags))) {
sigsOk = checkUpgradeKeySetLP((PackageSetting) bp.packageSetting, pkg);
} else {
sigsOk = compareSignatures(bp.packageSetting.signatures.mSignatures,
pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;
}
if (!sigsOk) {
// If the owning package is the system itself, we log but allow
// install to proceed; we fail the install on all other permission
// redefinitions.
if (!bp.sourcePackage.equals("android")) {
res.setError(INSTALL_FAILED_DUPLICATE_PERMISSION, "Package "
+ pkg.packageName + " attempting to redeclare permission "
+ perm.info.name + " already owned by " + bp.sourcePackage);
res.origPermission = perm.info.name;
res.origPackage = bp.sourcePackage;
return;
} else {
Slog.w(TAG, "Package " + pkg.packageName
+ " attempting to redeclare system permission "
+ perm.info.name + "; ignoring new declaration");
pkg.permissions.remove(i);
}
}
}
}
}
// 如果是帶帶有系統(tǒng)應(yīng)用標(biāo)志的應(yīng)用,卻要安裝在SD卡上捞烟,則報(bào)錯(cuò)返回薄声,安裝失敗原因錯(cuò)誤的安裝路徑
if (systemApp && onExternal) {
// Disable updates to system apps on sdcard
res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
"Cannot install updates to system apps on sdcard");
return;
}
// ******* 第五步 *******
// 如果是移動(dòng)APP
if (args.move != null) {
// We did an in-place move, so dex is ready to roll
scanFlags |= SCAN_NO_DEX;
scanFlags |= SCAN_MOVE;
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(pkgName);
if (ps == null) {
res.setError(INSTALL_FAILED_INTERNAL_ERROR,
"Missing settings for moved package " + pkgName);
}
// We moved the entire application as-is, so bring over the
// previously derived ABI information.
pkg.applicationInfo.primaryCpuAbi = ps.primaryCpuAbiString;
pkg.applicationInfo.secondaryCpuAbi = ps.secondaryCpuAbiString;
}
} else if (!forwardLocked && !pkg.applicationInfo.isExternalAsec()) {
// Enable SCAN_NO_DEX flag to skip dexopt at a later stage
scanFlags |= SCAN_NO_DEX;
try {
derivePackageAbi(pkg, new File(pkg.codePath), args.abiOverride,
true /* extract libs */);
} catch (PackageManagerException pme) {
Slog.e(TAG, "Error deriving application ABI", pme);
res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Error deriving application ABI");
return;
}
// Run dexopt before old package gets removed, to minimize time when app is unavailable
int result = mPackageDexOptimizer
.performDexOpt(pkg, null /* instruction sets */, false /* forceDex */,
false /* defer */, false /* inclDependencies */,
true /* boot complete */);
// 實(shí)際為dex2oat操作,用來將apk中的dex文件轉(zhuǎn)換為oat文件
if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
res.setError(INSTALL_FAILED_DEXOPT, "Dexopt failed for " + pkg.codePath);
return;
}
}
// ******* 第六步 *******
// 重命名题画,將/data/app/vmdl{安裝會(huì)話}.tmp重命名為/data/apppp/包名-suffix,suffix為1默辨、2...
if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
// 如果重命名失敗,則報(bào)錯(cuò)苍息,退出缩幸,安裝失敗原因:無法重命名
res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
return;
}
// ******* 第七步 *******
startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);
// ******* 第八步 *******
if (replace) {
// 覆蓋安裝
replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
installerPackageName, volumeUuid, res);
} else {
// 首次安裝
installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
args.user, installerPackageName, volumeUuid, res);
}
// ******* 第九步 *******
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(pkgName);
if (ps != null) {
res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
}
}
}
上面注釋已經(jīng)很清楚了,我這里主要這個(gè)方法的整體流程分為9個(gè)部分如下:
- 第一步:初始化變量
- 第二步:解析APK
- 第三步:判斷是新安裝還是升級(jí)安裝竞思,無論是新安裝還是升級(jí)安裝都是需要獲取簽名信息桌粉。
- 第四步:檢查權(quán)限
- 第五步:根據(jù)不同的安裝標(biāo)志,來進(jìn)行操作衙四,分為三種情況
- 移動(dòng)操作:
- 非鎖定安裝且沒有安裝在SD卡上:新安裝走這里,這里面主要做兩個(gè)操作:第①步是so拷貝患亿,第②步是進(jìn)行dex優(yōu)化传蹈,第③步機(jī)械性dex2oat操作押逼,將dex文件轉(zhuǎn)化為oat。
- 如果上面兩個(gè)條件都不滿足惦界,則什么也不做
- 第六步:重命名安裝:將/data/app/vmdl{安裝會(huì)話}.tmp重命名為/data/apppp/包名-suffix,suffix為1挑格、2...
- 第七步:開始intent filter驗(yàn)證
- 第八步:這里根據(jù)不同的安裝方式進(jìn)行不同的方式,主要有兩種情況
- 覆蓋安裝即更新安裝:調(diào)用replacePackageLI方法進(jìn)行覆蓋安裝
- 首次安裝:調(diào)用installNewPackageLI方法進(jìn)行首次安裝
- 第九步:安裝收尾沾歪,調(diào)用PackageSetting的queryInstalledUsers設(shè)置安裝用戶
這里面涉及到7個(gè)比較復(fù)雜的方法漂彤,我會(huì)在后面的一片文章中詳細(xì)講解:
- 1、pp.setSeparateProcesses(mSeparateProcesses):設(shè)置獨(dú)立進(jìn)程屬性灾搏,這塊內(nèi)容請(qǐng)參考APK安裝流程詳解15——PMS中的新安裝流程下(裝載)補(bǔ)充中的 一挫望、PackageParser#setSeparateProcesses(String[] procs)方法解析 。
- 2狂窑、shouldCheckUpgradeKeySetLP(ps, scanFlags):這塊內(nèi)容請(qǐng)參考APK安裝流程詳解15——PMS中的新安裝流程下(裝載)補(bǔ)充中二媳板、PackageManagerService#shouldCheckUpgradeKeySetLP(PackageSetting, int) 方法解析
- 3、checkUpgradeKeySetLP(ps, pkg):這塊內(nèi)容請(qǐng)參考APK安裝流程詳解15——PMS中的新安裝流程下(裝載)補(bǔ)充中 三泉哈、PackageManagerService#checkUpgradeKeySetLP(PackageSetting, PackageParser.Package) 方法解析
- 4蛉幸、verifySignaturesLP(ps, pkg):這塊內(nèi)容請(qǐng)參考APK安裝流程詳解15——PMS中的新安裝流程下(裝載)補(bǔ)充中 四、PackageManagerService#verifySignaturesLP(PackageSetting, PackageParser.Package)方法解析
- 5丛晦、mPackageDexOptimizer.performDexOpt(pkg, null , false, false , false , true):這塊內(nèi)容請(qǐng)參考APK安裝流程詳解15——PMS中的新安裝流程下(裝載)補(bǔ)充中 五奕纫、PackageDexOptimizer#performDexOp(PackageParser.Package, String[], String[], boolean, String,CompilerStats.PackageStats)方法解析方法解析
- 6、args.doRename(res.returnCode, pkg, oldCodePath):這塊內(nèi)容請(qǐng)參考APK安裝流程詳解15——PMS中的新安裝流程下(裝載)補(bǔ)充中六烫沙、args.doRename(res.returnCode, pkg, oldCodePath)方法解析
- 7匹层、startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg):這塊內(nèi)容請(qǐng)參考APK安裝流程詳解15——PMS中的新安裝流程下(裝載)補(bǔ)充中七、startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg)方法解析
因?yàn)槲覀兪鞘状伟惭b斧吐,所以我們來看下installNewPackageLI方法內(nèi)部的執(zhí)行情況
四又固、PackageManagerService#installNewPackageLI(PackageParser.Package, int, int, UserHandle, String, String,PackageInstalledInfo)方法解析
代碼在PackageManagerService.java 11757行
/*
* Install a non-existing package.
*/
// 安裝一個(gè)不存在的安裝包。
private void installNewPackageLI(PackageParser.Package pkg, int parseFlags, int scanFlags,
UserHandle user, String installerPackageName, String volumeUuid,
PackageInstalledInfo res) {
// ******** 第一部分 ********
// Remember this for later, in case we need to rollback this install
String pkgName = pkg.packageName;
if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg);
// 判斷是否存在APK的數(shù)據(jù)煤率。如果一個(gè)APK不是經(jīng)過經(jīng)常的卸載流程仰冠,那其歷史數(shù)據(jù)可能還是保留下來的。
final boolean dataDirExists = Environment
.getDataUserPackageDirectory(volumeUuid, UserHandle.USER_OWNER, pkgName).exists();
// 如果package已經(jīng)存在了
synchronized(mPackages) {
if (mSettings.mRenamedPackages.containsKey(pkgName)) {
// 已經(jīng)安裝了具有相同名稱的包蝶糯,盡管它已經(jīng)被重命名為較舊的名稱洋只。所以應(yīng)該走更新流程而不是新安裝流程
// A package with the same name is already installed, though
// it has been renamed to an older name. The package we
// are trying to install should be installed as an update to
// the existing one, but that has not been requested, so bail.
res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName
+ " without first uninstalling package running as "
+ mSettings.mRenamedPackages.get(pkgName));
return;
}
if (mPackages.containsKey(pkgName)) {
// 如果已經(jīng)有了相同的包名,則禁止安裝
// Don't allow installation over an existing package with the same name.
res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName
+ " without first uninstalling.");
return;
}
}
// ******** 第二部分 ********
try {
//核心調(diào)用: 安裝APK
PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanFlags,
System.currentTimeMillis(), user);
// 更新Settings
updateSettingsLI(newPackage, installerPackageName, volumeUuid, null, null, res, user);
// delete the partially installed application. the data directory will have to be
// restored if it was already existing
// 如果安裝失敗昼捍,則執(zhí)行回退操作识虚,刪除創(chuàng)建的文件夾等緩存文件
if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
// remove package from internal structures. Note that we want deletePackageX to
// delete the package data and cache directories that it created in
// scanPackageLocked, unless those directories existed before we even tried to
// install.
deletePackageLI(pkgName, UserHandle.ALL, false, null, null,
dataDirExists ? PackageManager.DELETE_KEEP_DATA : 0,
res.removedInfo, true);
}
} catch (PackageManagerException e) {
res.setError("Package couldn't be installed in " + pkg.codePath, e);
}
}
這個(gè)方法我主要分為兩個(gè)部分:
- 第一部分:安裝前檢索,主要是根據(jù)兩個(gè)檢索條件來排除相同的包名的情況:
- 判斷重命名的包中是否含有相同的包名
- 判斷已有的安裝包中是否有相同的包名- 第二部分:進(jìn)行安裝:主要是調(diào)用scanPackageLI進(jìn)行安裝妒茬,通過這個(gè)方法APK的跟中信息都會(huì)記錄在PackageManagerService中担锤;之后調(diào)用了updateSettingsLI進(jìn)行設(shè)置信息的更新,主要是更新了權(quán)限信息與安裝完成信息乍钻。如果安裝失敗就會(huì)刪除安裝包信息肛循。
這個(gè)方法內(nèi)部有兩個(gè)核心方法如下:
- PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanFlags,System.currentTimeMillis(), user):負(fù)責(zé)安裝
- updateSettingsLI(newPackage, installerPackageName, null, null, res):安裝后的Setting信息的更新铭腕。
下面我們來看下這個(gè)兩個(gè)方法
五、PackageManagerService#scanPackageLI(File scanFile, int parseFlags, int scanFlags,long currentTime, UserHandle user) 方法解析
代碼在PackageManagerService.java 6466行
private PackageParser.Package scanPackageLI(PackageParser.Package pkg, int parseFlags,
int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
boolean success = false;
try {
final PackageParser.Package res = scanPackageDirtyLI(pkg, parseFlags, scanFlags,
currentTime, user);
success = true;
return res;
} finally {
if (!success && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) {
removeDataDirsLI(pkg.volumeUuid, pkg.packageName);
}
}
}
scanPackageLI主要調(diào)用了scanPackageDirtyLI多糠,如果調(diào)用失敗則調(diào)用removeDataDirsLI來移除安裝信息累舷,scanPackageDirtyLI的代碼如下:
這塊代碼之前在APK安裝流程詳解8——PackageManagerService的啟動(dòng)流程(下)中的六、6夹孔、PackageManagerService#scanPackageLI(PackageParser.Package, int, int, long, UserHandle)方法解析(首參數(shù)為Package)已經(jīng)講解過了被盈,這里就不詳細(xì)講解了
六、PackageManagerService#updateSettingsLIupdateSettingsLI(PackageParser.Package, String,String, int[], boolean[], PackageInstalledInfo,UserHandle)方法解析
代碼在PackageManagerService.java 12158行
private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName,
String volumeUuid, int[] allUsers, boolean[] perUserInstalled, PackageInstalledInfo res,
UserHandle user) {
//************** 第一步 **************
// 包名
String pkgName = newPackage.packageName;
synchronized (mPackages) {
//write settings. the installStatus will be incomplete at this stage.
//note that the new package setting would have already been
//added to mPackages. It hasn't been persisted yet.
// 設(shè)置安裝狀態(tài)搭伤,并寫入
mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_INCOMPLETE);
mSettings.writeLPr();
}
if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + newPackage.codePath);
//************** 第二步 **************
synchronized (mPackages) {
// 更新權(quán)限
updatePermissionsLPw(newPackage.packageName, newPackage,
UPDATE_PERMISSIONS_REPLACE_PKG | (newPackage.permissions.size() > 0
? UPDATE_PERMISSIONS_ALL : 0));
// For system-bundled packages, we assume that installing an upgraded version
// of the package implies that the user actually wants to run that new code,
// so we enable the package.
// 對(duì)于綁定的軟件只怎,如果是更新,我們可以理解用戶要運(yùn)行新程序闷畸,我們啟動(dòng)這個(gè)軟件
//************** 第三步 **************
PackageSetting ps = mSettings.mPackages.get(pkgName);
if (ps != null) {
// 如果系統(tǒng)中擁有同樣包名的設(shè)置PackageSetting
if (isSystemApp(newPackage)) {
// 如果是系統(tǒng)應(yīng)用
// NB: implicit assumption that system package upgrades apply to all users
// 系統(tǒng)應(yīng)用適用于所有用戶
if (DEBUG_INSTALL) {
Slog.d(TAG, "Implicitly enabling system package on upgrade: " + pkgName);
}
//如果存在已經(jīng)安裝該應(yīng)用的用戶組
if (res.origUsers != null) {
for (int userHandle : res.origUsers) {
// 設(shè)置它為啟用狀態(tài)
ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT,
userHandle, installerPackageName);
}
}
// Also convey the prior install/uninstall state
// 修改其對(duì)應(yīng)的用戶狀態(tài)
if (allUsers != null && perUserInstalled != null) {
for (int i = 0; i < allUsers.length; i++) {
if (DEBUG_INSTALL) {
Slog.d(TAG, " user " + allUsers[i]
+ " => " + perUserInstalled[i]);
}
ps.setInstalled(perUserInstalled[i], allUsers[i]);
}
// these install state changes will be persisted in the
// upcoming call to mSettings.writeLPr().
}
}
// It's implied that when a user requests installation, they want the app to be
// installed and enabled.
// 用戶進(jìn)行安裝的時(shí)候尝盼,其實(shí)他們是希望安裝并啟用應(yīng)用程序的。所以設(shè)置他們的屬性
int userId = user.getIdentifier();
if (userId != UserHandle.USER_ALL) {
ps.setInstalled(true, userId);
ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName);
}
}
res.name = pkgName;
res.uid = newPackage.applicationInfo.uid;
res.pkg = newPackage;
mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_COMPLETE);
mSettings.setInstallerPackageName(pkgName, installerPackageName);
res.returnCode = PackageManager.INSTALL_SUCCEEDED;
//to update install status
// 寫入
//************** 第四步 **************
mSettings.writeLPr();
}
}
我將這個(gè)方法分為四個(gè)部分:
- 第一步:設(shè)置更新狀態(tài)
- 第二步:更新權(quán)限
- 第三步:調(diào)整包狀態(tài)
- 第四步:Settings寫入
至此安裝結(jié)束佑菩。
七盾沫、 PackageHandler的處理Message的what值為POST_INSTALL的情況解析
代碼在PackageManagerService.java1333行
void doHandleMessage(Message msg) {
switch (msg.what) {
...
case POST_INSTALL: {
if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1);
//***************** 第一步 ****************
// 我們知道m(xù)sg的arg1是token,arg2是0
PostInstallData data = mRunningInstalls.get(msg.arg1);
// 因?yàn)橐呀?jīng)安裝成功了殿漠,所以在 正在安裝列表中刪除了這個(gè)選項(xiàng)
mRunningInstalls.delete(msg.arg1);
boolean deleteOld = false;
if (data != null) {
InstallArgs args = data.args;
PackageInstalledInfo res = data.res;
// 如果安裝成功
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
final String packageName = res.pkg.applicationInfo.packageName;
res.removedInfo.sendBroadcast(false, true, false);
Bundle extras = new Bundle(1);
extras.putInt(Intent.EXTRA_UID, res.uid);
// Now that we successfully installed the package, grant runtime
// permissions if requested before broadcasting the install.
// 如果已經(jīng)成功的安裝了應(yīng)用赴精,在發(fā)送廣播之前先授予一些必要的權(quán)限
// 這些權(quán)限在 installPackageAsUser 中創(chuàng)建 InstallParams 時(shí)傳遞的,為null绞幌。
if ((args.installFlags
& PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0) {
//***************** 第二步 ****************
grantRequestedRuntimePermissions(res.pkg, args.user.getIdentifier(),
args.installGrantPermissions);
}
// Determine the set of users who are adding this
// package for the first time vs. those who are seeing
// an update.
// 看一下當(dāng)前應(yīng)用對(duì)于那些用戶是第一此安裝蕾哟,那些用戶是更新升級(jí)安裝
int[] firstUsers;
int[] updateUsers = new int[0];
if (res.origUsers == null || res.origUsers.length == 0) {
//所有用戶都是第一次安裝
firstUsers = res.newUsers;
} else {
firstUsers = new int[0];
// 這里通過剛剛已經(jīng)安裝該包的用戶中選出 那些之前安裝過該包的用戶
for (int i=0; i<res.newUsers.length; i++) {
int user = res.newUsers[i];
boolean isNew = true;
for (int j=0; j<res.origUsers.length; j++) {
if (res.origUsers[j] == user) {
// 找到以前安裝過該包的用戶
isNew = false;
break;
}
}
if (isNew) {
int[] newFirst = new int[firstUsers.length+1];
System.arraycopy(firstUsers, 0, newFirst, 0,
firstUsers.length);
newFirst[firstUsers.length] = user;
firstUsers = newFirst;
} else {
int[] newUpdate = new int[updateUsers.length+1];
System.arraycopy(updateUsers, 0, newUpdate, 0,
updateUsers.length);
newUpdate[updateUsers.length] = user;
updateUsers = newUpdate;
}
}
}
//***************** 第三步 ****************
// 安裝完成之后發(fā)送"ACTION_PACKAGE_ADDED"廣播
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
packageName, extras, null, null, firstUsers);
final boolean update = res.removedInfo.removedPackage != null;
if (update) {
extras.putBoolean(Intent.EXTRA_REPLACING, true);
}
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
packageName, extras, null, null, updateUsers);
//***************** 第四步 ****************
if (update) {
// 如果是升級(jí)更新安裝,還會(huì)發(fā)送ACTION_PACKAGE_REPLACED和ACTION_MY_PACKAGE_REPLACED廣播
// 這兩個(gè)廣播不同之處在于PACKAGE_REPLACE將攜帶一個(gè)extra信息
sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
packageName, extras, null, null, updateUsers);
sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
null, null, packageName, null, updateUsers);
//***************** 第五步 ****************
// treat asec-hosted packages like removable media on upgrade
// 判斷該包是否設(shè)置了PRIVATE_FLAG_FORWARD_LOCK標(biāo)志或者是要求安裝在SD卡上
if (res.pkg.isForwardLocked() || isExternal(res.pkg)) {
if (DEBUG_INSTALL) {
Slog.i(TAG, "upgrading pkg " + res.pkg
+ " is ASEC-hosted -> AVAILABLE");
}
int[] uidArray = new int[] { res.pkg.applicationInfo.uid };
ArrayList<String> pkgList = new ArrayList<String>(1);
pkgList.add(packageName);
sendResourcesChangedBroadcast(true, true,
pkgList,uidArray, null);
}
}
if (res.removedInfo.args != null) {
// Remove the replaced package's older resources safely now
// 刪除被替換應(yīng)用的資源目錄
deleteOld = true;
}
//***************** 第六步 ****************
// If this app is a browser and it's newly-installed for some
// users, clear any default-browser state in those users
// 針對(duì)Browser做一些處理
if (firstUsers.length > 0) {
// the app's nature doesn't depend on the user, so we can just
// check its browser nature in any user and generalize.
// 判斷是否是瀏覽器應(yīng)用
if (packageIsBrowser(packageName, firstUsers[0])) {
synchronized (mPackages) {
for (int userId : firstUsers) {
mSettings.setDefaultBrowserPackageNameLPw(null, userId);
}
}
}
}
// Log current value of "unknown sources" setting
EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED,
getUnknownSourcesSettings());
}
//***************** 第七步 ****************
// Force a gc to clear up things
// 執(zhí)行g(shù)c操作
Runtime.getRuntime().gc();
// We delete after a gc for applications on sdcard.
//***************** 第八步 ****************
// 執(zhí)行刪除操作
if (deleteOld) {
synchronized (mInstallLock) {
// 調(diào)用FileInstallArgs的doPostDeleteLI進(jìn)行資源清理
res.removedInfo.args.doPostDeleteLI(true);
}
}
//***************** 第九步 ****************
if (args.observer != null) {
try {
Bundle extras = extrasForInstallResult(res);
// 回調(diào)onPackageInstalled方法
args.observer.onPackageInstalled(res.name, res.returnCode,
res.returnMsg, extras);
} catch (RemoteException e) {
Slog.i(TAG, "Observer no longer exists.");
}
}
} else {
Slog.e(TAG, "Bogus post-install token " + msg.arg1);
}
} break;
...
}
終于到了裝載的最后一個(gè)流程了莲蜘,我將這個(gè)方法內(nèi)部分為9個(gè)部分
- 第一步:這里主要是先將安裝信息從安裝列列表中移除谭确,這個(gè)也是前面在processPendingInstall中添加的
- 第二步:安裝成功后,獲取運(yùn)行時(shí)權(quán)限
- 第三步:獲取權(quán)限后票渠,發(fā)送ACTION_PACKAGE_ADDED廣播逐哈,告訴Laucher之流,來新客人了问顷,趕緊把icon啥的放上去昂秃。
- 第四步:如果是升級(jí)更新則在發(fā)送兩條廣播
- ACTION_PACKAGE_REPLACED:一個(gè)新版本的應(yīng)用安裝到設(shè)備上,替換換之前已經(jīng)存在的版本
- ACTION_MY_PACKAGE_REPLACED:應(yīng)用的新版本替換舊版本被安裝杜窄,只發(fā)給被更新的應(yīng)用自己
- 第五步:如果安裝包中設(shè)置了PRIVATE_FLAG_FORWARD_LOCK或者被要求安裝在SD卡上肠骆,則調(diào)用sendResourcesChangedBroadcast方法來發(fā)送一個(gè)資源更改的廣播
- 第六步:如果該應(yīng)用是一個(gè)瀏覽器,則要清除瀏覽器設(shè)置塞耕,重新檢查瀏覽器設(shè)置蚀腿。
- 第七步:強(qiáng)制調(diào)用gc,出發(fā)JVM進(jìn)行垃圾回收操作扫外。
- 第八步:刪除舊的安裝信息唯咬。
- 第九步:回調(diào)回調(diào)args.observer.packageInstalled方法纱注。告訴PackageInstaller安裝結(jié)果。從而實(shí)現(xiàn)了安裝回調(diào)到UI層胆胰。
八、總結(jié)
1刻获、安裝大致流程圖
安裝過程:復(fù)制apk安裝包到/data/app目錄下蜀涨,解壓并掃描安裝包,向資源管理器注入apk資源蝎毡,解析AndroidManifest文件厚柳,并在/data/data目錄下創(chuàng)建對(duì)應(yīng)的應(yīng)用數(shù)據(jù)目錄,然后針對(duì)dalvik/art環(huán)境優(yōu)化dex文件沐兵,保存到dalvik-cache目錄别垮,將AndroidManifest文件解析出的組件、權(quán)限注冊(cè)到PackageManagerService扎谎,完成后發(fā)送廣播碳想。
2、安裝詳細(xì)時(shí)序圖
上面的圖太模糊了毁靶,不利于了解細(xì)節(jié)胧奔,這里"偷"了一張書序圖,以便大家分析
點(diǎn)擊放大查看高清無碼大圖
3预吆、整體架構(gòu)圖
上一篇文章 APK安裝流程詳解12——PackageManagerService中的新安裝流程上(拷貝)
下一篇文章 APK安裝流程詳解14——PMS中的新安裝流程上(拷貝)補(bǔ)充