该系列文章总纲链接:专题分纲目录 Android Framework 包管理子系统


本章关键点总结 & 说明:

Android Framework 包管理子系统(04)应用卸载_数据

导图是不断迭代的,这里主要关注➕ PkgMS卸载应用部分。主要是删除应用的文件、数据和缓存数据。

卸载应用和安装应用的分析流程是极其类似的,因此会感觉有些代码有些重复但却不同,也是从adb uninstall命令开始分析。

1 卸载应用程序(从 adb unInstall 到PkgMS)

一般我们会使用adb uninstall 来安装应用,因此这里就从adb uninstall开始分析,adb命令,uninstall参数。直接看adb命令 处理install参数的代码,如下所示:

int adb_commandline(int argc, char **argv)
{
    char buf[4096];
    //...
    if (!strcmp(argv[0], "install")) {
        if (argc < 2) return usage();
        return install_app(ttype, serial, argc, argv);
    }
	//...
    if (!strcmp(argv[0], "uninstall")) {
        if (argc < 2) return usage();
        return uninstall_app(ttype, serial, argc, argv);
    }
	//...
    usage();
    return 1;
}

这里继续分析uninstall_app实现,代码如下:

int uninstall_app(transport_type transport, char* serial, int argc, char** argv)
{
    if (argc == 3 && strcmp(argv[1], "-k") == 0)
    {
        return -1;
    }
    return pm_command(transport, serial, argc, argv);
}

这里比安装apk简单,主要关注pm_command方法(下面这一小段与上节相同),代码如下:

static int pm_command(transport_type transport, char* serial,
                      int argc, char** argv)
{
    char buf[4096];

    snprintf(buf, sizeof(buf), "shell:pm");

    while(argc-- > 0) {
        char *quoted = escape_arg(*argv++);
        strncat(buf, " ", sizeof(buf) - 1);
        strncat(buf, quoted, sizeof(buf) - 1);
        free(quoted);
    }

    send_shellcommand(transport, serial, buf);
    return 0;
}

这里调用了send_shellcommand 发送"shell:pm uninstall 参数"给手机端的adbd,这里继续分析pm命令,pm是脚本文件,构成如下:

# Script to start "pm" on the device,which has a very rudimentary
# shell.
#

base=/system
export CLASSPATH=$base/frameworks/pm.jar
exec app_process $base/bincom.android.commands.pm.Pm "$@"

在编译system镜像时,Android.mk中会将该脚本复制到system/bin目录下。从pm脚本的内容来看,它就是通过app_process执行pm.jar包的main函数。pm的main函数实现如下:

public static void main(String[] args) {
    int exitCode = 1;
    exitCode = new Pm().run(args);
    //...
    System.exit(exitCode);
}

这里的run方法实现如下(这里主要关注uninstall命令):

public int run(String[] args) throws IOException, RemoteException {
    //...
    //获取UserM 和 PkgMS的binder客户端
    mUm = IUserManager.Stub.asInterface(ServiceManager.getService("user"));
    mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
    //...
    mInstaller = mPm.getPackageInstaller();
    //...
    //处理其他命令,这里仅考虑uninstall的处理
    if ("uninstall".equals(op)) {
        return runUninstall();
    }
    //...
}

这里仅仅关注uninstall部分相关代码,runUninstall代码实现如下:

private int runUninstall() throws RemoteException {
    int flags = 0;
    int userId = UserHandle.USER_ALL;

    //参数与错误处理...
    if (userId == UserHandle.USER_ALL) {
        userId = UserHandle.USER_OWNER;
        flags |= PackageManager.DELETE_ALL_USERS;
    } else {
        PackageInfo info;
        try {
            //获取package信息
            info = mPm.getPackageInfo(pkg, 0, userId);
        } catch (RemoteException e) {
            //...
        }
        //...
        final boolean isSystem =
                (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
        if (isSystem) {
            flags |= PackageManager.DELETE_SYSTEM_APP;
        }
    }

    final LocalIntentReceiver receiver = new LocalIntentReceiver();
    //调用PackageInstallerService的uninstall方法来处理
    mInstaller.uninstall(pkg, flags, receiver.getIntentSender(), userId);

    final Intent result = receiver.getResult();
    final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
            PackageInstaller.STATUS_FAILURE);
    //...
}

 

继续分析PackageInstallerService的uninstall方法,代码如下:

public void uninstall(String packageName, int flags, IntentSender statusReceiver, int userId) {
    mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, true, "uninstall");

    final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext,
            statusReceiver, packageName);
    if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES)
            == PackageManager.PERMISSION_GRANTED) {
        //关键点,这里调用了PkgMS的deletePackage方法
        mPm.deletePackage(packageName, adapter.getBinder(), userId, flags);

    } else {
        // Take a short detour to confirm with user
        final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
        intent.setData(Uri.fromParts("package", packageName, null));
        intent.putExtra(PackageInstaller.EXTRA_CALLBACK, adapter.getBinder().asBinder());
        adapter.onUserActionRequired(intent);
    }
}

至此,从adb unistall就调用到了PkgMS的deletePackage方法。接下来会从deletePackage开始继续分析。

2 卸载应用程序(删除文件)

deletePackage函数代码实现如下:

public void deletePackage(final String packageName,
        final IPackageDeleteObserver2 observer, final int userId, final int flags) {
    //检查执行权限
    mContext.enforceCallingOrSelfPermission(
            android.Manifest.permission.DELETE_PACKAGES, null);
    final int uid = Binder.getCallingUid();
    if (UserHandle.getUserId(uid) != userId) {
        mContext.enforceCallingPermission(
                android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
                "deletePackage for user " + userId);
    }
    //权限错误相关处理...
    //...
    
    // Queue up an async operation since the package deletion may take a little while.
    mHandler.post(new Runnable() {//post消息,在处理方法中执行卸载应用操作
        public void run() {
            mHandler.removeCallbacks(this);
            //调用deletePackageX执行卸载应用操作
            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
    });
}

这里最后post了一个消息,在消息处理中继续执行卸载操作,这样避免了Binder调用的时间过长;实际卸载通过deletePackageX来完成,代码实现如下:

private int deletePackageX(String packageName, int userId, int flags) {
    final PackageRemovedInfo info = new PackageRemovedInfo();
    final boolean res;

    final UserHandle removeForUser = (flags & PackageManager.DELETE_ALL_USERS) != 0
            ? UserHandle.ALL : new UserHandle(userId);
    //检查用户removeForUser 是否有权限删除该应用
    if (isPackageDeviceAdmin(packageName, removeForUser.getIdentifier())) {
        return PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER;
    }

    boolean removedForAllUsers = false;
    boolean systemUpdate = false;

    // for the uninstall-updates case and restricted profiles, remember the per-
    // userhandle installed state
    int[] allUsers;
    boolean[] perUserInstalled;
    synchronized (mPackages) {
        //获取setting信息
        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, removeForUser,
                true, allUsers, perUserInstalled,
                flags | REMOVE_CHATTY, info, true);
        //...
    }

    if (res) {
        //如果卸载的是某个应用升级包,这里会导致低版本的系统应用重新使用
        //代码发送广播通知这种变化。
        info.sendBroadcast(true, systemUpdate, removedForAllUsers);

        // If the removed package was a system update, the old system package
        // was re-enabled; we need to broadcast this information
        if (systemUpdate) {
            Bundle extras = new Bundle(1);
            extras.putInt(Intent.EXTRA_UID, info.removedAppId >= 0
                    ? info.removedAppId : info.uid);
            extras.putBoolean(Intent.EXTRA_REPLACING, true);

            sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
                    extras, null, null, null);
            sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
                    extras, null, null, null);
            sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null,
                    null, packageName, null, null);
        }
    }
    //清理相关工作
    Runtime.getRuntime().gc();
    if (info.args != null) {
        synchronized (mInstallLock) {
            info.args.doPostDeleteLI(true);
        }
    }
    return res ? PackageManager.DELETE_SUCCEEDED : PackageManager.DELETE_FAILED_INTERNAL_ERROR;
}

该方法搜集了应用在不同用户下的安装情况后,调用deletePackageLI方法继续执行删除操作。如果删除的是某个系统应用的升级包,这里还会发出广播通知以前的应用可以重新使用。deletePackageLI代码实现如下:

private boolean deletePackageLI(String packageName, UserHandle user,...) {
    //...
    PackageSetting ps;
    boolean dataOnly = false;
    int removeUser = -1;
    int appId = -1;
    synchronized (mPackages) {
        ps = mSettings.mPackages.get(packageName);
        //...
        if ((!isSystemApp(ps) || (flags&PackageManager.DELETE_SYSTEM_APP) != 0) && user != null
                && user.getIdentifier() != UserHandle.USER_ALL) {
            //卸载某个用户下的应用
            ps.setUserState(user.getIdentifier(),...);
            if (!isSystemApp(ps)) {
                if (ps.isAnyInstalled(sUserManager.getUserIds())) {
                    removeUser = user.getIdentifier();
                    appId = ps.appId;
                    mSettings.writePackageRestrictionsLPr(removeUser);
                } else {
                    //没有用户使用,完全删除应用
                    ps.setInstalled(true, user.getIdentifier());
                }
            } else {
                //只清除用户目录下的数据
                removeUser = user.getIdentifier();
                appId = ps.appId;
                mSettings.writePackageRestrictionsLPr(removeUser);
            }
        }
    }
    //...
    if (dataOnly) {
        //删除应用数据
        removePackageDataLI(ps, null, null, outInfo, flags, writeSettings);
        return true;
    }
    boolean ret = false;
    if (isSystemApp(ps)) {
        //删除系统应用
        ret = deleteSystemPackageLI(ps, allUserHandles, perUserInstalled,
                flags, outInfo, writeSettings);
    } else {
        // 停止应用进程,删除文件
        killApplication(packageName, ps.appId, "uninstall pkg");
        ret = deleteInstalledPackageLI(ps, deleteCodeAndResources, flags,...);
    }
    return ret;
}

这里主要工作是根据不同用户的安装情况来删除应用的apk文件和数据。删除的不仅有文件和数据,还要将PkgMS中缓存的应用数据也清除掉。