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方法。
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值