PKMS中卸載應用是通過deletePackage函數來執行,這個函數主要是調用了一些Observer回調,然后調用了deletePackageX函數。
publicvoiddeletePackage(final String packageName,
final IPackageDeleteObserver2 observer, final intuserId, finalintflags) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.DELETE_PACKAGES, null);
Preconditions.checkNotNull(packageName);
Preconditions.checkNotNull(observer);
final intuid = Binder.getCallingUid();
if(UserHandle.getUserId(uid) != userId) {
mContext.enforceCallingPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
"deletePackage for user "+ userId);
}
if(isUserRestricted(userId, UserManager.DISALLOW_UNINSTALL_APPS)) {
try{
observer.onPackageDeleted(packageName,//回調
PackageManager.DELETE_FAILED_USER_RESTRICTED, null);
} catch(RemoteException re) {
}
return;
}
boolean uninstallBlocked = false;
if((flags & PackageManager.DELETE_ALL_USERS) != 0) {
int[] users = sUserManager.getUserIds();
for(inti = 0; i
if(getBlockUninstallForUser(packageName, users[i])) {
uninstallBlocked = true;
break;
}
}
} else{
uninstallBlocked = getBlockUninstallForUser(packageName, userId);
}
if(uninstallBlocked) {
try{
observer.onPackageDeleted(packageName, PackageManager.DELETE_FAILED_OWNER_BLOCKED,
null);
} catch(RemoteException re) {
}
return;
}
if(DEBUG_REMOVE) {
Slog.d(TAG, "deletePackageAsUser: pkg="+ packageName +" user="+ userId);
}
// Queue up an async operation since the package deletion may take a little while.
mHandler.post(newRunnable() {//異步調用deletePackageX函數
publicvoidrun() {
mHandler.removeCallbacks(this);
final intreturnCode = 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
});
}
我們來看下deletePackageX函數,主要是調用deletePackageLI函數繼續執行,關鍵當返回的info不為空就調用info.args.doPostDeleteLI(true)刪除應用。
privateintdeletePackageX(String packageName,intuserId,intflags) {
......
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);
}
......
// Force a gc here.
Runtime.getRuntime().gc();
// Delete the resources here after sending the broadcast to let
// other processes clean up before deleting resources.
if(info.args != null) {
synchronized (mInstallLock) {
info.args.doPostDeleteLI(true);//刪除資源、apk等
}
}
returnres ? PackageManager.DELETE_SUCCEEDED : PackageManager.DELETE_FAILED_INTERNAL_ERROR;
}
我們來看deletePackageLI函數,如果只有data,直接調用removePackageDataLI函數。如果是系統應用調用deleteSystemPackageLI,不是調用deleteInstalledPackageLI函數。
privateboolean deletePackageLI(String packageName, UserHandle user,
boolean deleteCodeAndResources, int[] allUserHandles, boolean[] perUserInstalled,
intflags, PackageRemovedInfo outInfo,
boolean writeSettings) {
......
if(dataOnly) {
// Delete application data first
if(DEBUG_REMOVE) Slog.d(TAG,"Removing package data only");
removePackageDataLI(ps, null, null, outInfo, flags, writeSettings);
returntrue;
}
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);
}
returnret;
}
卸載系統應用
為什么會有卸載系統應用呢,當我們調用scanPackageLI掃描目錄時有時候失敗,也需要調用deletePackageLI來刪除apk,這個時候就會有系統應用了。
privateboolean deleteSystemPackageLI(PackageSetting newPs,
int[] allUserHandles, boolean[] perUserInstalled,
intflags, PackageRemovedInfo outInfo, boolean writeSettings) {
final boolean applyUserRestrictions
= (allUserHandles != null) && (perUserInstalled != null);
PackageSetting disabledPs = null;
// Confirm if the system package has been updated
// An updated system app can be deleted. This will also have to restore
// the system pkg from system partition
// reader
synchronized (mPackages) {
disabledPs = mSettings.getDisabledSystemPkgLPr(newPs.name);
}
if(DEBUG_REMOVE) Slog.d(TAG,"deleteSystemPackageLI: newPs="+ newPs
+ " disabledPs="+ disabledPs);
if(disabledPs == null) {
Slog.w(TAG, "Attempt to delete unknown system package "+ newPs.name);
returnfalse;
} elseif(DEBUG_REMOVE) {
Slog.d(TAG, "Deleting system pkg from data partition");
}
// Delete the updated package
outInfo.isRemovedPackageSystemUpdate = true;
if(disabledPs.versionCode
// Delete data for downgrades
flags &= ~PackageManager.DELETE_KEEP_DATA;//是否保留資源
} else{
// Preserve data by setting flag
flags |= PackageManager.DELETE_KEEP_DATA;
}
boolean ret = deleteInstalledPackageLI(newPs, true, flags,//卸載apk
allUserHandles, perUserInstalled, outInfo, writeSettings);
if(!ret) {
returnfalse;
}
// writer
synchronized (mPackages) {
// Reinstate the old system package
mSettings.enableSystemPackageLPw(newPs.name);
// Remove any native libraries from the upgraded package.
NativeLibraryHelper.removeNativeBinariesLI(newPs.legacyNativeLibraryPathString);
}
// Install the system package
intparseFlags = PackageParser.PARSE_MUST_BE_APK | PackageParser.PARSE_IS_SYSTEM;
if(locationIsPrivileged(disabledPs.codePath)) {
parseFlags |= PackageParser.PARSE_IS_PRIVILEGED;
}
final PackageParser.Package newPkg;
try{
newPkg = scanPackageLI(disabledPs.codePath, parseFlags, SCAN_NO_PATHS, 0, null);//重新apk掃描目錄
} catch(PackageManagerException e) {
Slog.w(TAG, "Failed to restore system package:"+ newPs.name +": "+ e.getMessage());
returnfalse;
}
// writer
synchronized (mPackages) {
PackageSetting ps = mSettings.mPackages.get(newPkg.packageName);
// Propagate the permissions state as we do not want to drop on the floor
// runtime permissions. The update permissions method below will take
// care of removing obsolete permissions and grant install permissions.
ps.getPermissionsState().copyFrom(newPs.getPermissionsState());
updatePermissionsLPw(newPkg.packageName, newPkg,
UPDATE_PERMISSIONS_ALL | UPDATE_PERMISSIONS_REPLACE_PKG);
if(applyUserRestrictions) {
if(DEBUG_REMOVE) {
Slog.d(TAG, "Propagating install state across reinstall");
}
for(inti = 0; i
if(DEBUG_REMOVE) {
Slog.d(TAG, " user "+ allUserHandles[i]
+ " => "+ perUserInstalled[i]);
}
ps.setInstalled(perUserInstalled[i], allUserHandles[i]);
mSettings.writeRuntimePermissionsForUserLPr(allUserHandles[i], false);
}
// Regardless of writeSettings we need to ensure that this restriction
// state propagation is persisted
mSettings.writeAllUsersPackageRestrictionsLPr();
}
// can downgrade to reader here
if(writeSettings) {
mSettings.writeLPr();//更新packages.xml
}
}
returntrue;
}
卸載系統應用,最后也是調用deleteInstalledPackageLI來完成卸載的,我們來看下這個函數。
卸載普通應用
卸載普通應用就直接調用了deleteInstalledPackageLI函數
privateboolean deleteInstalledPackageLI(PackageSetting ps,
boolean deleteCodeAndResources, intflags,
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);
}
returntrue;
}
我們先看removePackageDataLI函數,就是刪除各種資源,但是我們沒看到刪除apk文件。
privatevoidremovePackageDataLI(PackageSetting ps,
int[] allUserHandles, boolean[] perUserInstalled,
PackageRemovedInfo outInfo, intflags, boolean writeSettings) {
String packageName = ps.name;
if(DEBUG_REMOVE) Slog.d(TAG,"removePackageDataLI: "+ ps);
removePackageLI(ps, (flags&REMOVE_CHATTY) != 0);
// Retrieve object to delete permissions for shared user later on
final PackageSetting deletedPs;
// reader
synchronized (mPackages) {
deletedPs = mSettings.mPackages.get(packageName);
if(outInfo != null) {
outInfo.removedPackage = packageName;
outInfo.removedUsers = deletedPs != null
? deletedPs.queryInstalledUsers(sUserManager.getUserIds(), true)
: null;
}
}
if((flags&PackageManager.DELETE_KEEP_DATA) == 0) {
removeDataDirsLI(ps.volumeUuid, packageName);// 卸載data目錄
schedulePackageCleaning(packageName, UserHandle.USER_ALL, true);
}
// writer
synchronized (mPackages) {
if(deletedPs != null) {
if((flags&PackageManager.DELETE_KEEP_DATA) == 0) {
clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL);
clearDefaultBrowserIfNeeded(packageName);
if(outInfo != null) {
mSettings.mKeySetManagerService.removeAppKeySetDataLPw(packageName);
outInfo.removedAppId = mSettings.removePackageLPw(packageName);
}
updatePermissionsLPw(deletedPs.name, null, 0);
if(deletedPs.sharedUser != null) {
// Remove permissions associated with package. Since runtime
// permissions are per user we have to kill the removed package
// or packages running under the shared user of the removed
// package if revoking the permissions requested only by the removed
// package is successful and this causes a change in gids.
for(intuserId : UserManagerService.getInstance().getUserIds()) {
final intuserIdToKill = 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(newRunnable() {
@Override
publicvoidrun() {
// This has to happen with no lock held.
killApplication(deletedPs.name, deletedPs.appId,
KILL_APP_REASON_GIDS_CHANGED);
}
});
break;
}
}
}
clearPackagePreferredActivitiesLPw(deletedPs.name, UserHandle.USER_ALL);
}
// make sure to preserve per-user disabled state if this removal was just
// a downgrade of a system app to the factory package
if(allUserHandles != null && perUserInstalled != null) {
if(DEBUG_REMOVE) {
Slog.d(TAG, "Propagating install state across downgrade");
}
for(inti = 0; i
if(DEBUG_REMOVE) {
Slog.d(TAG, " user "+ allUserHandles[i]
+ " => "+ perUserInstalled[i]);
}
ps.setInstalled(perUserInstalled[i], allUserHandles[i]);
}
}
}
// can downgrade to reader
if(writeSettings) {
// Save settings now
mSettings.writeLPr();//更新packages.xml
}
}
if(outInfo != null) {
// A user ID was deleted here. Go through all users and remove it
// from KeyStore.
removeKeystoreDataIfNeeded(UserHandle.USER_ALL, outInfo.removedAppId);
}
我們再來看deleteInstalledPackageLI函數,最后一段代碼,
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);
}
我們來看下這個函數,如果不是安裝在sd卡上,最后新建一個FileInstallArgs對象。
privateInstallArgs createInstallArgsForExisting(intinstallFlags, String codePath,
String resourcePath, String[] instructionSets) {
final boolean isInAsec;
if(installOnExternalAsec(installFlags)) {
/* Apps on SD card are always in ASEC containers. */
isInAsec = true;
} elseif(installForwardLocked(installFlags)
&& !codePath.startsWith(mDrmAppPrivateInstallDir.getAbsolutePath())) {
/*
* Forward-locked apps are only in ASEC containers if they're the
* new style
*/
isInAsec = true;
} else{
isInAsec = false;
}
if(isInAsec) {
returnnewAsecInstallArgs(codePath, instructionSets,
installOnExternalAsec(installFlags), installForwardLocked(installFlags));
} else{
returnnewFileInstallArgs(codePath, resourcePath, instructionSets);
}
}
最后返回到deletePackageX函數,最后調用如下代碼,直接到FileInstallArgs的doPostDeleteLI函數。
if(info.args != null) {
synchronized (mInstallLock) {
info.args.doPostDeleteLI(true);
}
}
doPostDeleteLI函數如下:
boolean doPostDeleteLI(booleandelete) {
// XXX err, shouldn't we respect the delete flag?
cleanUpResourcesLI();
returntrue;
}
cleanUpResourcesLI函數會刪除資源和代碼文件,dex文件
voidcleanUpResourcesLI() {
// Try enumerating all code paths before deleting
List 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函數就是刪除apk文件。
privateboolean cleanUp() {
if(codeFile == null || !codeFile.exists()) {
returnfalse;
}
if(codeFile.isDirectory()) {
mInstaller.rmPackageDir(codeFile.getAbsolutePath());
} else{
codeFile.delete();
}
if(resourceFile != null && !FileUtils.contains(codeFile, resourceFile)) {
resourceFile.delete();
}
returntrue;
}
時序圖