apk解除安裝分析
1, apk解除安裝
和安裝APK過程相對,解除安裝apk過程如下,
1,從PMS的內部結構上刪除acitivity、service、provider等資訊
2,更新Settings中的package資訊
3.刪除code、resource等資訊
4.刪除dex檔案
Apk安裝時,一般都會走PackageManagerService 中的 installPackage 方法。
相反,解除安裝apk的時候一般會呼叫PMS中的deletePackage方法。
2, apk解除安裝流程
流程圖如下,
PMS的deletePackage方法會另開一個子執行緒執行解除安裝apk。
mHandler.post(new Runnable() { public void run() { mHandler.removeCallbacks(this); final int returnCode = deletePackageX(packageName, userId, flags); if (observer != null) { try { observer.onPackageDeleted(packageName, returnCode, null); } catch (RemoteException e) { Log.i(TAG, "Observer no longer exists."); } //end catch } //end if } //end run });
解除安裝完成之後,如果註冊了觀察者,就會回撥onPackageDeleted方法。
synchronized (mInstallLock) { if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageX: pkg=" + packageName + " user=" + userId); res = deletePackageLI(packageName, removeForUser, true, allUsers, perUserInstalled, flags | REMOVE_CHATTY, info, true); systemUpdate = info.isRemovedPackageSystemUpdate; if (res && !systemUpdate && mPackages.get(packageName) == null) { removedForAllUsers = true; } if (DEBUG_REMOVE) Slog.d(TAG, "delete res: systemUpdate=" + systemUpdate + " removedForAllUsers=" + removedForAllUsers); }
deletePackageX直接呼叫deletePackageLI方法, deletePackageLI主要方法如下,
boolean ret = false; if (isSystemApp(ps)) { if (DEBUG_REMOVE) Slog.d(TAG, "Removing system package:" + ps.name); // When an updated system application is deleted we delete the existing resources as well and // fall back to existing code in system partition ret = deleteSystemPackageLI(ps, allUserHandles, perUserInstalled, flags, outInfo, writeSettings); } else { if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package:" + ps.name); // Kill application pre-emptively especially for apps on sd. killApplication(packageName, ps.appId, "uninstall pkg"); ret = deleteInstalledPackageLI(ps, deleteCodeAndResources, flags, allUserHandles, perUserInstalled, outInfo, writeSettings); }
如果是系統apk, 則呼叫deleteSystemPackageLI完成解除安裝(該方法其實也是呼叫deleteInstalledPackageLI
方法完成解除安裝);如果不是系統apk, 則呼叫deleteInstalledPackageLI完成解除安裝。
deleteInstalledPackageLI方法如下,
private boolean deleteInstalledPackageLI(PackageSetting ps,
boolean deleteCodeAndResources, int flags,
int[] allUserHandles, boolean[] perUserInstalled,
PackageRemovedInfo outInfo, boolean writeSettings) {
if (outInfo != null) {
outInfo.uid = ps.appId;
}
// Delete package data from internal structures and also remove data if flag is set
removePackageDataLI(ps, allUserHandles, perUserInstalled, outInfo, flags, writeSettings);
// Delete application code and resources
if (deleteCodeAndResources && (outInfo != null)) {
outInfo.args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args);
}
return true;
}
在deleteInstalledPackageLI方法中,主要分為2個步驟:
第一步呼叫removePackageDataLI方法刪除/data/data下面的資料目錄,
並從PMS的內部資料結構上清除當前解除安裝的package資訊;
第二步呼叫createInstallArgsForExisting方法刪除code和resource檔案。
2.1 removePackageDataLI
removePackageDataLI呼叫流程圖如下,
2.1.1 removePackageLI
removePackageLI方法如下,
void removePackageLI(PackageSetting ps, boolean chatty) {
if (DEBUG_INSTALL) {
if (chatty)
Log.d(TAG, "Removing package " + ps.name);
}
// writer
synchronized (mPackages) {
mPackages.remove(ps.name);
final PackageParser.Package pkg = ps.pkg;
if (pkg != null) {
cleanPackageDataStructuresLILPw(pkg, chatty);
}
}
}
首先刪除包名,然後呼叫cleanPackageDataStructuresLILPw方法清除資料結構中的四大元件以及相關資訊。
cleanPackageDataStructuresLILPw刪除receiver程式碼如下,
N = pkg.receivers.size();
r = null;
for (i=0; i<N; i++) {
PackageParser.Activity a = pkg.receivers.get(i);
mReceivers.removeActivity(a, "receiver");
if (DEBUG_REMOVE && chatty) {
if (r == null) {
r = new StringBuilder(256);
} else {
r.append(' ');
}
r.append(a.info.name);
}
}
2.1.2 removeDataDirsLI
private int removeDataDirsLI(String volumeUuid, String packageName) {
int[] users = sUserManager.getUserIds();
int res = 0;
for (int user : users) {
int resInner = mInstaller.remove(volumeUuid, packageName, user);
if (resInner < 0) {
res = resInner;
}
}
return res;
}
呼叫Installer的remove方法去刪除/data/data下面的目錄
然後呼叫schedulePackageCleaning方法進一步清理。
最後呼叫Settings的updateSharedUserPermsLPw方法更新資訊。for (int userId : UserManagerService.getInstance().getUserIds()) {
final int userIdToKill = mSettings.updateSharedUserPermsLPw(deletedPs,
userId);
if (userIdToKill == UserHandle.USER_ALL
|| userIdToKill >= UserHandle.USER_OWNER) {
// If gids changed for this user, kill all affected packages.
mHandler.post(new Runnable() {
@Override
public void run() {
// This has to happen with no lock held.
killApplication(deletedPs.name, deletedPs.appId,
KILL_APP_REASON_GIDS_CHANGED);
}
});
break;
}
}
2.2 createInstallArgsForExisting
createInstallArgsForExisting方法根據安裝目錄的不同, 分別構造FileInstallArgs和AsecInstallArgs來完成code和resource資源的清除。
if (isInAsec) {
return new AsecInstallArgs(codePath, instructionSets,
installOnExternalAsec(installFlags), installForwardLocked(installFlags));
} else {
return new FileInstallArgs(codePath, resourcePath, instructionSets);
}
這裡只是構造了一個FileInstallArgs物件,如何呼叫的呢?
流程圖如下,
在deletePackageX的最後會呼叫FileInstallArgs的doPostDeleteLI方法。
if (info.args != null) {
synchronized (mInstallLock) {
if(info.args.isExternalAsec()){
Message msg = mHandler.obtainMessage(DEL_APK_FOR_EXTERNAL);
msg.obj = info.args;
mHandler.sendMessage(msg);
}else
{
info.args.doPostDeleteLI(true);
}
}
}
FileInstallArgs和PackageRemovedInfo都是PMS的內部類。
cleanUpResourcesLI方法如下,
void cleanUpResourcesLI() {
// Try enumerating all code paths before deleting
List<String> allCodePaths = Collections.EMPTY_LIST;
if (codeFile != null && codeFile.exists()) {
try {
final PackageLite pkg = PackageParser.parsePackageLite(codeFile, 0);
allCodePaths = pkg.getAllCodePaths();
} catch (PackageParserException e) {
// Ignored; we tried our best
}
}
cleanUp();
removeDexFiles(allCodePaths, instructionSets);
}
Cleanup主要刪除code、resource等檔案。
private boolean cleanUp() {
if (codeFile == null || !codeFile.exists()) {
return false;
}
if (codeFile.isDirectory()) {
mInstaller.rmPackageDir(codeFile.getAbsolutePath());
} else {
codeFile.delete();
}
if (resourceFile != null && !FileUtils.contains(codeFile, resourceFile)) {
resourceFile.delete();
}
return true;
}
removeDexFiles刪除Dex檔案。
private void removeDexFiles(List<String> allCodePaths, String[] instructionSets) {
if (!allCodePaths.isEmpty()) {
if (instructionSets == null) {
throw new IllegalStateException("instructionSet == null");
}
String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
for (String codePath : allCodePaths) {
for (String dexCodeInstructionSet : dexCodeInstructionSets) {
int retCode = mInstaller.rmdex(codePath, dexCodeInstructionSet);
if (retCode < 0) {
Slog.w(TAG, "Couldn't remove dex file for package: "
+ " at location " + codePath + ", retcode=" + retCode);
// we don't consider this to be a failure of the core package deletion
}
}
}
}
}
最後,在解除安裝apk之前,在deletePackageX方法中會呼叫killApplication方法,
PMS的killApplication方法如下,
private void killApplication(String pkgName, int appId, String reason) {
// Request the ActivityManager to kill the process(only for existing packages)
// so that we do not end up in a confused state while the user is still using the older
// version of the application while the new one gets installed.
IActivityManager am = ActivityManagerNative.getDefault();
if (am != null) {
try {
am.killApplicationWithAppId(pkgName, appId, reason);
} catch (RemoteException e) {
}
}
}
顧名思義,該方法會殺死一個程序,呼叫的是AMS的killApplicationWithAppId方法。