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.Manifest;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.pm.PackageManager;
23 import android.content.pm.ResolveInfo;
24 import android.os.Binder;
25 
26 import com.android.internal.R;
27 import com.android.internal.config.appcloning.AppCloningDeviceConfigHelper;
28 import com.android.server.pm.pkg.PackageStateInternal;
29 import com.android.server.pm.resolution.ComponentResolverApi;
30 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
31 
32 import java.util.ArrayList;
33 import java.util.List;
34 import java.util.function.Function;
35 
36 /**
37  * Intent resolution strategy used when no filtering is required. As of now, the known use-case is
38  * clone profile.
39  */
40 public class NoFilteringResolver extends CrossProfileResolver {
41 
42     /**
43      * Feature flag to allow/restrict intent redirection from/to clone profile.
44      * Default value is false,this is to ensure that framework is not impacted by intent redirection
45      * till we are ready to launch.
46      * From Android U onwards, this would be set to true and eventually removed.
47      * @hide
48      */
49     private static final String FLAG_ALLOW_INTENT_REDIRECTION_FOR_CLONE_PROFILE =
50             "allow_intent_redirection_for_clone_profile";
51 
52     /**
53      * Returns true if intent redirection for clone profile feature flag
54      * (enable_app_cloning_building_blocks) is set and if its query,
55      * then check if calling user have necessary permission
56      * (android.permission.QUERY_CLONED_APPS) as well as required flag
57      * (PackageManager.MATCH_CLONE_PROFILE) bit set.
58      * @return true if resolver would be used for cross profile resolution.
59      */
isIntentRedirectionAllowed(Context context, AppCloningDeviceConfigHelper appCloningDeviceConfigHelper, boolean resolveForStart, long flags)60     public static boolean isIntentRedirectionAllowed(Context context,
61             AppCloningDeviceConfigHelper appCloningDeviceConfigHelper, boolean resolveForStart,
62             long flags) {
63         boolean canMatchCloneProfile = (flags & PackageManager.MATCH_CLONE_PROFILE) != 0
64                 || (flags & PackageManager.MATCH_CLONE_PROFILE_LONG) != 0;
65         return isAppCloningBuildingBlocksEnabled(context, appCloningDeviceConfigHelper)
66                 && (resolveForStart
67                     || (canMatchCloneProfile
68                         && hasPermission(context, Manifest.permission.QUERY_CLONED_APPS)));
69     }
70 
NoFilteringResolver(ComponentResolverApi componentResolver, UserManagerService userManagerService)71     public NoFilteringResolver(ComponentResolverApi componentResolver,
72             UserManagerService userManagerService) {
73         super(componentResolver, userManagerService);
74     }
75 
76     /**
77      * This is resolution strategy for when no filtering is required.
78      * In case of clone profile, the profile is supposed to be transparent to end user. To end user
79      * clone and owner profile should be part of same user space. Hence, the resolution strategy
80      * would resolve intent in both profile and return combined result without any filtering of the
81      * results.
82      *
83      * @param computer ComputerEngine instance that would be needed by ComponentResolverApi
84      * @param intent request
85      * @param resolvedType the MIME data type of intent request
86      * @param userId source/initiating user
87      * @param targetUserId target user id
88      * @param flags of intent request
89      * @param pkgName the application package name this Intent is limited to
90      * @param matchingFilters {@link CrossProfileIntentFilter}s configured for source user,
91      *                                                        targeting the targetUserId
92      * @param hasNonNegativePriorityResult if source have any non-negative(active and valid)
93      *                                     resolveInfo in their profile.
94      * @param pkgSettingFunction function to find PackageStateInternal for given package
95      * @return list of {@link CrossProfileDomainInfo}
96      */
97     @Override
resolveIntent(Computer computer, Intent intent, String resolvedType, int userId, int targetUserId, long flags, String pkgName, List<CrossProfileIntentFilter> matchingFilters, boolean hasNonNegativePriorityResult, Function<String, PackageStateInternal> pkgSettingFunction)98     public List<CrossProfileDomainInfo> resolveIntent(Computer computer, Intent intent,
99             String resolvedType, int userId, int targetUserId, long flags,
100             String pkgName, List<CrossProfileIntentFilter> matchingFilters,
101             boolean hasNonNegativePriorityResult,
102             Function<String, PackageStateInternal> pkgSettingFunction) {
103         List<ResolveInfo> resolveInfos = mComponentResolver.queryActivities(computer,
104                 intent, resolvedType, flags, targetUserId);
105         List<CrossProfileDomainInfo> crossProfileDomainInfos = new ArrayList<>();
106         if (resolveInfos != null) {
107 
108             for (int index = 0; index < resolveInfos.size(); index++) {
109                 crossProfileDomainInfos.add(new CrossProfileDomainInfo(resolveInfos.get(index),
110                         DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE,
111                         targetUserId));
112             }
113         }
114         return filterIfNotSystemUser(crossProfileDomainInfos, userId);
115     }
116 
117     /**
118      * In case of Clone profile, the clone and owner profile are going to be part of the same
119      * userspace, we need no filtering out of any clone profile's result.
120      * @param intent request
121      * @param crossProfileDomainInfos resolved in target user
122      * @param flags for intent resolution
123      * @param sourceUserId source user
124      * @param targetUserId target user
125      * @param highestApprovalLevel highest level of domain approval
126      * @return list of CrossProfileDomainInfo
127      */
128     @Override
filterResolveInfoWithDomainPreferredActivity(Intent intent, List<CrossProfileDomainInfo> crossProfileDomainInfos, long flags, int sourceUserId, int targetUserId, int highestApprovalLevel)129     public List<CrossProfileDomainInfo> filterResolveInfoWithDomainPreferredActivity(Intent intent,
130             List<CrossProfileDomainInfo> crossProfileDomainInfos, long flags, int sourceUserId,
131             int targetUserId, int highestApprovalLevel) {
132         // no filtering
133         return crossProfileDomainInfos;
134     }
135 
136     /**
137      * Checks if calling uid have the mentioned permission
138      * @param context calling context
139      * @param permission permission name
140      * @return true if uid have the permission
141      */
hasPermission(Context context, String permission)142     private static boolean hasPermission(Context context, String permission) {
143         return context.checkCallingOrSelfPermission(permission)
144                 == PackageManager.PERMISSION_GRANTED;
145     }
146 
147     /**
148      * Checks if the AppCloningBuildingBlocks flag is enabled.
149      */
isAppCloningBuildingBlocksEnabled(Context context, AppCloningDeviceConfigHelper appCloningDeviceConfigHelper)150     private static boolean isAppCloningBuildingBlocksEnabled(Context context,
151             AppCloningDeviceConfigHelper appCloningDeviceConfigHelper) {
152         final long token = Binder.clearCallingIdentity();
153         try {
154             return context.getResources().getBoolean(R.bool.config_enableAppCloningBuildingBlocks)
155                     && appCloningDeviceConfigHelper.getEnableAppCloningBuildingBlocks();
156         } finally {
157             Binder.restoreCallingIdentity(token);
158         }
159     }
160 }
161