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方法。