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         return isAppCloningBuildingBlocksEnabled(context, appCloningDeviceConfigHelper)
64                     && (resolveForStart || (((flags & PackageManager.MATCH_CLONE_PROFILE) != 0)
65                     && hasPermission(context, Manifest.permission.QUERY_CLONED_APPS)));
66     }
67 
NoFilteringResolver(ComponentResolverApi componentResolver, UserManagerService userManagerService)68     public NoFilteringResolver(ComponentResolverApi componentResolver,
69             UserManagerService userManagerService) {
70         super(componentResolver, userManagerService);
71     }
72 
73     /**
74      * This is resolution strategy for when no filtering is required.
75      * In case of clone profile, the profile is supposed to be transparent to end user. To end user
76      * clone and owner profile should be part of same user space. Hence, the resolution strategy
77      * would resolve intent in both profile and return combined result without any filtering of the
78      * results.
79      *
80      * @param computer ComputerEngine instance that would be needed by ComponentResolverApi
81      * @param intent request
82      * @param resolvedType the MIME data type of intent request
83      * @param userId source/initiating user
84      * @param targetUserId target user id
85      * @param flags of intent request
86      * @param pkgName the application package name this Intent is limited to
87      * @param matchingFilters {@link CrossProfileIntentFilter}s configured for source user,
88      *                                                        targeting the targetUserId
89      * @param hasNonNegativePriorityResult if source have any non-negative(active and valid)
90      *                                     resolveInfo in their profile.
91      * @param pkgSettingFunction function to find PackageStateInternal for given package
92      * @return list of {@link CrossProfileDomainInfo}
93      */
94     @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)95     public List<CrossProfileDomainInfo> resolveIntent(Computer computer, Intent intent,
96             String resolvedType, int userId, int targetUserId, long flags,
97             String pkgName, List<CrossProfileIntentFilter> matchingFilters,
98             boolean hasNonNegativePriorityResult,
99             Function<String, PackageStateInternal> pkgSettingFunction) {
100         List<ResolveInfo> resolveInfos = mComponentResolver.queryActivities(computer,
101                 intent, resolvedType, flags, targetUserId);
102         List<CrossProfileDomainInfo> crossProfileDomainInfos = new ArrayList<>();
103         if (resolveInfos != null) {
104 
105             for (int index = 0; index < resolveInfos.size(); index++) {
106                 crossProfileDomainInfos.add(new CrossProfileDomainInfo(resolveInfos.get(index),
107                         DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE,
108                         targetUserId));
109             }
110         }
111         return filterIfNotSystemUser(crossProfileDomainInfos, userId);
112     }
113 
114     /**
115      * In case of Clone profile, the clone and owner profile are going to be part of the same
116      * userspace, we need no filtering out of any clone profile's result.
117      * @param intent request
118      * @param crossProfileDomainInfos resolved in target user
119      * @param flags for intent resolution
120      * @param sourceUserId source user
121      * @param targetUserId target user
122      * @param highestApprovalLevel highest level of domain approval
123      * @return list of CrossProfileDomainInfo
124      */
125     @Override
filterResolveInfoWithDomainPreferredActivity(Intent intent, List<CrossProfileDomainInfo> crossProfileDomainInfos, long flags, int sourceUserId, int targetUserId, int highestApprovalLevel)126     public List<CrossProfileDomainInfo> filterResolveInfoWithDomainPreferredActivity(Intent intent,
127             List<CrossProfileDomainInfo> crossProfileDomainInfos, long flags, int sourceUserId,
128             int targetUserId, int highestApprovalLevel) {
129         // no filtering
130         return crossProfileDomainInfos;
131     }
132 
133     /**
134      * Checks if calling uid have the mentioned permission
135      * @param context calling context
136      * @param permission permission name
137      * @return true if uid have the permission
138      */
hasPermission(Context context, String permission)139     private static boolean hasPermission(Context context, String permission) {
140         return context.checkCallingOrSelfPermission(permission)
141                 == PackageManager.PERMISSION_GRANTED;
142     }
143 
144     /**
145      * Checks if the AppCloningBuildingBlocks flag is enabled.
146      */
isAppCloningBuildingBlocksEnabled(Context context, AppCloningDeviceConfigHelper appCloningDeviceConfigHelper)147     private static boolean isAppCloningBuildingBlocksEnabled(Context context,
148             AppCloningDeviceConfigHelper appCloningDeviceConfigHelper) {
149         final long token = Binder.clearCallingIdentity();
150         try {
151             return context.getResources().getBoolean(R.bool.config_enableAppCloningBuildingBlocks)
152                     && appCloningDeviceConfigHelper.getEnableAppCloningBuildingBlocks();
153         } finally {
154             Binder.restoreCallingIdentity(token);
155         }
156     }
157 }
158