Android PackageManagerService分析三:卸载APK

这一章我们介绍APK的卸载过程,从前一章分析安装APK的过程,我们应该大致了解这里的卸载的过程如下:

1.从PMS的内部结构上删除acitivity、service、provider等信息

2.删除code、library和resource等信息

3.调用installd删除/data/data/packageName以及/data/dalvik-cache下面的文件

4.更新Settings中的package信息


当我们在Settings中的应用页面找到一个安装了的应用程序,并点击卸载后,就会发送一个Intent给UninstallerActivity,在UninstallerActivity最后会启动UninstallAppProgress的initView方法,并调用如下卸载函数:

[java]
getPackageManager().deletePackage(mAppInfo.packageName, observer,  
        mAllUsers ? PackageManager.DELETE_ALL_USERS : 0);


上面的mAllUsers默认是false。getPackageManager()函数的实现在ContextImpl.java,它最后会调用到ApplicantPackageManger.java的deletePackage方法:

[java]
public void deletePackage(String packageName, IPackageDeleteObserver observer, int flags) {  
    try {  
        mPM.deletePackageAsUser(packageName, observer, UserHandle.myUserId(), flags);  
    } catch (RemoteException e) {  
        // Should never happen!  
    }  
}


通过Binder调用,我们来看PMS中的deletePackageAsUser方法:

[java]
public void deletePackageAsUser(final String packageName,  
                                final IPackageDeleteObserver observer,  
                                final int userId, final int flags) {  
    mContext.enforceCallingOrSelfPermission(  
            android.Manifest.permission.DELETE_PACKAGES, null);  
    final int uid = Binder.getCallingUid();  
    if (isUserRestricted(userId, UserManager.DISALLOW_UNINSTALL_APPS)) {  
        try {  
            observer.packageDeleted(packageName, PackageManager.DELETE_FAILED_USER_RESTRICTED);  
        } catch (RemoteException re) {  
        }  
        return;  
    }  
  
    mHandler.post(new Runnable() {  
        public void run() {  
            mHandler.removeCallbacks(this);  
            final int returnCode = deletePackageX(packageName, userId, flags);  
            if (observer != null) {  
                try {  
                    observer.packageDeleted(packageName, returnCode);  
                } catch (RemoteException e) {  
                    Log.i(TAG, "Observer no longer exists.");  
                } //end catch  
            } //end if  
        } //end run  
    });  
}


在deletePackageAsUser方法中,首先做权限检查,然后就调用deletePackageX方法去执行卸载任务:

[java]
private int deletePackageX(String packageName, int userId, int flags) {  
    final PackageRemovedInfo info = new PackageRemovedInfo();  
    final boolean res;  
  
    boolean removedForAllUsers = false;  
    boolean systemUpdate = false;  
  
    int[] allUsers;  
    boolean[] perUserInstalled;  
    synchronized (mPackages) {  
        PackageSetting ps = mSettings.mPackages.get(packageName);  
        allUsers = sUserManager.getUserIds();  
        perUserInstalled = new boolean[allUsers.length];  
        for (int i = 0; i < allUsers.length; i++) {  
            perUserInstalled[i] = ps != null ? ps.getInstalled(allUsers[i]) : false;  
        }  
    }  
  
    synchronized (mInstallLock) {  
        res = deletePackageLI(packageName,  
                (flags & PackageManager.DELETE_ALL_USERS) != 0  
                        ? UserHandle.ALL : new UserHandle(userId),  
                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);  
    }  
  
    if (res) {  
        info.sendBroadcast(true, systemUpdate, removedForAllUsers);  
  
    }  
    Runtime.getRuntime().gc();  
    if (info.args != null) {  
        synchronized (mInstallLock) {  
            info.args.doPostDeleteLI(true);  
        }  
    }  
  
    return res ? PackageManager.DELETE_SUCCEEDED : PackageManager.DELETE_FAILED_INTERNAL_ERROR;  
}


deletePackageX在这里我们只考虑当前只有一个user的情况,来看deletePackageLI的实现:

[java] view plaincopy
private boolean deletePackageLI(String packageName, UserHandle user,  
        boolean deleteCodeAndResources, int[] allUserHandles, boolean[] perUserInstalled,  
        int flags, PackageRemovedInfo outInfo,  
        boolean writeSettings) {  
    PackageSetting ps;  
    boolean dataOnly = false;  
    int removeUser = -1;  
    int appId = -1;  
    synchronized (mPackages) {  
        ps = mSettings.mPackages.get(packageName);  
        if (ps == null) {  
            Slog.w(TAG, "Package named '" + packageName + "' doesn't exist.");  
            return false;  
        }  
        if ((!isSystemApp(ps) || (flags&PackageManager.DELETE_SYSTEM_APP) != 0) && user != null  
                && user.getIdentifier() != UserHandle.USER_ALL) {  
            ps.setUserState(user.getIdentifier(),  
                    COMPONENT_ENABLED_STATE_DEFAULT,  
                    false, //installed  
                    true,  //stopped  
                    true,  //notLaunched  
                    false, //blocked  
                    null, null, null);  
            if (!isSystemApp(ps)) {  
                if (ps.isAnyInstalled(sUserManager.getUserIds())) {  
                     
            } else {  
                removeUser = user.getIdentifier();  
                appId = ps.appId;  
                mSettings.writePackageRestrictionsLPr(removeUser);  
            }  
        }  
    }  
  
    boolean ret = false;  
    mSettings.mKeySetManager.removeAppKeySetData(packageName);  
    if (isSystemApp(ps)) {  
        ret = deleteSystemPackageLI(ps, allUserHandles, perUserInstalled,  
                flags, outInfo, writeSettings);  
    } else {  
        // Kill application pre-emptively especially for apps on sd.  
        killApplication(packageName, ps.appId, "uninstall pkg");  
        ret = deleteInstalledPackageLI(ps, deleteCodeAndResources, flags,  
                allUserHandles, perUserInstalled,  
                outInfo, writeSettings);  
    }  
  
    return ret;  
}


在deletePackageLI函数中根据是否是systemApp调用不同的流程,如果是systemApp,则调用deleteSystemPackageLI完成卸载;如果非systemApp,则调用deleteInstalledPackageLI完成卸载,当然在卸载之前,首先会调用AMS的killApplication方法先让这个APP停止运行。我们主要介绍非systemApp的卸载过程,来看deleteInstalledPackageLI方法的实现:

[java] view plaincopy
private boolean deleteInstalledPackageLI(PackageSetting ps,  
        boolean deleteCodeAndResources, int flags,  
        int[] allUserHandles, boolean[] perUserInstalled,  
        PackageRemovedInfo outInfo, boolean writeSettings) {  
    if (outInfo != null) {  
        outInfo.uid = ps.appId;  
    }  
  
    removePackageDataLI(ps, allUserHandles, perUserInstalled, outInfo, flags, writeSettings);  
  
    if (deleteCodeAndResources && (outInfo != null)) {  
        outInfo.args = createInstallArgs(packageFlagsToInstallFlags(ps), ps.codePathString,  
                ps.resourcePathString, ps.nativeLibraryPathString);  
    }  
    return true;  
}


在deleteInstalledPackageLI方法中,分为两步去卸载应用:第一步删除/data/data下面的数据目录,并从PMS的内部数据结构上清除当前卸载的package信息;第二步就删除code和resource文件。我们先来看第一步:

[java] view plaincopy
private void removePackageDataLI(PackageSetting ps,  
        int[] allUserHandles, boolean[] perUserInstalled,  
        PackageRemovedInfo outInfo, int flags, boolean writeSettings) {  
    String packageName = ps.name;  
    removePackageLI(ps, (flags&REMOVE_CHATTY) != 0);  
    final PackageSetting deletedPs;  
  
    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(packageName);  
        schedulePackageCleaning(packageName, UserHandle.USER_ALL, true);  
    }


removePackageDataLI用于删除应用的/data/data数据目录,并且从PMS内部数据结构里面清除package的信息。首先调用removePackageLI从PMS内部的数据结构上删除要卸载的package信息:

[java] view plaincopy
void removePackageLI(PackageSetting ps, boolean chatty) {  
    synchronized (mPackages) {  
        mPackages.remove(ps.name);  
        if (ps.codePathString != null) {  
            mAppDirs.remove(ps.codePathString);  
        }  
  
        final PackageParser.Package pkg = ps.pkg;  
        if (pkg != null) {  
            cleanPackageDataStructuresLILPw(pkg, chatty);  
        }  
    }  
}


cleanPackageDataStructuresLILPw用于将package的providers、services、receivers、activities等信息去PMS的全局数据结构上移除,这部分代码比较简单。如果没有设置DELETE_KEEP_DATA这个flag,就会首先调用removeDataDirsLI去删除/data/data下面的目录:

[java] view plaincopy
private int removeDataDirsLI(String packageName) {  
    int[] users = sUserManager.getUserIds();  
    int res = 0;  
    for (int user : users) {  
        int resInner = mInstaller.remove(packageName, user);  
        if (resInner < 0) {  
            res = resInner;  
        }  
    }  
  
    final File nativeLibraryFile = new File(mAppLibInstallDir, packageName);  
    NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryFile);  
    if (!nativeLibraryFile.delete()) {  
        Slog.w(TAG, "Couldn't delete native library directory " + nativeLibraryFile.getPath());  
    }  
  
    return res;  
}


这里首先调用installd的remove方法去删除/data/data下面的目录。然后去删除/data/app-lib下面的应用程序的library信息,但因为这里的nativeLibraryFile为/data/app-lib/packageName,和前面介绍的APK安装过程中的目录/data/app-lib/packageName-num不一样,所以实际上,这里并没有真正的去删除library目录。先来看installd的remove方法:

[cpp] view plaincopy
static int do_remove(char **arg, char reply[REPLY_MAX])  
{  
    return uninstall(arg[0], atoi(arg[1])); /* pkgname, userid */  
}  
  
int uninstall(const char *pkgname, userid_t userid)  
{  
    char pkgdir[PKG_PATH_MAX];  
  
    if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, userid))  
        return -1;  
  
    return delete_dir_contents(pkgdir, 1, NULL);  
}  
  
int delete_dir_contents(const char *pathname,  
                        int also_delete_dir,  
                        const char *ignore)  
{  
    int res = 0;  
    DIR *d;  
  
    d = opendir(pathname);  
    if (d == NULL) {  
        ALOGE("Couldn't opendir %s: %s\n", pathname, strerror(errno));  
        return -errno;  
    }  
    res = _delete_dir_contents(d, ignore);  
    closedir(d);  
    if (also_delete_dir) {  
        if (rmdir(pathname)) {  
            ALOGE("Couldn't rmdir %s: %s\n", pathname, strerror(errno));  
            res = -1;  
        }  
    }  
    return res;  
}


create_pkg_path方法构造/data/data/packageName的文件路径名,然后调用delete_dir_contents来删除文件内容以及目录,前面介绍过,/data/data/packageName的文件其实都是符号链接,所以_delete_dir_contents的实现中都是调用unlinkat去删除这些符号链接。回到removePackageDataLI中,接着调用schedulePackageCleaning来安排清理动作:

[java] view plaincopy
void schedulePackageCleaning(String packageName, int userId, boolean andCode) {  
    mHandler.sendMessage(mHandler.obtainMessage(START_CLEANING_PACKAGE,  
            userId, andCode ? 1 : 0, packageName));  
}

这里向PackageHandler发送START_CLEANING_PACKAGE消息,PMS会调用ContainService的函数去删除/storage/sdcard0/Android/data和/storage/sdcard0/Android/media下面与package相关的文件,有兴趣可以去看一下这部分的code。接着来看removePackageDataLI方法:

[java] view plaincopy
synchronized (mPackages) {  
    if (deletedPs != null) {  
        if ((flags&PackageManager.DELETE_KEEP_DATA) == 0) {  
            if (outInfo != null) {  
                outInfo.removedAppId = mSettings.removePackageLPw(packageName);  
            }  
            if (deletedPs != null) {  
                updatePermissionsLPw(deletedPs.name, null, 0);  
                if (deletedPs.sharedUser != null) {  
                    // remove permissions associated with package  
                    mSettings.updateSharedUserPermsLPw(deletedPs, mGlobalGids);  
                }  
            }  
            clearPackagePreferredActivitiesLPw(deletedPs.name, UserHandle.USER_ALL);  
        }  
    }  
  
    if (writeSettings) {  
        mSettings.writeLPr();  
    }  
}  
if (outInfo != null) {  
    removeKeystoreDataIfNeeded(UserHandle.USER_ALL, outInfo.removedAppId);  
}


这里首先从Settings中删除PackageSettings的信息:

[java] view plaincopy
int removePackageLPw(String name) {  
    final PackageSetting p = mPackages.get(name);  
    if (p != null) {  
        mPackages.remove(name);  
        if (p.sharedUser != null) {  
            p.sharedUser.removePackage(p);  
            if (p.sharedUser.packages.size() == 0) {  
                mSharedUsers.remove(p.sharedUser.name);  
                removeUserIdLPw(p.sharedUser.userId);  
                return p.sharedUser.userId;  
            }  
        } else {  
            removeUserIdLPw(p.appId);  
            return p.appId;  
        }  
    }  
    return -1;  
}


removePackageLPw首先从mPackages这个map中删除PackageSettings信息,如果不存在sharedUser,则从mUserIds这个数组中删除对应的Package UID信息;如果存在sharedUser,则首先检查这个sharedUser是否所有的package都已经被卸载了,如果都被卸载了,这个sharedUser也就可以删除。然后removePackageDataLI调用updatePermissionsLPw去检查mPermissionTrees和mPermissions两个数组中的权限是否是被删除的Package提供,如果有,则删除。Settings的updateSharedUserPermsLPw方法用于清除sharedUser不用的gid信息,防止权限泄露:

[java] view plaincopy
void updateSharedUserPermsLPw(PackageSetting deletedPs, int[] globalGids) {  
    SharedUserSetting sus = deletedPs.sharedUser;  
  
    for (String eachPerm : deletedPs.pkg.requestedPermissions) {  
        boolean used = false;  
        if (!sus.grantedPermissions.contains(eachPerm)) {  
            continue;  
        }  
        for (PackageSetting pkg:sus.packages) {  
            if (pkg.pkg != null &&  
                    !pkg.pkg.packageName.equals(deletedPs.pkg.packageName) &&  
                    pkg.pkg.requestedPermissions.contains(eachPerm)) {  
                used = true;  
                break;  
            }  
        }  
        if (!used) {  
            sus.grantedPermissions.remove(eachPerm);  
        }  
    }  
    int newGids[] = globalGids;  
    for (String eachPerm : sus.grantedPermissions) {  
        BasePermission bp = mPermissions.get(eachPerm);  
        if (bp != null) {  
            newGids = PackageManagerService.appendInts(newGids, bp.gids);  
        }  
    }  
    sus.gids = newGids;  
}

循环的从要被卸载的Package所在的sharedUser组中找被申请的权限是否还被同一组的其它package使用,如果没有使用者,就从sharedUser的grantedPermissions删除。clearPackagePreferredActivitiesLPw与AMS相关,我们留到以后再来介绍。在removePackageDataLI方法最好调用Settings.writeLPr()方法将改动的信息写到Package.xml中。到这里,我们前面所说的deleteInstalledPackageLI方法中的第一步已经完成,来看第二部分:

[java] view plaincopy
    if (deleteCodeAndResources && (outInfo != null)) {  
        outInfo.args = createInstallArgs(packageFlagsToInstallFlags(ps), ps.codePathString,  
                ps.resourcePathString, ps.nativeLibraryPathString);  
    }  
  
private InstallArgs createInstallArgs(int flags, String fullCodePath, String fullResourcePath,  
        String nativeLibraryPath) {  
    final boolean isInAsec;  
    if (installOnSd(flags)) {  
        isInAsec = true;  
    } else if (installForwardLocked(flags)  
            && !fullCodePath.startsWith(mDrmAppPrivateInstallDir.getAbsolutePath())) {  
        isInAsec = true;  
    } else {  
        isInAsec = false;  
    }  
  
    if (isInAsec) {  
        return new AsecInstallArgs(fullCodePath, fullResourcePath, nativeLibraryPath,  
                installOnSd(flags), installForwardLocked(flags));  
    } else {  
        return new FileInstallArgs(fullCodePath, fullResourcePath, nativeLibraryPath);  
    }  
}

这里根据安装目录的不同,分别构造FileInstallArgs和AsecInstallArgs来完成code和resource资源的清除。这里我们主要介绍卸载内部存储空间上面的APK,来看FileInstallArgs的doPostDeleteLI方法:

[java] view plaincopy
boolean doPostDeleteLI(boolean delete) {  
    cleanUpResourcesLI();  
    return true;  
}  
  
void cleanUpResourcesLI() {  
    String sourceDir = getCodePath();  
    if (cleanUp()) {  
        int retCode = mInstaller.rmdex(sourceDir);  
        if (retCode < 0) {  
            Slog.w(TAG, "Couldn't remove dex file for package: "  
                    +  " at location "  
                    + sourceDir + ", retcode=" + retCode);  
            // we don't consider this to be a failure of the core package deletion  
        }  
    }  
}


cleanUpResourcesLI方法中首先调用cleanUp方法去删除code、resource以及library文件:

[java] view plaincopy
private boolean cleanUp() {  
    boolean ret = true;  
    String sourceDir = getCodePath();  
    String publicSourceDir = getResourcePath();  
    if (sourceDir != null) {  
        File sourceFile = new File(sourceDir);  
        if (!sourceFile.exists()) {  
            Slog.w(TAG, "Package source " + sourceDir + " does not exist.");  
            ret = false;  
        }  
  
        sourceFile.delete();  
    }  
    if (publicSourceDir != null && !publicSourceDir.equals(sourceDir)) {  
        final File publicSourceFile = new File(publicSourceDir);  
        if (!publicSourceFile.exists()) {  
            Slog.w(TAG, "Package public source " + publicSourceFile + " does not exist.");  
        }  
        if (publicSourceFile.exists()) {  
            publicSourceFile.delete();  
        }  
    }  
  
    if (libraryPath != null) {  
        File nativeLibraryFile = new File(libraryPath);  
        NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryFile);  
        if (!nativeLibraryFile.delete()) {  
            Slog.w(TAG, "Couldn't delete native library directory " + libraryPath);  
        }  
    }  
  
    return ret;  
}


然后cleanUpResourcesLI调用installd的rmdex方法去删除存在/data/dalvik-cache文件:

[cpp] view plaincopy
static int do_rm_dex(char **arg, char reply[REPLY_MAX])  
{  
    return rm_dex(arg[0]); /* pkgname */  
}  
  
int rm_dex(const char *path)  
{  
    char dex_path[PKG_PATH_MAX];  
  
    if (validate_apk_path(path)) return -1;  
    if (create_cache_path(dex_path, path)) return -1;  
  
    ALOGV("unlink %s\n", dex_path);  
    if (unlink(dex_path) < 0) {  
        ALOGE("Couldn't unlink %s: %s\n", dex_path, strerror(errno));  
        return -1;  
    } else {  
        return 0;  
    }  
}

create_cache_path依据path构造/data/dalvik-cache下的文件目录,调用unlink去删除文件。到这里卸载APK的deletePackageAsUser函数就已经分析完了。这时会通过observer把卸载结果返回给UninstallAppProgre


你可能感兴趣的:(android,卸载)