1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.pm;
18 
19 import android.content.Intent;
20 import android.content.pm.ActivityInfo;
21 import android.content.pm.ResolveInfo;
22 import android.content.pm.UserInfo;
23 import android.os.Binder;
24 import android.os.UserHandle;
25 
26 import com.android.internal.util.CollectionUtils;
27 import com.android.server.pm.pkg.PackageStateInternal;
28 import com.android.server.pm.resolution.ComponentResolverApi;
29 
30 import java.util.List;
31 import java.util.function.Function;
32 
33 /**
34  * Abstract Class act as base class for Cross Profile strategy.
35  * This will be used by {@link CrossProfileIntentResolverEngine} to resolve intent across profile.
36  */
37 public abstract class CrossProfileResolver {
38 
39     protected ComponentResolverApi mComponentResolver;
40     protected UserManagerService mUserManager;
41 
CrossProfileResolver(ComponentResolverApi componentResolver, UserManagerService userManager)42     public CrossProfileResolver(ComponentResolverApi componentResolver,
43             UserManagerService userManager) {
44         mComponentResolver = componentResolver;
45         mUserManager = userManager;
46     }
47 
48     /**
49      * This method would be overridden by concrete implementation. This method should define how to
50      * resolve given intent request in target profile.
51      * @param computer ComputerEngine instance that would be needed by ComponentResolverApi
52      * @param intent request
53      * @param resolvedType the MIME data type of intent request
54      * @param userId source/initiating user
55      * @param targetUserId target user id
56      * @param flags of intent request
57      * @param pkgName package name if defined.
58      * @param matchingFilters {@link CrossProfileIntentFilter}s configured for source user,
59      *                                                        targeting the targetUserId
60      * @param hasNonNegativePriorityResult if source have any non-negative(active and valid)
61      *                                     resolveInfo in their profile.
62      * @param pkgSettingFunction function to find PackageStateInternal for given package
63      * @return list of {@link CrossProfileDomainInfo}
64      */
resolveIntent(Computer computer, Intent intent, String resolvedType, int userId, int targetUserId, long flags, String pkgName, List<CrossProfileIntentFilter> matchingFilters, boolean hasNonNegativePriorityResult, Function<String, PackageStateInternal> pkgSettingFunction)65     public abstract List<CrossProfileDomainInfo> resolveIntent(Computer computer, Intent intent,
66             String resolvedType, int userId, int targetUserId, long flags,
67             String pkgName, List<CrossProfileIntentFilter> matchingFilters,
68             boolean hasNonNegativePriorityResult,
69             Function<String, PackageStateInternal> pkgSettingFunction);
70 
71     /**
72      * Filters the CrossProfileDomainInfos, the filtering technique would be defined by concrete
73      * implementation class
74      * @param intent request
75      * @param crossProfileDomainInfos resolved in target user
76      * @param flags for intent resolution
77      * @param sourceUserId source user
78      * @param targetUserId target user
79      * @param highestApprovalLevel highest level of domain approval
80      * @return filtered list of {@link CrossProfileDomainInfo}
81      */
filterResolveInfoWithDomainPreferredActivity( Intent intent, List<CrossProfileDomainInfo> crossProfileDomainInfos, long flags, int sourceUserId, int targetUserId, int highestApprovalLevel)82     public abstract List<CrossProfileDomainInfo> filterResolveInfoWithDomainPreferredActivity(
83             Intent intent, List<CrossProfileDomainInfo> crossProfileDomainInfos, long flags,
84             int sourceUserId, int targetUserId, int highestApprovalLevel);
85 
86     /**
87      * Checks if mentioned user is enabled
88      * @param userId of requested user
89      * @return true if user is enabled
90      */
isUserEnabled(int userId)91     protected final boolean isUserEnabled(int userId) {
92         final long callingId = Binder.clearCallingIdentity();
93         try {
94             UserInfo userInfo = mUserManager.getUserInfo(userId);
95             return userInfo != null && userInfo.isEnabled();
96         } finally {
97             Binder.restoreCallingIdentity(callingId);
98         }
99     }
100 
101     /**
102      * Filters out {@link CrossProfileDomainInfo} if they are not for any user apart from system
103      * user. If mentioned user is system user, then returns all responses.
104      * @param crossProfileDomainInfos result from resolution
105      * @param userId source user id
106      * @return filtered list of {@link CrossProfileDomainInfo}
107      */
filterIfNotSystemUser( List<CrossProfileDomainInfo> crossProfileDomainInfos, int userId)108     protected final List<CrossProfileDomainInfo> filterIfNotSystemUser(
109             List<CrossProfileDomainInfo> crossProfileDomainInfos, int userId) {
110         if (userId == UserHandle.USER_SYSTEM) {
111             return crossProfileDomainInfos;
112         }
113 
114         for (int i = CollectionUtils.size(crossProfileDomainInfos) - 1; i >= 0; i--) {
115             ResolveInfo info = crossProfileDomainInfos.get(i).mResolveInfo;
116             if ((info.activityInfo.flags & ActivityInfo.FLAG_SYSTEM_USER_ONLY) != 0) {
117                 crossProfileDomainInfos.remove(i);
118             }
119         }
120         return crossProfileDomainInfos;
121     }
122 
123     /**
124      * Returns user info of parent profile is applicable
125      * @param userId requested user
126      * @return parent's user info, null if parent is not present
127      */
getProfileParent(int userId)128     protected final UserInfo getProfileParent(int userId) {
129         final long identity = Binder.clearCallingIdentity();
130         try {
131             return mUserManager.getProfileParent(userId);
132         } finally {
133             Binder.restoreCallingIdentity(identity);
134         }
135     }
136 }
137