Android 应用安装-提交阶段

经过前面准备浏览协调这些步骤,马上要进入提交阶段了。所谓提交,就是把这些安装应用的相关信息和状态都放到系统中。对于已安装普通应用,它其实分为两个步骤,先卸载旧包,再安装新包。当然,如果是新安装的应用包,只涉及安装新包这个步骤。

它实现在commitPackagesLocked(final CommitRequest request)中:

java 复制代码
    @GuardedBy("mLock")
    private void commitPackagesLocked(final CommitRequest request) {
        // TODO: remove any expected failures from this method; this should only be able to fail due
        //       to unavoidable errors (I/O, etc.)
        for (ReconciledPackage reconciledPkg : request.reconciledPackages.values()) {
            final ScanResult scanResult = reconciledPkg.scanResult;
            final ScanRequest scanRequest = scanResult.request;
            final ParsedPackage parsedPackage = scanRequest.parsedPackage;
            final String packageName = parsedPackage.getPackageName();
            final PackageInstalledInfo res = reconciledPkg.installResult;

            if (reconciledPkg.prepareResult.replace) {
                AndroidPackage oldPackage = mPackages.get(packageName);

                // Set the update and install times
                PackageSetting deletedPkgSetting = getPackageSetting(oldPackage.getPackageName());
                reconciledPkg.pkgSetting.firstInstallTime = deletedPkgSetting.firstInstallTime;
                reconciledPkg.pkgSetting.lastUpdateTime = System.currentTimeMillis();

                res.removedInfo.broadcastAllowList = mAppsFilter.getVisibilityAllowList(
                        reconciledPkg.pkgSetting, request.mAllUsers, mSettings.getPackagesLocked());
                if (reconciledPkg.prepareResult.system) {
                    // Remove existing system package
                    removePackageLI(oldPackage, true);
                    if (!disableSystemPackageLPw(oldPackage)) {
                        // We didn't need to disable the .apk as a current system package,
                        // which means we are replacing another update that is already
                        // installed.  We need to make sure to delete the older one's .apk.
                        res.removedInfo.args = createInstallArgsForExisting(
                                oldPackage.getPath(),
                                getAppDexInstructionSets(
                                        AndroidPackageUtils.getPrimaryCpuAbi(oldPackage,
                                                deletedPkgSetting),
                                        AndroidPackageUtils.getSecondaryCpuAbi(oldPackage,
                                                deletedPkgSetting)));
                    } else {
                        res.removedInfo.args = null;
                    }
                } else {
                    try {
                        // Settings will be written during the call to updateSettingsLI().
                        executeDeletePackageLIF(reconciledPkg.deletePackageAction, packageName,
                                true, request.mAllUsers, false, parsedPackage);
                    } catch (SystemDeleteException e) {
                        if (mIsEngBuild) {
                            throw new RuntimeException("Unexpected failure", e);
                            // ignore; not possible for non-system app
                        }
                    }
                    // Successfully deleted the old package; proceed with replace.

                    // If deleted package lived in a container, give users a chance to
                    // relinquish resources before killing.
                    if (oldPackage.isExternalStorage()) {
                        if (DEBUG_INSTALL) {
                            Slog.i(TAG, "upgrading pkg " + oldPackage
                                    + " is ASEC-hosted -> UNAVAILABLE");
                        }
                        final int[] uidArray = new int[]{oldPackage.getUid()};
                        final ArrayList<String> pkgList = new ArrayList<>(1);
                        pkgList.add(oldPackage.getPackageName());
                        sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
                    }

                    // Update the in-memory copy of the previous code paths.
                    PackageSetting ps1 = mSettings.getPackageLPr(
                            reconciledPkg.prepareResult.existingPackage.getPackageName());
                    if ((reconciledPkg.installArgs.installFlags & PackageManager.DONT_KILL_APP)
                            == 0) {
                        if (ps1.mOldCodePaths == null) {
                            ps1.mOldCodePaths = new ArraySet<>();
                        }
                        Collections.addAll(ps1.mOldCodePaths, oldPackage.getBaseApkPath());
                        if (oldPackage.getSplitCodePaths() != null) {
                            Collections.addAll(ps1.mOldCodePaths, oldPackage.getSplitCodePaths());
                        }
                    } else {
                        ps1.mOldCodePaths = null;
                    }

                    if (reconciledPkg.installResult.returnCode
                            == PackageManager.INSTALL_SUCCEEDED) {
                        PackageSetting ps2 = mSettings.getPackageLPr(
                                parsedPackage.getPackageName());
                        if (ps2 != null) {
                            res.removedInfo.removedForAllUsers = mPackages.get(ps2.name) == null;
                        }
                    }
                }
            }

            AndroidPackage pkg = commitReconciledScanResultLocked(reconciledPkg, request.mAllUsers);
            updateSettingsLI(pkg, reconciledPkg.installArgs, request.mAllUsers, res);

            final PackageSetting ps = mSettings.getPackageLPr(packageName);
            if (ps != null) {
                res.newUsers = ps.queryInstalledUsers(mUserManager.getUserIds(), true);
                ps.setUpdateAvailable(false /*updateAvailable*/);
            }
            if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                updateSequenceNumberLP(ps, res.newUsers);
                updateInstantAppInstallerLocked(packageName);
            }
        }
        ApplicationPackageManager.invalidateGetPackagesForUidCache();
    }

CommitRequest对象request的成员reconciledPackages是Map<String, ReconciledPackage>对象,它里面是对应每一个安装应用,key值是包名。该方法就是对每一个安装应用循环,接着看它的处理。

ScanResult对象scanResult、ScanRequest对象scanRequest是在 Android 安装应用-浏览阶段 中生成的对象。ParsedPackage对象parsedPackage是解析对象包,parsedPackage是安装应用的包名,PackageInstalledInfo对象res 是对应的应用的安装信息。

reconciledPkg.prepareResult.replace为true,代表是需要替换旧包进行更新。

在替换更新的情况下,先得到旧的解析包对象。mPackages是WatchedArrayMap<String, AndroidPackage>,它维持着所有的解析包对象,key值是包名。在替换之前,mPackages中的解析包对象就是旧包对象oldPackage,最后它需要被parsedPackage替换掉。deletedPkgSetting则是即将删除的PackageSetting对象,reconciledPkg.pkgSetting则是之前Android 安装应用-浏览阶段生成的新的PackageSetting对象,下面是更新新生成的PackageSetting对象的首次安装时间和最后更新时间(当前时间)。res.removedInfo.broadcastAllowList是SparseArray<int[]>类型,它的key值是用户UserId,value则是所有的应用的app Ids。request.mAllUsers在这里是所有的用户UserId数组,mAppsFilter.getVisibilityAllowList()得到所有的能看到该安装应用的应用,res.removedInfo.broadcastAllowList就是是存放的这些能看到它的应用的app Ids。

如果被替换的包是系统包,调用removePackageLI(oldPackage, true)。该方法主要是将包名对应的解析包对象从mPackages中删除,然后将旧包的声明的组件删除,声明的权限删除,所有定义的属性删除,定义的instrumentation删除,如果是系统包,还会将它引用的共享库去除。如果它声明了静态共享库,也会将它去除。

如果被替换的包是系统包,它接着会检查当前旧包使用的系统预安装的还是后续已经升级过的包。它是通过disableSystemPackageLPw(oldPackage)来判断的,如果它为true,代表当前旧包使用的系统预安装的包,是不能删除的。所以这里将删除信息的参数args = null。如果它为false,则代表后续已经升级过新的安装包,后面需要将它删除。所以创建新的删除参数(包括旧包的安装路径和主、副ABI),并将它设置为res.removedInfo.args。

如果被替换包不是系统包,它会执行executeDeletePackageLIF方法,删除应用包。删除应用包见下面分析。

删除包之后,继续往下执行,如果旧包在外部空间,它会发送一个资源改变的广播,通知用户们。它还会更新PackageSetting对象的旧包的安装路径。

executeDeletePackageLIF方法上面都是用来清除应用的,接下来就是要安装新的应用了。

它是首先调用commitReconciledScanResultLocked(reconciledPkg, request.mAllUsers) 提交修改系统中对应的状态,然后调用updateSettingsLI(pkg, reconciledPkg.installArgs, request.mAllUsers, res)用来更新PackageSetting对象的状态。

接下来,res.newUsers则是新应用提交并更新PackageSetting对象之后,得到的installed状态为true的用户id数组。还会将PackageSetting对象的updateAvailable设置为false。

目前安装成功的话,它会调用updateSequenceNumberLP()来更新对应包的更新序号,这个更新序列号就是每次更新会加1。调用updateInstantAppInstallerLocked(packageName)来更新用来安装instant应用的Activity。

最后调用ApplicationPackageManager.invalidateGetPackagesForUidCache()是使CACHE_KEY_PACKAGES_FOR_UID_PROPERTY属性缓存无效。这个属性值存储的是对应用户的安装应用包名。

下面先看看删除应用包,然后再看提交修改包应用的系统的状态、更新PackageSetting对象的状态。

删除应用包

删除应用包是由executeDeletePackageLIF方法实现的,看一下它的代码:

java 复制代码
    /** Deletes a package. Only throws when install of a disabled package fails. */
    private void executeDeletePackageLIF(DeletePackageAction action,
            String packageName, boolean deleteCodeAndResources,
            @NonNull int[] allUserHandles, boolean writeSettings,
            ParsedPackage replacingPackage) throws SystemDeleteException {
        final PackageSetting ps = action.deletingPs;
        final PackageRemovedInfo outInfo = action.outInfo;
        final UserHandle user = action.user;
        final int flags = action.flags;
        final boolean systemApp = isSystemApp(ps);

        // We need to get the permission state before package state is (potentially) destroyed.
        final SparseBooleanArray hadSuspendAppsPermission = new SparseBooleanArray();
        for (int userId : allUserHandles) {
            hadSuspendAppsPermission.put(userId, checkPermission(Manifest.permission.SUSPEND_APPS,
                    packageName, userId) == PERMISSION_GRANTED);
        }

        final int userId = user == null ? UserHandle.USER_ALL : user.getIdentifier();

        if ((!systemApp || (flags & PackageManager.DELETE_SYSTEM_APP) != 0)
                && userId != UserHandle.USER_ALL) {
            // The caller is asking that the package only be deleted for a single
            // user.  To do this, we just mark its uninstalled state and delete
            // its data. If this is a system app, we only allow this to happen if
            // they have set the special DELETE_SYSTEM_APP which requests different
            // semantics than normal for uninstalling system apps.
            final boolean clearPackageStateAndReturn;
            synchronized (mLock) {
                markPackageUninstalledForUserLPw(ps, user);
                if (!systemApp) {
                    // Do not uninstall the APK if an app should be cached
                    boolean keepUninstalledPackage = shouldKeepUninstalledPackageLPr(packageName);
                    if (ps.isAnyInstalled(mUserManager.getUserIds()) || keepUninstalledPackage) {
                        // Other users still have this package installed, so all
                        // we need to do is clear this user's data and save that
                        // it is uninstalled.
                        if (DEBUG_REMOVE) Slog.d(TAG, "Still installed by other users");
                        clearPackageStateAndReturn = true;
                    } else {
                        // We need to set it back to 'installed' so the uninstall
                        // broadcasts will be sent correctly.
                        if (DEBUG_REMOVE) Slog.d(TAG, "Not installed by other users, full delete");
                        ps.setInstalled(true, userId);
                        mSettings.writeKernelMappingLPr(ps);
                        clearPackageStateAndReturn = false;
                    }
                } else {
                    // This is a system app, so we assume that the
                    // other users still have this package installed, so all
                    // we need to do is clear this user's data and save that
                    // it is uninstalled.
                    if (DEBUG_REMOVE) Slog.d(TAG, "Deleting system app");
                    clearPackageStateAndReturn = true;
                }
            }
            if (clearPackageStateAndReturn) {
                clearPackageStateForUserLIF(ps, userId, outInfo, flags);
                synchronized (mLock) {
                    scheduleWritePackageRestrictionsLocked(user);
                }
                return;
            }
        }

        // TODO(b/109941548): break reasons for ret = false out into mayDelete method
        if (systemApp) {
            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
            deleteSystemPackageLIF(action, ps, allUserHandles, flags, outInfo, writeSettings);
        } else {
            if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package: " + ps.name);
            deleteInstalledPackageLIF(ps, deleteCodeAndResources, flags, allUserHandles,
                    outInfo, writeSettings);
        }

        // If the package removed had SUSPEND_APPS, unset any restrictions that might have been in
        // place for all affected users.
        int[] affectedUserIds = (outInfo != null) ? outInfo.removedUsers : null;
        if (affectedUserIds == null) {
            affectedUserIds = resolveUserIds(userId);
        }
        for (final int affectedUserId : affectedUserIds) {
            if (hadSuspendAppsPermission.get(affectedUserId)) {
                unsuspendForSuspendingPackage(packageName, affectedUserId);
                removeAllDistractingPackageRestrictions(affectedUserId);
            }
        }

        // Take a note whether we deleted the package for all users
        if (outInfo != null) {
            outInfo.removedForAllUsers = mPackages.get(ps.name) == null;
        }
    }

DeletePackageAction类型对象action是关于要删除包行为的,action.deletingPs就是要删除的PackageSetting对象,action.outInfo是删除的信息,action.user是用户,action.flags是删除标识,变量systemApp代表是系统APP。

hadSuspendAppsPermission里面存储每个用户对应的包名packageName的应用是否已经授权过SUSPEND_APPS权限。

action.user是需要删除的应用的用户,在我们的例子中,这里是null(可以参考 Android 应用安装-协调阶段),也就是代表所有用户。

接下来这段代码是是处理某个用户删除的情况(userId != UserHandle.USER_ALL),还要满足不是系统APP或者设置了删除系统app标识PackageManager.DELETE_SYSTEM_APP。

变量clearPackageStateAndReturn代表清除包的对应的这个单独用户的数据之后,就返回,不继续向下执行。

markPackageUninstalledForUserLPw(ps, user)用来标记用户的卸载状态,PackageSetting对象会维护每个用户的用户状态,这个用户状态是用PackageUserState对象来表示的。这里主要将PackageUserState对象的installed状态设置为false。

接着对非系统APP进行处理。

变量keepUninstalledPackage代表保持卸载包。它是由shouldKeepUninstalledPackageLPr(packageName)来决定的,而该方法是由成员变量mKeepUninstalledPackages来判断的,如果包名在它里面,即为true。像这种情况,会将变量clearPackageStateAndReturn = true。还有一种情况,其他用户还有这个包的installed状态(即ps.isAnyInstalled(mUserManager.getUserIds())为true,上面通过markPackageUninstalledForUserLPw(ps, user)将user的installed设置为false),也会clearPackageStateAndReturn = true,代表需要将该用户的数据清除就返回,其他用户的数据是不能清除的。如果上面两个情况不满足,调用ps.setInstalled(true, userId)将该用户对应的installed设置为true,因为刚才通过markPackageUninstalledForUserLPw(ps, user)将用户对应的installed设置为false,并且也判断过ps.isAnyInstalled(mUserManager.getUserIds())了,所以在这里是将它的值还原。mSettings.writeKernelMappingLPr(ps)则是处理Settings对象中维持的KernelPackageState对象,它包含着应用的appId和installed状态已经是false的用户(维持在PackageSetting对象中)。最后将clearPackageStateAndReturn = false。

接着对系统APP进行处理。直接是将clearPackageStateAndReturn = true,它是假定其他用户有这个系统应用的installed状态,所以只清理该单独用户的。

上面处理之后,变量clearPackageStateAndReturn为true,代表只需要清理这个userId这个单独用户的数据就行,它是由clearPackageStateForUserLIF()实现,见下面

之后调用scheduleWritePackageRestrictionsLocked(user),它首先执行invalidatePackageInfoCache(),让一些缓存无效(包信息的缓存,包含PackageManagerService对象的Computer缓存),接着向PackageManagerService对象的后台ServiceThread发送一个延迟(10s)消息WRITE_PACKAGE_RESTRICTIONS,而在ServiceThread中处理该消息时,会调用Settings对象的writePackageRestrictionsLPr(userId)方法来处理,它和"/data/system/users/" + userId + "/package-restrictions.xml" 及 "/data/system/users/" + userId + "/package-restrictions-backup.xml"两个文件有关,后面这个文件是前一个文件的备份。writePackageRestrictionsLPr(userId)方法主要是将用户对应的各个应用包状态、较好的Activity信息、持久较好的Activity信息、intent配置信息、默认的浏览器包名等相关信息写入"/package-restrictions.xml"文件。这些处理完毕,就直接return,不再继续向下执行。

再接下来如果clearPackageStateAndReturn为false,或者参数user == null的情况下,它会继续向下执行代码,还是分系统APP还是非系统APP的情况。

系统APP,会执行deleteSystemPackageLIF(action, ps, allUserHandles, flags, outInfo, writeSettings);非系统APP会执行deleteInstalledPackageLIF(ps, deleteCodeAndResources, flags, allUserHandles, outInfo, writeSettings)。这俩方法的区别是,删除系统应用包时,它会继续使用系统分区中的安装包,所以在它删除了更新包之后,会执行安装系统分区中的安装包。deleteInstalledPackageLIF(ps, deleteCodeAndResources, flags, allUserHandles, outInfo, writeSettings)的实现见下面

接下来executeDeletePackageLIF方法会继续检查被删除应用如果被授予Manifest.permission.SUSPEND_APPS权限,这里会去除它对别的应用暂停的影响。

这样executeDeletePackageLIF方法就看完了。

下面看下clearPackageStateForUserLIF(ps, userId, outInfo, flags),它是用来清理userId的数据。看下实现的代码:

删除包对应用户的数据

java 复制代码
    private void clearPackageStateForUserLIF(PackageSetting ps, int userId,
            PackageRemovedInfo outInfo, int flags) {
        final AndroidPackage pkg;
        synchronized (mLock) {
            pkg = mPackages.get(ps.name);
        }

        destroyAppProfilesLIF(pkg);

        final SharedUserSetting sus = ps.getSharedUser();
        List<AndroidPackage> sharedUserPkgs = sus != null ? sus.getPackages() : null;
        if (sharedUserPkgs == null) {
            sharedUserPkgs = Collections.emptyList();
        }
        final int[] userIds = (userId == UserHandle.USER_ALL) ? mUserManager.getUserIds()
                : new int[] {userId};
        for (int nextUserId : userIds) {
            if (DEBUG_REMOVE) {
                Slog.d(TAG, "Updating package:" + ps.name + " install state for user:"
                        + nextUserId);
            }
            if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
                destroyAppDataLIF(pkg, nextUserId,
                        FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL);
            }
            removeKeystoreDataIfNeeded(mInjector.getUserManagerInternal(), nextUserId, ps.appId);
            clearPackagePreferredActivities(ps.name, nextUserId);
            mDomainVerificationManager.clearPackageForUser(ps.name, nextUserId);
        }
        mPermissionManager.onPackageUninstalled(ps.name, ps.appId, pkg, sharedUserPkgs, userId);

        if (outInfo != null) {
            if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
                outInfo.dataRemoved = true;
            }
            outInfo.removedPackage = ps.name;
            outInfo.installerPackageName = ps.installSource.installerPackageName;
            outInfo.isStaticSharedLib = pkg != null && pkg.getStaticSharedLibName() != null;
            outInfo.removedAppId = ps.appId;
            outInfo.removedUsers = userIds;
            outInfo.broadcastUsers = userIds;
        }
    }

在这里得到mPackages中对应的包名的安装包信息。

接着调用destroyAppProfilesLIF(pkg),将包对应的配置文件删除。因为它要与installd 进程通信(实现在InstalldNativeService.cpp中的destroyAppProfiles(const std::string& packageName)中),跳转代码太多,直接说实现的内容了,对"/data/user/"目录下面的用户遍历userid,删除 "/data/misc/profiles/cur/" + userid + "/" + package_name 目录及其里面的文件,如果没在"/data/user/"目录下面找到userid,就只找寻userid为0的用户,也就是删除"/data/misc/profiles/cur/0/" + package_name 目录及其中的文件,接着删除"/data/misc/profiles/ref/" + package_name 目录及其里面的文件。

接着向下如果userId == UserHandle.USER_ALL(即-1),代表所有用户,所以取出来所有用户id;如果是单个用户id,则将单个用户放在数组中,赋值给数组userIds。

遍历用户数组userIds开始>>>>>>

遍历用户数组userIds,如果标识没有PackageManager.DELETE_KEEP_DATA,会调用destroyAppDataLIF方法,它是清除所有的APP数据。它根据标识做了以下三件事:

1、如果设置了FLAG_STORAGE_CE标识,它会清除用户的包数据。用户的数据存放位置和存储卷UUID、包名、用户ID有关的。如果存储卷UUID为null,则代表内部存储,那它的根目录为"/data",如果不为null,根目录为"/mnt/expand/" + volume_uuid。如果特定用户userid,则路径为 根目录 + "/user/" + userid + "/" + 包名。还有一种常见情况为内部存储,用户userid为0,如果 "/data/data"存在,并且是目录,则路径为"/data/data/" + 包名。所以这里会将这个用户目录包括其中的数据都清除掉。

2、如果设置了FLAG_STORAGE_DE标识,它会清除用户的设备加密的数据。它的路径文件路径和上面有些像,不过它的路径中的"/user/"换成了"/user_de/"。即为根目录 + "/user_de/" + userid + "/" + 包名。还以内部存储,用户userid为例,则它的路径为"/data/user_de/0/" + 包名。这里会将该目录及里面文件都删除掉。如果标识没有设置FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES,它还会删除应用和用户对应的配置文件,这和前面destroyAppProfilesLIF(pkg)不一样的是,它是删除某个用户id的。

3、如果设置了FLAG_STORAGE_EXTERNAL标识,它需要清除外部存储上该应用的存储数据。外部存储空间和用户相关的路径目录有哪些呢,它也是和用户id和包名相关。外部空间根目录是从"/proc/mounts"中读取出来的,外部空间根目录 + "/" + userId即是和用户相关的目录,暂称外部空间用户根目录extUserDir。具体删除的路径有以下几个:extUserDir + "/Android/data/" + 包名、extUserDir + "/Android/media/" + 包名、extUserDir + "/Android/obb/" + 包名。

继续看clearPackageStateForUserLIF方法中的执行方法,调用removeKeystoreDataIfNeeded方法,它是在APP卸载时,通知Keystore 2.0,并且对应的命名空间被删除。

接着是调用clearPackagePreferredActivities(ps.name, nextUserId),清除这个nextUserId用户包名等于ps.name的较好的Activity。它们是维护在Settings对象中的成员变量mPreferredActivities中。所以这块就是把符合要求的对应用户的Activity从它里面去除。如果确实有需要删除的对应用户的Activity,它接下来会调用updateDefaultHomeNotLocked(changedUsers)来更新对应用户默认的Home,默认的Home的包名是PackageManagerService对象的成员变量DefaultAppProvider mDefaultAppProvider维持的,如果mDefaultAppProvider维持的Home包名和查询得到的nextUserId的当前较好的Activity不同,会更新Home包名,并且发送nextUserId用户Intent.ACTION_PREFERRED_ACTIVITY_CHANGED广播。

如果确实有需要删除的对应用户的Activity,clearPackagePreferredActivities(ps.name, nextUserId)还会执行scheduleWritePackageRestrictionsLocked(userId)方法,它首先执行invalidatePackageInfoCache(),让一些缓存无效(包信息的缓存,包含PackageManagerService对象的Computer缓存)。接着向PackageManagerService对象的后台ServiceThread发送一个延迟(10s)消息WRITE_PACKAGE_RESTRICTIONS,而在ServiceThread中处理该消息时,会调用Settings对象的writePackageRestrictionsLPr(userId)方法来处理,它和"/data/system/users/" + userId + "/package-restrictions.xml" 及 "/data/system/users/" + userId + "/package-restrictions-backup.xml"两个文件有关,后面这个文件是前一个文件的备份。writePackageRestrictionsLPr(userId)方法主要是将用户对应的各个应用包状态、较好的Activity信息、持久较好的Activity信息、intent配置信息、默认的浏览器包名等相关信息写入"/package-restrictions.xml"文件。

回到clearPackageStateForUserLIF方法中,还是在循环遍历中,它接着执行mDomainVerificationManager.clearPackageForUser(ps.name, nextUserId)。mDomainVerificationManager在这里是DomainVerificationService对象,在这里它是用来清除对应应用和对应用户的域名验证状态。

遍历用户数组userIds结束<<<<<<

上面是遍历用户的循环看完了,下面是调用mPermissionManager.onPackageUninstalled(ps.name, ps.appId, pkg, sharedUserPkgs, userId)。mPermissionManager实际是PermissionManagerServiceInternalImpl对象,这块是包要卸载了,需要对相应权限进行处理。

下面outInfo对象则是对清理状况的一种描述信息。包括删除的包名,删除包名的安装应用,数据是否删除(是否调用了destroyAppDataLIF方法)、是否是静态库、去除了哪个用户的包信息。

删除安装包

它是由deleteInstalledPackageLIF()实现的,看下它实现的代码:

java 复制代码
    private void deleteInstalledPackageLIF(PackageSetting ps,
            boolean deleteCodeAndResources, int flags, @NonNull int[] allUserHandles,
            PackageRemovedInfo outInfo, boolean writeSettings) {
        synchronized (mLock) {
            if (outInfo != null) {
                outInfo.uid = ps.appId;
                outInfo.broadcastAllowList = mAppsFilter.getVisibilityAllowList(ps,
                        allUserHandles, mSettings.getPackagesLocked());
            }
        }

        // Delete package data from internal structures and also remove data if flag is set
        removePackageDataLIF(ps, allUserHandles, outInfo, flags, writeSettings);

        // Delete application code and resources only for parent packages
        if (deleteCodeAndResources && (outInfo != null)) {
            outInfo.args = createInstallArgsForExisting(
                    ps.getPathString(), getAppDexInstructionSets(
                            ps.primaryCpuAbiString, ps.secondaryCpuAbiString));
            if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args);
        }
    }

deleteInstalledPackageLIF()方法会给PackageRemovedInfo对象outInfo中的相关字段赋值,主要就是调用removePackageDataLIF(ps, allUserHandles, outInfo, flags, writeSettings)方法。如果参数deleteCodeAndResources为true,代表需要删除代码和资源包。所以构造outInfo.args对象,它是FileInstallArgs对象,包含安装包路径和指令集。

removePackageDataLIF()会做如下工作,会给PackageRemovedInfo对象outInfo中的相关字段赋值,包括removedUsers。接着会调用removePackageLI()方法,它会去除包相关的数据。如果参数标识flags没有PackageManager.DELETE_KEEP_DATA,它还会调用destroyAppDataLIF(),见上面destroyAppProfilesLIF(),见上面来删除对应应用包的相关文件。

如果参数标识flags没有PackageManager.DELETE_KEEP_DATA,它还会用来清除对应应用的域名验证状态,应用的keySet,它还会调用mSettings.removePackageLPw(packageName),清除成员变量mSettings中的PackageSetting对象并将与其相关的内容一并清除。如果不是系统包,它也会调用mPermissionManager.onPackageUninstalled(packageName, deletedPs.appId, deletedPs.pkg, sharedUserPkgs, UserHandle.USER_ALL)把它的权限状态给去除。接着会调用clearPackagePreferredActivitiesLPw(deletedPs.name, changedUsers, UserHandle.USER_ALL),清除这个nextUserId用户包名等于ps.name的较好的Activity。它们是维护在Settings对象中的成员变量mPreferredActivities中。所以这块就是把符合要求的Activity从它里面去除。如果在清除了用户的较好的Activity时,那接着就需要更新这个用户的默认home。然后发送较好Activity发生变化的广播(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED)。

removePackageDataLIF()还会根据boolean型参数writeSettings,是否保存Settings类型对象中的状态。还会从密钥库删除对应应用的密钥。

提交修改包应用的系统的状态

commitReconciledScanResultLocked(reconciledPkg, request.mAllUsers)的代码如下:

java 复制代码
    /**
     * Commits the package scan and modifies system state.
     * <p><em>WARNING:</em> The method may throw an excpetion in the middle
     * of committing the package, leaving the system in an inconsistent state.
     * This needs to be fixed so, once we get to this point, no errors are
     * possible and the system is not left in an inconsistent state.
     */
    @GuardedBy({"mLock", "mInstallLock"})
    private AndroidPackage commitReconciledScanResultLocked(
            @NonNull ReconciledPackage reconciledPkg, int[] allUsers) {
        final ScanResult result = reconciledPkg.scanResult;
        final ScanRequest request = result.request;
        // TODO(b/135203078): Move this even further away
        ParsedPackage parsedPackage = request.parsedPackage;
        if ("android".equals(parsedPackage.getPackageName())) {
            // TODO(b/135203078): Move this to initial parse
            parsedPackage.setVersionCode(mSdkVersion)
                    .setVersionCodeMajor(0);
        }

        final AndroidPackage oldPkg = request.oldPkg;
        final @ParseFlags int parseFlags = request.parseFlags;
        final @ScanFlags int scanFlags = request.scanFlags;
        final PackageSetting oldPkgSetting = request.oldPkgSetting;
        final PackageSetting originalPkgSetting = request.originalPkgSetting;
        final UserHandle user = request.user;
        final String realPkgName = request.realPkgName;
        final List<String> changedAbiCodePath = result.changedAbiCodePath;
        final PackageSetting pkgSetting;
        if (request.pkgSetting != null && request.pkgSetting.sharedUser != null
                && request.pkgSetting.sharedUser != result.pkgSetting.sharedUser) {
            // shared user changed, remove from old shared user
            request.pkgSetting.sharedUser.removePackage(request.pkgSetting);
        }
        if (result.existingSettingCopied) {
            pkgSetting = request.pkgSetting;
            pkgSetting.updateFrom(result.pkgSetting);
        } else {
            pkgSetting = result.pkgSetting;
            if (originalPkgSetting != null) {
                mSettings.addRenamedPackageLPw(parsedPackage.getRealPackage(),
                        originalPkgSetting.name);
                mTransferredPackages.add(originalPkgSetting.name);
            } else {
                mSettings.removeRenamedPackageLPw(parsedPackage.getPackageName());
            }
        }
        if (pkgSetting.sharedUser != null) {
            pkgSetting.sharedUser.addPackage(pkgSetting);
        }
        if (reconciledPkg.installArgs != null && reconciledPkg.installArgs.forceQueryableOverride) {
            pkgSetting.forceQueryableOverride = true;
        }

        // If this is part of a standard install, set the initiating package name, else rely on
        // previous device state.
        if (reconciledPkg.installArgs != null) {
            InstallSource installSource = reconciledPkg.installArgs.installSource;
            if (installSource.initiatingPackageName != null) {
                final PackageSetting ips = mSettings.getPackageLPr(
                        installSource.initiatingPackageName);
                if (ips != null) {
                    installSource = installSource.setInitiatingPackageSignatures(
                            ips.signatures);
                }
            }
            pkgSetting.setInstallSource(installSource);
        }

        // TODO(toddke): Consider a method specifically for modifying the Package object
        // post scan; or, moving this stuff out of the Package object since it has nothing
        // to do with the package on disk.
        // We need to have this here because addUserToSettingLPw() is sometimes responsible
        // for creating the application ID. If we did this earlier, we would be saving the
        // correct ID.
        parsedPackage.setUid(pkgSetting.appId);
        final AndroidPackage pkg = parsedPackage.hideAsFinal();

        mSettings.writeUserRestrictionsLPw(pkgSetting, oldPkgSetting);

        if (realPkgName != null) {
            mTransferredPackages.add(pkg.getPackageName());
        }

        if (reconciledPkg.collectedSharedLibraryInfos != null) {
            executeSharedLibrariesUpdateLPr(pkg, pkgSetting, null, null,
                    reconciledPkg.collectedSharedLibraryInfos, allUsers);
        }

        final KeySetManagerService ksms = mSettings.getKeySetManagerService();
        if (reconciledPkg.removeAppKeySetData) {
            ksms.removeAppKeySetDataLPw(pkg.getPackageName());
        }
        if (reconciledPkg.sharedUserSignaturesChanged) {
            pkgSetting.sharedUser.signaturesChanged = Boolean.TRUE;
            pkgSetting.sharedUser.signatures.mSigningDetails = reconciledPkg.signingDetails;
        }
        pkgSetting.signatures.mSigningDetails = reconciledPkg.signingDetails;

        if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) {
            for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) {
                final String codePathString = changedAbiCodePath.get(i);
                try {
                    mInstaller.rmdex(codePathString,
                            getDexCodeInstructionSet(getPreferredInstructionSet()));
                } catch (InstallerException ignored) {
                }
            }
        }

        final int userId = user == null ? 0 : user.getIdentifier();
        // Modify state for the given package setting
        commitPackageSettings(pkg, oldPkg, pkgSetting, oldPkgSetting, scanFlags,
                (parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0 /*chatty*/, reconciledPkg);
        if (pkgSetting.getInstantApp(userId)) {
            mInstantAppRegistry.addInstantAppLPw(userId, pkgSetting.appId);
        }
        pkgSetting.setStatesOnCommit();

        return pkg;
    }

所需要的数据都封装在参数reconciledPkg对象中,ScanResult对象、ScanRequest对象都是前面文章 Android 安装应用-浏览阶段 中生成的,现在通过reconciledPkg对象取出来。其中parsedPackage是解析出来的包对象,oldPkgSetting是包的旧设置信息对象。originalPkgSetting是原来的包设置对象,realPkgName是包的真名字,这俩变量是和改包名相关的,如果没有改包名这俩变量为null,如果originalPkgSetting不为null代表是首次安装更改包名的应用。changedAbiCodePath是包共享应用中改变了abi的应用的路径。

接着如果包共享用户发生了变化,需要从原来的共享应用包中去除之前的应用。

如果result.existingSettingCopied为true,则result.pkgSetting是通过深copy request.pkgSetting创建的对象,并且它里面的值有些可能发生了变化,所以这里,通过updateFrom方法来更新它的值。如果result.existingSettingCopied为false,则result.pkgSetting是通过new新创建的对象,这里直接将result.pkgSetting赋值给变量pkgSetting。接着如果originalPkgSetting != null,代表包名进行了更改,所以这里通过mSettings.addRenamedPackageLPw(parsedPackage.getRealPackage(), originalPkgSetting.name)将现在的包名和之前的旧包名建立对应关系(是通过mSettings的成员mRenamedPackages实现),还会将旧包名放入变量mTransferredPackages中,代表它的包名发生了变更。如果originalPkgSetting为null,代表没有变更,所以这里将包名从mSettings的成员mRenamedPackages中去除。

如果应用是共享用户,这里会将包设置对象添加到共享用户中。

如果安装参数中forceQueryableOverride为true,这里会将包设置对象属性forceQueryableOverride设置为true。

接着设置包设置对象的安装源信息。这里是处理了一下安装者应用的签名信息。

再接下来给解析包对象设置应用UId。

在这里会调用解析包对象parsedPackage的hideAsFinal()方法,这里parsedPackage实际是PackageImpl对象,

接着调用mSettings.writeUserRestrictionsLPw(pkgSetting, oldPkgSetting) 它是用来持久化应用的一些状态的,不过在这里,普通的应用安装应该不会去更新,因为现在包已经从mSettings中去除了,还没有加入其中。

接着如果realPkgName不为null,将包名加入mTransferredPackages中。前面的文章 Android 安装应用-浏览阶段,如果realPkgName不为null,则pkg.getPackageName()为旧包名,这里将它加入mTransferredPackages中,代表它有新包名。

如果reconciledPkg.collectedSharedLibraryInfos != null,代表应用声明了库更新信息,所以这里执行库的更新。

reconciledPkg.removeAppKeySetData为true,代表需要更新应用的签名key。所以这里先将它们删除,后续再更新新的签名key。

reconciledPkg.sharedUserSignaturesChanged为true,代表共享用户的签名发生了变化,这里更新对应的包设置对象的共享用户签名。

还会更新包设置对象的签名。

changedAbiCodePath.size() > 0,代表共享用户中有些用的ABI发生了变化,这里会将对应的dex文件删除。

接着会根据变量user的值来设置局部变量userId的值,如果为null,将userId设置为0,也即System用户。

解下来就是调用commitPackageSettings()方法来修改对应包的状态了,详细见下面。

如果应用是InstantApp,则会将应用的安装信息添加到mInstantAppRegistry中。

最后调用包设置对象的setStatesOnCommit()方法,提交这个包的安装状态。最后将解析包对象返回。

修改对应包的状态

看一下commitPackageSettings()方法的实现

java 复制代码
    /**
     * Adds a scanned package to the system. When this method is finished, the package will
     * be available for query, resolution, etc...
     */
    private void commitPackageSettings(@NonNull AndroidPackage pkg, @Nullable AndroidPackage oldPkg,
            @NonNull PackageSetting pkgSetting, @Nullable PackageSetting oldPkgSetting,
            final @ScanFlags int scanFlags, boolean chatty, ReconciledPackage reconciledPkg) {
        final String pkgName = pkg.getPackageName();
        if (mCustomResolverComponentName != null &&
                mCustomResolverComponentName.getPackageName().equals(pkg.getPackageName())) {
            setUpCustomResolverActivity(pkg, pkgSetting);
        }

        if (pkg.getPackageName().equals("android")) {
            synchronized (mLock) {
                // Set up information for our fall-back user intent resolution activity.
                mPlatformPackage = pkg;

                // The instance stored in PackageManagerService is special cased to be non-user
                // specific, so initialize all the needed fields here.
                mAndroidApplication = pkg.toAppInfoWithoutState();
                mAndroidApplication.flags = PackageInfoUtils.appInfoFlags(pkg, pkgSetting);
                mAndroidApplication.privateFlags =
                        PackageInfoUtils.appInfoPrivateFlags(pkg, pkgSetting);
                mAndroidApplication.initForUser(UserHandle.USER_SYSTEM);

                if (!mResolverReplaced) {
                    mResolveActivity.applicationInfo = mAndroidApplication;
                    mResolveActivity.name = ResolverActivity.class.getName();
                    mResolveActivity.packageName = mAndroidApplication.packageName;
                    mResolveActivity.processName = "system:ui";
                    mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
                    mResolveActivity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NEVER;
                    mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
                    mResolveActivity.theme = R.style.Theme_Material_Dialog_Alert;
                    mResolveActivity.exported = true;
                    mResolveActivity.enabled = true;
                    mResolveActivity.resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
                    mResolveActivity.configChanges = ActivityInfo.CONFIG_SCREEN_SIZE
                            | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE
                            | ActivityInfo.CONFIG_SCREEN_LAYOUT
                            | ActivityInfo.CONFIG_ORIENTATION
                            | ActivityInfo.CONFIG_KEYBOARD
                            | ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
                    mResolveInfo.activityInfo = mResolveActivity;
                    mResolveInfo.priority = 0;
                    mResolveInfo.preferredOrder = 0;
                    mResolveInfo.match = 0;
                    mResolveComponentName = new ComponentName(
                            mAndroidApplication.packageName, mResolveActivity.name);
                }
                onChanged();
            }
        }

        ArrayList<AndroidPackage> clientLibPkgs = null;
        // writer
        synchronized (mLock) {
            if (!ArrayUtils.isEmpty(reconciledPkg.allowedSharedLibraryInfos)) {
                for (SharedLibraryInfo info : reconciledPkg.allowedSharedLibraryInfos) {
                    commitSharedLibraryInfoLocked(info);
                }
                final Map<String, AndroidPackage> combinedSigningDetails =
                        reconciledPkg.getCombinedAvailablePackages();
                try {
                    // Shared libraries for the package need to be updated.
                    updateSharedLibrariesLocked(pkg, pkgSetting, null, null,
                            combinedSigningDetails);
                } catch (PackageManagerException e) {
                    Slog.e(TAG, "updateSharedLibrariesLPr failed: ", e);
                }
                // Update all applications that use this library. Skip when booting
                // since this will be done after all packages are scaned.
                if ((scanFlags & SCAN_BOOTING) == 0) {
                    clientLibPkgs = updateAllSharedLibrariesLocked(pkg, pkgSetting,
                            combinedSigningDetails);
                }
            }
        }
        if (reconciledPkg.installResult != null) {
            reconciledPkg.installResult.libraryConsumers = clientLibPkgs;
        }

        if ((scanFlags & SCAN_BOOTING) != 0) {
            // No apps can run during boot scan, so they don't need to be frozen
        } else if ((scanFlags & SCAN_DONT_KILL_APP) != 0) {
            // Caller asked to not kill app, so it's probably not frozen
        } else if ((scanFlags & SCAN_IGNORE_FROZEN) != 0) {
            // Caller asked us to ignore frozen check for some reason; they
            // probably didn't know the package name
        } else {
            // We're doing major surgery on this package, so it better be frozen
            // right now to keep it from launching
            checkPackageFrozen(pkgName);
        }

        // Also need to kill any apps that are dependent on the library.
        if (clientLibPkgs != null) {
            for (int i=0; i<clientLibPkgs.size(); i++) {
                AndroidPackage clientPkg = clientLibPkgs.get(i);
                killApplication(clientPkg.getPackageName(),
                        clientPkg.getUid(), "update lib");
            }
        }

        // writer
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");

        synchronized (mLock) {
            // We don't expect installation to fail beyond this point
            // Add the new setting to mSettings
            mSettings.insertPackageSettingLPw(pkgSetting, pkg);
            // Add the new setting to mPackages
            mPackages.put(pkg.getPackageName(), pkg);
            if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) {
                mApexManager.registerApkInApex(pkg);
            }

            // Add the package's KeySets to the global KeySetManagerService
            KeySetManagerService ksms = mSettings.getKeySetManagerService();
            ksms.addScannedPackageLPw(pkg);

            mComponentResolver.addAllComponents(pkg, chatty);
            final boolean isReplace =
                    reconciledPkg.prepareResult != null && reconciledPkg.prepareResult.replace;
            mAppsFilter.addPackage(pkgSetting, isReplace);
            mPackageProperty.addAllProperties(pkg);

            if (oldPkgSetting == null || oldPkgSetting.getPkg() == null) {
                mDomainVerificationManager.addPackage(pkgSetting);
            } else {
                mDomainVerificationManager.migrateState(oldPkgSetting, pkgSetting);
            }

            int collectionSize = ArrayUtils.size(pkg.getInstrumentations());
            StringBuilder r = null;
            int i;
            for (i = 0; i < collectionSize; i++) {
                ParsedInstrumentation a = pkg.getInstrumentations().get(i);
                a.setPackageName(pkg.getPackageName());
                mInstrumentation.put(a.getComponentName(), a);
                if (chatty) {
                    if (r == null) {
                        r = new StringBuilder(256);
                    } else {
                        r.append(' ');
                    }
                    r.append(a.getName());
                }
            }
            if (r != null) {
                if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Instrumentation: " + r);
            }

            final List<String> protectedBroadcasts = pkg.getProtectedBroadcasts();
            if (!protectedBroadcasts.isEmpty()) {
                synchronized (mProtectedBroadcasts) {
                    mProtectedBroadcasts.addAll(protectedBroadcasts);
                }
            }

            mPermissionManager.onPackageAdded(pkg, (scanFlags & SCAN_AS_INSTANT_APP) != 0, oldPkg);
        }

        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }

前面这两段代码主要是处理mResolveActivity、mResolveInfo成员变量的。可见平台的包名应用是"android",并且会将mPlatformPackage指向应用包对象。

我们主要看下面的代码,它是处理共享库的代码。

reconciledPkg.allowedSharedLibraryInfos是应用声明的库。它不为空的情况下,遍历它,调用commitSharedLibraryInfoLocked(info)是将库信息加入成员mSharedLibraries中,如果是静态库,还会加入成员mStaticLibsByDeclaringPackage中。

接着调用updateSharedLibrariesLocked(pkg, pkgSetting, null, null, combinedSigningDetails) 会将它使用的类库添加到它声明的库中。

接着如果不是系统启动中的情况,它会调用updateAllSharedLibrariesLocked(pkg, pkgSetting, combinedSigningDetails)更新所有使用该库的应用。并且返回值clientLibPkgs为所有使用该类库的应用包。

下面还会将clientLibPkgs赋值给reconciledPkg.installResult.libraryConsumers。

接着在系统启动中或scanFlags有SCAN_DONT_KILL_APP或SCAN_IGNORE_FROZEN标签的情况下,不检查应用的冻结状态,其他情况,需要调用checkPackageFrozen(pkgName)来检查。

接下来,如果存在使用更新库的应用,需要将这些应用杀掉。它调用的是killApplication(clientPkg.getPackageName(), clientPkg.getUid(), "update lib")方法。

接下来,就是调用mSettings.insertPackageSettingLPw(pkgSetting, pkg),将包设置对象pkgSetting加入mSettings中。它还会处理,如果pkgSetting中的签名信息不在,需要从pkg.getSigningDetails()取得,赋值到pkgSetting中的签名信息。最主要的是将pkgSetting对象添加到mSettings中的成员变量mPackages中,它是WatchedArrayMap<String, PackageSetting>类型,key是包名,value是PackageSetting对象。如果是共享用户,在这里会将PackageSetting对象添加到共享SharedUserSetting对象中。

调用mPackages.put(pkg.getPackageName(), pkg)将包解析对象加入mPackages中。

下面就是将包的KeySet添加到KeySetManagerService对象中。它调用的是ksms.addScannedPackageLPw(pkg)方法。

再下面就是调用mComponentResolver.addAllComponents(pkg, chatty)添加包应用的组件信息,包含Activity、BroadCastReceiver、Service、Provider。

mAppsFilter.addPackage(pkgSetting, isReplace)是用来处理APP之间可见性的。

mPackageProperty.addAllProperties(pkg)添加包定义的所有属性值。

接下来是处理包名配置的域名验证。它主要是处理配置的Action为Intent.ACTION_VIEW、Category为Intent.CATEGORY_BROWSABLE的Activity其中的域名验证。

下面是将包声明的Instrumentation添加到成员mInstrumentation中。

将包中的受保护的广播添加到成员mProtectedBroadcasts中。

最后调用mPermissionManager.onPackageAdded(pkg, (scanFlags & SCAN_AS_INSTANT_APP) != 0, oldPkg),通知应用权限的添加。

更新PackageSetting对象的状态

updateSettingsInternalLI()方法是用来更新PackageSetting对象的相关数据的,接下来看看updateSettingsInternalLI()方法:

java 复制代码
    private void updateSettingsInternalLI(AndroidPackage pkg, InstallArgs installArgs,
            int[] allUsers, PackageInstalledInfo res) {
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");

        final String pkgName = pkg.getPackageName();
        final int[] installedForUsers = res.origUsers;
        final int installReason = installArgs.installReason;
        InstallSource installSource = installArgs.installSource;
        final String installerPackageName = installSource.installerPackageName;

        if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + pkg.getPath());
        synchronized (mLock) {
            // For system-bundled packages, we assume that installing an upgraded version
            // of the package implies that the user actually wants to run that new code,
            // so we enable the package.
            final PackageSetting ps = mSettings.getPackageLPr(pkgName);
            final int userId = installArgs.user.getIdentifier();
            if (ps != null) {
                if (pkg.isSystem()) {
                    if (DEBUG_INSTALL) {
                        Slog.d(TAG, "Implicitly enabling system package on upgrade: " + pkgName);
                    }
                    // Enable system package for requested users
                    if (res.origUsers != null) {
                        for (int origUserId : res.origUsers) {
                            if (userId == UserHandle.USER_ALL || userId == origUserId) {
                                ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT,
                                        origUserId, installerPackageName);
                            }
                        }
                    }
                    // Also convey the prior install/uninstall state
                    if (allUsers != null && installedForUsers != null) {
                        for (int currentUserId : allUsers) {
                            final boolean installed = ArrayUtils.contains(
                                    installedForUsers, currentUserId);
                            if (DEBUG_INSTALL) {
                                Slog.d(TAG, "    user " + currentUserId + " => " + installed);
                            }
                            ps.setInstalled(installed, currentUserId);
                        }
                        // these install state changes will be persisted in the
                        // upcoming call to mSettings.writeLPr().
                    }

                    if (allUsers != null) {
                        for (int currentUserId : allUsers) {
                            ps.resetOverrideComponentLabelIcon(currentUserId);
                        }
                    }
                }

                // Retrieve the overlays for shared libraries of the package.
                if (!ps.getPkgState().getUsesLibraryInfos().isEmpty()) {
                    for (SharedLibraryInfo sharedLib : ps.getPkgState().getUsesLibraryInfos()) {
                        for (int currentUserId : UserManagerService.getInstance().getUserIds()) {
                            if (!sharedLib.isDynamic()) {
                                // TODO(146804378): Support overlaying static shared libraries
                                continue;
                            }
                            final PackageSetting libPs = mSettings.getPackageLPr(
                                    sharedLib.getPackageName());
                            if (libPs == null) {
                                continue;
                            }
                            ps.setOverlayPathsForLibrary(sharedLib.getName(),
                                    libPs.getOverlayPaths(currentUserId), currentUserId);
                        }
                    }
                }

                // It's implied that when a user requests installation, they want the app to be
                // installed and enabled. (This does not apply to USER_ALL, which here means only
                // install on users for which the app is already installed).
                if (userId != UserHandle.USER_ALL) {
                    ps.setInstalled(true, userId);
                    ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName);
                }

                mSettings.addInstallerPackageNames(ps.installSource);

                // When replacing an existing package, preserve the original install reason for all
                // users that had the package installed before. Similarly for uninstall reasons.
                final Set<Integer> previousUserIds = new ArraySet<>();
                if (res.removedInfo != null && res.removedInfo.installReasons != null) {
                    final int installReasonCount = res.removedInfo.installReasons.size();
                    for (int i = 0; i < installReasonCount; i++) {
                        final int previousUserId = res.removedInfo.installReasons.keyAt(i);
                        final int previousInstallReason = res.removedInfo.installReasons.valueAt(i);
                        ps.setInstallReason(previousInstallReason, previousUserId);
                        previousUserIds.add(previousUserId);
                    }
                }
                if (res.removedInfo != null && res.removedInfo.uninstallReasons != null) {
                    for (int i = 0; i < res.removedInfo.uninstallReasons.size(); i++) {
                        final int previousUserId = res.removedInfo.uninstallReasons.keyAt(i);
                        final int previousReason = res.removedInfo.uninstallReasons.valueAt(i);
                        ps.setUninstallReason(previousReason, previousUserId);
                    }
                }

                // Set install reason for users that are having the package newly installed.
                final int[] allUsersList = mUserManager.getUserIds();
                if (userId == UserHandle.USER_ALL) {
                    // TODO(b/152629990): It appears that the package doesn't actually get newly
                    //  installed in this case, so the installReason shouldn't get modified?
                    for (int currentUserId : allUsersList) {
                        if (!previousUserIds.contains(currentUserId)) {
                            ps.setInstallReason(installReason, currentUserId);
                        }
                    }
                } else if (!previousUserIds.contains(userId)) {
                    ps.setInstallReason(installReason, userId);
                }

                // TODO(b/169721400): generalize Incremental States and create a Callback object
                // that can be used for all the packages.
                final String codePath = ps.getPathString();
                if (IncrementalManager.isIncrementalPath(codePath) && mIncrementalManager != null) {
                    final IncrementalStatesCallback incrementalStatesCallback =
                            new IncrementalStatesCallback(ps.name,
                                    UserHandle.getUid(userId, ps.appId),
                                    getInstalledUsers(ps, userId));
                    ps.setIncrementalStatesCallback(incrementalStatesCallback);
                    mIncrementalManager.registerLoadingProgressCallback(codePath,
                            new IncrementalProgressListener(ps.name));
                }

                // Ensure that the uninstall reason is UNKNOWN for users with the package installed.
                for (int currentUserId : allUsersList) {
                    if (ps.getInstalled(currentUserId)) {
                        ps.setUninstallReason(UNINSTALL_REASON_UNKNOWN, currentUserId);
                    }
                }

                mSettings.writeKernelMappingLPr(ps);

                final PermissionManagerServiceInternal.PackageInstalledParams.Builder
                        permissionParamsBuilder =
                        new PermissionManagerServiceInternal.PackageInstalledParams.Builder();
                final boolean grantPermissions = (installArgs.installFlags
                        & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0;
                if (grantPermissions) {
                    final List<String> grantedPermissions =
                            installArgs.installGrantPermissions != null
                                    ? Arrays.asList(installArgs.installGrantPermissions)
                                    : pkg.getRequestedPermissions();
                    permissionParamsBuilder.setGrantedPermissions(grantedPermissions);
                }
                final boolean allowlistAllRestrictedPermissions =
                        (installArgs.installFlags
                                & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS) != 0;
                final List<String> allowlistedRestrictedPermissions =
                        allowlistAllRestrictedPermissions ? pkg.getRequestedPermissions()
                        : installArgs.whitelistedRestrictedPermissions;
                if (allowlistedRestrictedPermissions != null) {
                    permissionParamsBuilder.setAllowlistedRestrictedPermissions(
                            allowlistedRestrictedPermissions);
                }
                final int autoRevokePermissionsMode = installArgs.autoRevokePermissionsMode;
                permissionParamsBuilder.setAutoRevokePermissionsMode(autoRevokePermissionsMode);
                mPermissionManager.onPackageInstalled(pkg, permissionParamsBuilder.build(), userId);
            }
            res.name = pkgName;
            res.uid = pkg.getUid();
            res.pkg = pkg;
            res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
            //to update install status
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "writeSettings");
            writeSettingsLPrTEMP();
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }

        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }

res.origUsers是在 Android 安装应用-准备阶段 赋值的,它是PackageSetting对象中 installe状态为true的用户,这里赋值给installedForUsers变量。

局部变量userId是安装参数中的用户id。

从mSettings中取得PackageSetting对象ps。

如果安装包pkg是系统安装包,会设定原来的installe状态为true的用户的enabled状态为COMPONENT_ENABLED_STATE_DEFAULT。还会把原来的installe状态为true的用户的installe状态继续为true,其他的用户设置为false。还会调用PackageSetting对象的resetOverrideComponentLabelIcon(currentUserId),重设组件的label和icon。

如果ps.getPkgState().getUsesLibraryInfos()不为null,并且它的库位动态的,则会调用ps.setOverlayPathsForLibrary(sharedLib.getName(), libPs.getOverlayPaths(currentUserId), currentUserId)将动态库的遮罩路径设置到它里面

如果安装用户是一个固定用户,不是指所有用户,则将它的installe状态为true,enabled状态为COMPONENT_ENABLED_STATE_DEFAULT。

ps.installSource是和安装源相关的对象,这里调用mSettings.addInstallerPackageNames(ps.installSource)将安装者包名添加到mSettings中。

接着就是处理用户的安装原因、卸载原因。可以看到,它先恢复之前的安装原因、卸载原因。对于那些installe状态为true的用户,则设置它的卸载原因为UNINSTALL_REASON_UNKNOWN。

下面是处理权限相关的。后面通过mPermissionManager.onPackageInstalled(pkg, permissionParamsBuilder.build(), userId)通知应用包安装了。

最后设置res的name、uid、解析包对象。ReturnCode是安装结果。PackageSetting对象更新了之后,调用writeSettingsLPrTEMP()来持久化相应的状态。

总结

在安装过程中有两个类AndroidPackage和PackageSetting,AndroidPackage类对象实际是PackageImpl对象,它是通过解析应用安装包得到的,包含安装包的所有配置(对应Manifest配置文件中的内容)。PackageSetting对象则是包数据设置对象,主要是后续设置的一些状态,像应用对每个用户的状态(例安装、停止状态),主要、次要CPU ABI,另外PackageSetting对象的成员pkg是指向PackageImpl对象的。

PackageManagerService对象中成员mPackages是WatchedArrayMap<String, AndroidPackage>,它的key是包名,value则是AndroidPackage对象,安装成功之后,PackageImpl对象是要添加到mPackages中的。

PackageManagerService对象中成员mSettings是Settings类。Settings类有成员变量mPackages,它是WatchedArrayMap<String, PackageSetting>,它的key是包名,value则是PackageSetting对象,安装成功之后,PackageSetting对象是要添加到Settings类的成员变量mPackages中的。

这两个类对象的处理,可以从文章中找到。

当然安装中还会将各大组件添加到系统中,这个和PackageManagerService对象中成员mComponentResolver相关,后续查找组件,也会通过该成员变量。

其实挺复杂的是权限的处理,这个等待后面再整理,该篇文章也是疏通了流程,精细的地方还待仔细琢磨。

相关推荐
晨春计15 分钟前
【git】
android·linux·git
标标大人1 小时前
c语言中的局部跳转以及全局跳转
android·c语言·开发语言
木鬼与槐2 小时前
MySQL高阶1783-大满贯数量
android·数据库·mysql
iofomo2 小时前
【Abyss】Android 平台应用级系统调用拦截框架
android·开发工具·移动端
AirDroid_cn5 小时前
在家找不到手机?除了语音助手,还可以用远程控制!
android·智能手机·远程控制·手机使用技巧·远程控制手机
Good_tea_h12 小时前
Android中如何处理运行时权限?
android
冬田里的一把火312 小时前
[Android][Reboot/Shutdown] 重启/关机 分析
android·gitee
大海..13 小时前
Android 系统开发人员的权限说明文档
android
技术无疆16 小时前
ButterKnife:Android视图绑定的简化专家
android·java·android studio·android-studio·androidx·butterknife·视图绑定
JohnsonXin17 小时前
【兼容性记录】video标签在 IOS 和 安卓中的问题
android·前端·css·ios·h5·兼容性