Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d805872c authored by Sarup Dalwani's avatar Sarup Dalwani
Browse files

Converting BFS to DFS for intent redirection

Based on the book-keeping required for BFS even when there are no cross
user available, to improve the performance converting this to DFS. Another optimisation is for lazy creation of book-keeping data structures for DFS.

Bug: 259125259
Test: atest CtsDomainVerificationDeviceMultiUserTestCases
Test: atest CtsAppCloningHostTest:com.android.cts.appcloning.IntentRedirectionTest
Test:
v2/android-crystalball-eng/health/microbench/instr_metric/package_manager
on forrest

Change-Id: I27e243f983ce587d4462cb89245685a14d5d5623
parent 3f967144
Loading
Loading
Loading
Loading
+132 −135
Original line number Diff line number Diff line
@@ -36,18 +36,15 @@ import android.util.FeatureFlagUtils;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;

import com.android.server.LocalServices;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import com.android.server.pm.verify.domain.DomainVerificationUtils;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.function.Function;

@@ -62,6 +59,7 @@ public class CrossProfileIntentResolverEngine {
    private final DomainVerificationManagerInternal mDomainVerificationManager;
    private final DefaultAppProvider mDefaultAppProvider;
    private final Context mContext;
    private final UserManagerInternal mUserManagerInternal;

    public CrossProfileIntentResolverEngine(UserManagerService userManager,
            DomainVerificationManagerInternal domainVerificationManager,
@@ -70,6 +68,7 @@ public class CrossProfileIntentResolverEngine {
        mDomainVerificationManager = domainVerificationManager;
        mDefaultAppProvider = defaultAppProvider;
        mContext = context;
        mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
    }

    /**
@@ -92,14 +91,14 @@ public class CrossProfileIntentResolverEngine {
            String resolvedType, int userId, long flags, String pkgName,
            boolean hasNonNegativePriorityResult,
            Function<String, PackageStateInternal> pkgSettingFunction) {
        return resolveIntentInternal(computer, intent, resolvedType, userId, flags, pkgName,
                hasNonNegativePriorityResult, pkgSettingFunction);
        return resolveIntentInternal(computer, intent, resolvedType, userId, userId, flags, pkgName,
                hasNonNegativePriorityResult, pkgSettingFunction, null);
    }

    /**
     * Resolves intent in directly linked profiles and return list of {@link CrossProfileDomainInfo}
     * which contains {@link ResolveInfo}. This would also iteratively call profiles not directly
     * linked using Breadth First Search.
     * which contains {@link ResolveInfo}. This would also recursively call profiles not directly
     * linked using Depth First Search.
     *
     * It first finds {@link CrossProfileIntentFilter} configured in current profile to find list of
     * target user profiles that can serve current intent request. It uses corresponding strategy
@@ -108,51 +107,48 @@ public class CrossProfileIntentResolverEngine {
     * @param computer {@link Computer} instance used for resolution by {@link ComponentResolverApi}
     * @param intent request
     * @param resolvedType the MIME data type of intent request
     * @param userId source user for which intent request is called
     * @param sourceUserId source user for which intent request is called
     * @param userId current user for cross profile resolution
     * @param flags used for intent resolution
     * @param pkgName the application package name this Intent is limited to.
     * @param hasNonNegativePriorityResult signifies if current profile have any non-negative(active
     *                                     and valid) ResolveInfo in current profile.
     * @param pkgSettingFunction function to find PackageStateInternal for given package
     * @param visitedUserIds users for which we have already performed resolution
     * @return list of {@link CrossProfileDomainInfo} from linked profiles.
     */
    private List<CrossProfileDomainInfo> resolveIntentInternal(@NonNull Computer computer,
            Intent intent, String resolvedType, int userId, long flags, String pkgName,
            boolean hasNonNegativePriorityResult,
            Function<String, PackageStateInternal> pkgSettingFunction) {
        Queue<Integer> pendingUsers = new ArrayDeque<>();
        Set<Integer> visitedUserIds = new HashSet<>();
        SparseBooleanArray hasNonNegativePriorityResultFromParent = new SparseBooleanArray();
        visitedUserIds.add(userId);
        pendingUsers.add(userId);
        hasNonNegativePriorityResultFromParent.put(userId, hasNonNegativePriorityResult);
        UserManagerInternal umInternal = LocalServices.getService(UserManagerInternal.class);
            Intent intent, String resolvedType, int sourceUserId, int userId, long flags,
            String pkgName, boolean hasNonNegativePriorityResult,
            Function<String, PackageStateInternal> pkgSettingFunction,
            Set<Integer> visitedUserIds) {

        if (visitedUserIds != null) visitedUserIds.add(userId);
        List<CrossProfileDomainInfo> crossProfileDomainInfos = new ArrayList<>();
        while (!pendingUsers.isEmpty()) {
            int currentUserId = pendingUsers.poll();

        List<CrossProfileIntentFilter> matchingFilters =
                computer.getMatchingCrossProfileIntentFilters(intent, resolvedType,
                            currentUserId);
                        userId);

        if (matchingFilters == null || matchingFilters.isEmpty()) {
            /** if intent is web intent, checking if parent profile should handle the intent
             * even if there is no matching filter. The configuration is based on user profile
             * restriction android.os.UserManager#ALLOW_PARENT_PROFILE_APP_LINKING **/
                if (currentUserId == userId && intent.hasWebURI()) {
                    UserInfo parent = computer.getProfileParent(currentUserId);
            if (sourceUserId == userId && intent.hasWebURI()) {
                UserInfo parent = computer.getProfileParent(userId);
                if (parent != null) {
                    CrossProfileDomainInfo generalizedCrossProfileDomainInfo = computer
                            .getCrossProfileDomainPreferredLpr(intent, resolvedType, flags,
                                        currentUserId, parent.id);
                                    userId, parent.id);
                    if (generalizedCrossProfileDomainInfo != null) {
                        crossProfileDomainInfos.add(generalizedCrossProfileDomainInfo);
                    }
                }
            }
                continue;
            return crossProfileDomainInfos;
        }

            UserInfo sourceUserInfo = umInternal.getUserInfo(currentUserId);
        UserInfo userInfo = mUserManagerInternal.getUserInfo(userId);

        // Grouping the CrossProfileIntentFilters based on targerId
        SparseArray<List<CrossProfileIntentFilter>> crossProfileIntentFiltersByUser =
@@ -170,6 +166,11 @@ public class CrossProfileIntentResolverEngine {
                    .add(crossProfileIntentFilter);
        }

        if (visitedUserIds == null) {
            visitedUserIds = new HashSet<>();
            visitedUserIds.add(userId);
        }

        /*
         For each target user, we would call their corresponding strategy
         {@link CrossProfileResolver} to resolve intent in corresponding user
@@ -183,11 +184,11 @@ public class CrossProfileIntentResolverEngine {
                continue;
            }

                UserInfo targetUserInfo = umInternal.getUserInfo(targetUserId);
            UserInfo targetUserInfo = mUserManagerInternal.getUserInfo(targetUserId);

            // Choosing strategy based on source and target user
            CrossProfileResolver crossProfileResolver =
                        chooseCrossProfileResolver(computer, sourceUserInfo, targetUserInfo);
                    chooseCrossProfileResolver(computer, userInfo, targetUserInfo);

        /*
        If {@link CrossProfileResolver} is available for source,target pair we will call it to
@@ -195,15 +196,12 @@ public class CrossProfileIntentResolverEngine {
         */
            if (crossProfileResolver != null) {
                List<CrossProfileDomainInfo> crossProfileInfos = crossProfileResolver
                            .resolveIntent(computer, intent, resolvedType, currentUserId,
                        .resolveIntent(computer, intent, resolvedType, userId,
                                targetUserId, flags, pkgName,
                                crossProfileIntentFiltersByUser.valueAt(index),
                                    hasNonNegativePriorityResultFromParent.get(currentUserId),
                                    pkgSettingFunction);
                                hasNonNegativePriorityResult, pkgSettingFunction);
                crossProfileDomainInfos.addAll(crossProfileInfos);

                    hasNonNegativePriorityResultFromParent.put(targetUserId,
                            hasNonNegativePriority(crossProfileInfos));
                visitedUserIds.add(targetUserId);

                /*
                Adding target user to queue if flag
@@ -221,10 +219,12 @@ public class CrossProfileIntentResolverEngine {
                    }
                }
                if (allowChainedResolution) {
                        pendingUsers.add(targetUserId);
                    }
                    visitedUserIds.add(targetUserId);
                    crossProfileDomainInfos.addAll(resolveIntentInternal(computer, intent,
                            resolvedType, sourceUserId, targetUserId, flags, pkgName,
                            hasNonNegativePriority(crossProfileInfos), pkgSettingFunction,
                            visitedUserIds));
                }

            }
        }

@@ -275,36 +275,32 @@ public class CrossProfileIntentResolverEngine {
    public boolean canReachTo(@NonNull Computer computer, @NonNull Intent intent,
            @Nullable String resolvedType, @UserIdInt int sourceUserId,
            @UserIdInt int targetUserId) {
        return canReachToInternal(computer, intent, resolvedType, sourceUserId, targetUserId);
        Set<Integer> visitedUserIds = new HashSet<>();
        return canReachToInternal(computer, intent, resolvedType, sourceUserId, targetUserId,
                visitedUserIds);
    }

    /**
     * Returns true if we source user can reach target user for given intent. The source can
     * directly or indirectly reach to target. This will perform breadth first search to check if
     * directly or indirectly reach to target. This will perform depth first search to check if
     * source can reach target.
     * @param computer {@link Computer} instance used for resolution by {@link ComponentResolverApi}
     * @param intent request
     * @param resolvedType the MIME data type of intent request
     * @param sourceUserId source user
     * @param targetUserId target user
     * @param visitedUserIds users for which resolution is checked
     * @return true if we source user can reach target user for given intent
     */
    private boolean canReachToInternal(@NonNull Computer computer, @NonNull Intent intent,
            @Nullable String resolvedType, @UserIdInt int sourceUserId,
            @UserIdInt int targetUserId) {
            @UserIdInt int targetUserId, Set<Integer> visitedUserIds) {
        if (sourceUserId == targetUserId) return true;

        Queue<Integer> pendingUsers = new ArrayDeque<>();
        Set<Integer> visitedUserIds = new HashSet<>();
        visitedUserIds.add(sourceUserId);
        pendingUsers.add(sourceUserId);

        while (!pendingUsers.isEmpty()) {
            int currentUserId = pendingUsers.poll();

        List<CrossProfileIntentFilter> matches =
                    computer.getMatchingCrossProfileIntentFilters(intent, resolvedType,
                            currentUserId);
                computer.getMatchingCrossProfileIntentFilters(intent, resolvedType, sourceUserId);

        if (matches != null) {
            for (int index = 0; index < matches.size(); index++) {
                CrossProfileIntentFilter crossProfileIntentFilter = matches.get(index);
@@ -323,8 +319,10 @@ public class CrossProfileIntentResolverEngine {
                 */
                if ((CrossProfileIntentFilter.FLAG_ALLOW_CHAINED_RESOLUTION
                        & crossProfileIntentFilter.mFlags) != 0) {
                        pendingUsers.add(crossProfileIntentFilter.mTargetUserId);
                    visitedUserIds.add(crossProfileIntentFilter.mTargetUserId);
                    if (canReachToInternal(computer, intent, resolvedType,
                            crossProfileIntentFilter.mTargetUserId, targetUserId, visitedUserIds)) {
                        return true;
                    }
                }
            }
@@ -628,8 +626,7 @@ public class CrossProfileIntentResolverEngine {
            categorizeResolveInfoByTargetUser, int sourceUserId, int highestApprovalLevel) {

        List<CrossProfileDomainInfo> crossProfileDomainInfos = new ArrayList<>();
        UserManagerInternal umInternal = LocalServices.getService(UserManagerInternal.class);
        UserInfo sourceUserInfo = umInternal.getUserInfo(sourceUserId);
        UserInfo sourceUserInfo = mUserManagerInternal.getUserInfo(sourceUserId);

        for (int index = 0; index < categorizeResolveInfoByTargetUser.size(); index++) {

@@ -639,7 +636,7 @@ public class CrossProfileIntentResolverEngine {
            } else {
                // finding cross profile strategy based on source and target user
                CrossProfileResolver crossProfileIntentResolver =
                        chooseCrossProfileResolver(computer, sourceUserInfo, umInternal
                        chooseCrossProfileResolver(computer, sourceUserInfo, mUserManagerInternal
                                .getUserInfo(categorizeResolveInfoByTargetUser.keyAt(index)));
                // if strategy is available call it and add its filtered results
                if (crossProfileIntentResolver != null) {