1 /*
2  * Copyright (C) 2021 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.managedprovisioning.common;
18 
19 import static android.app.admin.DevicePolicyManager.ACTION_ROLE_HOLDER_PROVISION_FINALIZATION;
20 import static android.app.admin.DevicePolicyManager.EXTRA_ROLE_HOLDER_PROVISIONING_INITIATOR_PACKAGE;
21 import static android.app.admin.DevicePolicyManager.EXTRA_ROLE_HOLDER_STATE;
22 
23 import static java.util.Objects.requireNonNull;
24 
25 import android.app.admin.DevicePolicyManager;
26 import android.app.role.RoleManager;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.pm.PackageManager;
30 import android.os.Bundle;
31 import android.os.PersistableBundle;
32 import android.os.UserHandle;
33 import android.text.TextUtils;
34 
35 import androidx.annotation.Nullable;
36 
37 import com.android.internal.annotations.VisibleForTesting;
38 import com.android.managedprovisioning.provisioning.Constants;
39 
40 import com.google.android.setupcompat.util.WizardManagerHelper;
41 
42 import java.util.Collection;
43 import java.util.List;
44 import java.util.Map;
45 import java.util.function.Consumer;
46 import java.util.stream.Collectors;
47 
48 /**
49  * Helper class for logic related to device management role holder launching.
50  */
51 public final class DeviceManagementRoleHolderHelper {
52     private static final Map<String, String> sManagedProvisioningToRoleHolderIntentAction =
53             createManagedProvisioningToRoleHolderIntentActionMap();
54 
55     @Nullable
56     private final String mRoleHolderPackageName;
57     private final PackageInstallChecker mPackageInstallChecker;
58     private final ResolveIntentChecker mResolveIntentChecker;
59     private final RoleHolderStubChecker mRoleHolderStubChecker;
60     private final FeatureFlagChecker mFeatureFlagChecker;
61     private final RoleGranter mRoleGranter;
62 
DeviceManagementRoleHolderHelper( @ullable String roleHolderPackageName, PackageInstallChecker packageInstallChecker, ResolveIntentChecker resolveIntentChecker, RoleHolderStubChecker roleHolderStubChecker, FeatureFlagChecker featureFlagChecker)63     public DeviceManagementRoleHolderHelper(
64             @Nullable String roleHolderPackageName,
65             PackageInstallChecker packageInstallChecker,
66             ResolveIntentChecker resolveIntentChecker,
67             RoleHolderStubChecker roleHolderStubChecker,
68             FeatureFlagChecker featureFlagChecker) {
69         this(
70                 roleHolderPackageName, packageInstallChecker, resolveIntentChecker,
71                 roleHolderStubChecker, featureFlagChecker,
72                 (context, user, roleName, packageName, callback) -> {
73                     var mRoleManager = requireNonNull(context.getSystemService(RoleManager.class),
74                             "Unable to obtain RoleManager");
75                     if (mRoleManager.getRoleHoldersAsUser(roleName, user).contains(packageName)) {
76                         ProvisionLogger.logi(roleName + " role is already granted to " + packageName
77                                 + " package on user " + user);
78                         callback.accept(true);
79                     } else {
80                         ProvisionLogger.logi("Granting " + roleName + " role to " + packageName
81                                 + " package on user " + user);
82                         mRoleManager.addRoleHolderAsUser(roleName, packageName, /* flags= */ 0,
83                                 user, context.getMainExecutor(), callback);
84                     }
85                 });
86     }
87 
88     @VisibleForTesting
DeviceManagementRoleHolderHelper( @ullable String roleHolderPackageName, PackageInstallChecker packageInstallChecker, ResolveIntentChecker resolveIntentChecker, RoleHolderStubChecker roleHolderStubChecker, FeatureFlagChecker featureFlagChecker, RoleGranter roleGranter)89     public DeviceManagementRoleHolderHelper(
90             @Nullable String roleHolderPackageName,
91             PackageInstallChecker packageInstallChecker,
92             ResolveIntentChecker resolveIntentChecker,
93             RoleHolderStubChecker roleHolderStubChecker,
94             FeatureFlagChecker featureFlagChecker,
95             RoleGranter roleGranter) {
96         mRoleHolderPackageName = roleHolderPackageName;
97         mPackageInstallChecker = requireNonNull(packageInstallChecker);
98         mResolveIntentChecker = requireNonNull(resolveIntentChecker);
99         mRoleHolderStubChecker = requireNonNull(roleHolderStubChecker);
100         mFeatureFlagChecker = requireNonNull(featureFlagChecker);
101         mRoleGranter = requireNonNull(roleGranter);
102     }
103 
104     /**
105      * Ensures that {@link RoleManager#ROLE_DEVICE_POLICY_MANAGEMENT} role is granted to
106      * {@link UserHandle#USER_SYSTEM}
107      *
108      * @param callback to invoke with role grant status
109      */
ensureRoleGranted( Context context, Consumer<Boolean> callback)110     public void ensureRoleGranted(
111             Context context,
112             Consumer<Boolean> callback) {
113         var packageName = requireNonNull(mRoleHolderPackageName,
114                 "Unable to ensure role grant: mRoleHolderPackageName unspecified");
115         mRoleGranter.ensureRoleGranted(context, UserHandle.of(UserHandle.USER_SYSTEM),
116                 RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT, packageName, callback);
117     }
118 
119     /**
120      * Returns whether the device management role holder is able to carry out the provisioning flow.
121      *
122      * <p>If this method returns {@code false}, then provisioning should be carried out by AOSP
123      * ManagedProvisioning instead.
124      *
125      * <p>If the device management role holder is a stub, it must be updated to the full device
126      * management role holder app via the device management role holder updater prior to carrying
127      * out provisioning.
128      */
isRoleHolderReadyForProvisioning( Context context, Intent managedProvisioningIntent)129     public boolean isRoleHolderReadyForProvisioning(
130             Context context, Intent managedProvisioningIntent) {
131         requireNonNull(context);
132         if (!mFeatureFlagChecker.canDelegateProvisioningToRoleHolder()) {
133             ProvisionLogger.logi("Cannot delegate provisioning to the role holder, because "
134                     + "the feature flag is turned off.");
135             return false;
136         }
137         if (!Constants.isRoleHolderProvisioningAllowedForAction(
138                 managedProvisioningIntent.getAction())) {
139             ProvisionLogger.logi("Cannot delegate provisioning to the role holder, because "
140                     + "intent action " + managedProvisioningIntent.getAction() + " is not "
141                     + "supported by the role holder.");
142             return false;
143         }
144         if (!isRoleHolderPresent(mRoleHolderPackageName, context.getPackageManager())) {
145             ProvisionLogger.logi("Cannot delegate provisioning to the role holder, because "
146                     + "the role holder is not installed.");
147             return false;
148         }
149         if (mRoleHolderStubChecker
150                 .isRoleHolderStub(mRoleHolderPackageName, context.getPackageManager())) {
151             ProvisionLogger.logi("Cannot delegate provisioning to the role holder, because "
152                     + "the role holder is a stub.");
153             return false;
154         }
155         boolean roleHolderValid =
156                 isRoleHolderValid(mRoleHolderPackageName, context.getPackageManager());
157         if (!roleHolderValid) {
158             ProvisionLogger.logi("Cannot delegate provisioning to the role holder, because "
159                     + "the role holder is not valid.");
160             return false;
161         }
162         return true;
163     }
164 
165     /**
166      * Returns a new intent with an equivalent intent action with which AOSP ManagedProvisioning
167      * was started with, which starts the device management role holder.
168      * <p>
169      * For example, if AOSP ManagedProvisioning was started with {@link
170      * DevicePolicyManager#ACTION_PROVISION_MANAGED_PROFILE}, then the resulting intent of this call
171      * will return an intent with action {@link DevicePolicyManager
172      * #ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE}.
173      *
174      * @see DevicePolicyManager#ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE
175      * @see DevicePolicyManager#ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE
176      */
createRoleHolderProvisioningIntent( Intent managedProvisioningIntent, Bundle roleHolderAdditionalExtras, @Nullable String callingPackage, @Nullable PersistableBundle roleHolderState)177     public Intent createRoleHolderProvisioningIntent(
178             Intent managedProvisioningIntent,
179             Bundle roleHolderAdditionalExtras,
180             @Nullable String callingPackage,
181             @Nullable PersistableBundle roleHolderState) {
182         requireNonNull(managedProvisioningIntent);
183         requireNonNull(roleHolderAdditionalExtras);
184         String provisioningAction = managedProvisioningIntent.getAction();
185         if (!Constants.isRoleHolderProvisioningAllowedForAction(provisioningAction)) {
186             throw new IllegalArgumentException("Intent action " + provisioningAction
187                     + " is not a valid provisioning action.");
188         }
189         if (TextUtils.isEmpty(mRoleHolderPackageName)) {
190             throw new IllegalStateException("Role holder package name is null or empty.");
191         }
192         String action = sManagedProvisioningToRoleHolderIntentAction.get(provisioningAction);
193         Intent roleHolderIntent = new Intent(action);
194         if (managedProvisioningIntent.getExtras() != null) {
195             roleHolderIntent.putExtras(managedProvisioningIntent.getExtras());
196         }
197         roleHolderIntent.setPackage(mRoleHolderPackageName);
198         if (roleHolderState != null) {
199             roleHolderIntent.putExtra(EXTRA_ROLE_HOLDER_STATE, roleHolderState);
200         }
201         if (callingPackage != null) {
202             roleHolderIntent.putExtra(
203                     EXTRA_ROLE_HOLDER_PROVISIONING_INITIATOR_PACKAGE, callingPackage);
204         }
205         roleHolderIntent.putExtras(roleHolderAdditionalExtras);
206         WizardManagerHelper.copyWizardManagerExtras(managedProvisioningIntent, roleHolderIntent);
207         return roleHolderIntent;
208     }
209 
210     /**
211      * Returns a new intent which starts the device management role holder finalization.
212      */
createRoleHolderFinalizationIntent(@ullable Intent parentActivityIntent)213     public Intent createRoleHolderFinalizationIntent(@Nullable Intent parentActivityIntent) {
214         if (TextUtils.isEmpty(mRoleHolderPackageName)) {
215             throw new IllegalStateException("Role holder package name is null or empty.");
216         }
217         Intent roleHolderIntent = new Intent(ACTION_ROLE_HOLDER_PROVISION_FINALIZATION);
218         roleHolderIntent.setPackage(mRoleHolderPackageName);
219         if (parentActivityIntent != null) {
220             WizardManagerHelper.copyWizardManagerExtras(parentActivityIntent, roleHolderIntent);
221         }
222         return roleHolderIntent;
223     }
224 
225     /**
226      * Returns {@code true} if role holder-driven provisioning is enabled.
227      *
228      * <p>For role holder-driven provisioning to be enabled, the following criteria must be
229      * met:
230      * <ul>
231      *     <li>The role holder package name must be a non-null, non-empty {@link String}</li>
232      *     <li>The role holder-driven provisioning feature flag must be enabled</li>
233      * </ul>
234      */
isRoleHolderProvisioningEnabled()235     public boolean isRoleHolderProvisioningEnabled() {
236         return !TextUtils.isEmpty(mRoleHolderPackageName)
237                 && mFeatureFlagChecker.canDelegateProvisioningToRoleHolder();
238     }
239 
isRoleHolderValid( @ullable String roleHolderPackageName, PackageManager packageManager)240     private boolean isRoleHolderValid(
241             @Nullable String roleHolderPackageName,
242             PackageManager packageManager) {
243         Collection<String> requiredRoleHolderIntentActions =
244                 sManagedProvisioningToRoleHolderIntentAction.values();
245         List<String> unhandledRequiredActions = requiredRoleHolderIntentActions.parallelStream()
246                 .filter(action -> !canResolveIntent(packageManager, roleHolderPackageName, action))
247                 .collect(Collectors.toList());
248         if (!unhandledRequiredActions.isEmpty()) {
249             ProvisionLogger.logi("Role holder validation failed. Role holder does not implement "
250                     + "the following required intents: "
251                     + String.join(", ", unhandledRequiredActions));
252             return false;
253         }
254         return true;
255     }
256 
canResolveIntent( PackageManager packageManager, String roleHolderPackageName, String requiredAction)257     private boolean canResolveIntent(
258             PackageManager packageManager,
259             String roleHolderPackageName,
260             String requiredAction) {
261         Intent intent = new Intent(requiredAction);
262         intent.setPackage(roleHolderPackageName);
263         return mResolveIntentChecker.canResolveIntent(intent, packageManager);
264     }
265 
isRoleHolderPresent( @ullable String roleHolderPackageName, PackageManager packageManager)266     private boolean isRoleHolderPresent(
267             @Nullable String roleHolderPackageName,
268             PackageManager packageManager) {
269         return !TextUtils.isEmpty(roleHolderPackageName)
270                 && mPackageInstallChecker.isPackageInstalled(roleHolderPackageName);
271     }
272 
createManagedProvisioningToRoleHolderIntentActionMap()273     private static Map<String, String> createManagedProvisioningToRoleHolderIntentActionMap() {
274         return Map.of(
275                 DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE,
276                 DevicePolicyManager.ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE,
277                 DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
278                 DevicePolicyManager.ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE,
279                 DevicePolicyManager.ACTION_PROVISION_FINALIZATION,
280                 ACTION_ROLE_HOLDER_PROVISION_FINALIZATION);
281     }
282 
283     /**
284      * Checker that checks whether an {@link Intent} can be resolved.
285      */
286     public interface ResolveIntentChecker {
287         /**
288          * Returns {@code true} if {@code intent} can be resolved.
289          */
canResolveIntent(Intent intent, PackageManager packageManager)290         boolean canResolveIntent(Intent intent, PackageManager packageManager);
291     }
292 
293     /**
294      * Default implementation of {@link ResolveIntentChecker}.
295      */
296     public static final class DefaultResolveIntentChecker implements ResolveIntentChecker {
297         @Override
canResolveIntent(Intent intent, PackageManager packageManager)298         public boolean canResolveIntent(Intent intent, PackageManager packageManager) {
299             return intent.resolveActivity(packageManager) != null;
300         }
301     }
302 
303     /**
304      * Checker that checks whether the role holder is a stub.
305      */
306     public interface RoleHolderStubChecker {
307         /**
308          * Returns {@code true} if the role holder with package {@code packageName} is a stub.
309          */
isRoleHolderStub(@ullable String packageName, PackageManager packageManager)310         boolean isRoleHolderStub(@Nullable String packageName, PackageManager packageManager);
311     }
312 
313     /**
314      * Default implementation of {@link RoleHolderStubChecker}.
315      */
316     public static final class DefaultRoleHolderStubChecker implements RoleHolderStubChecker {
317         @Override
isRoleHolderStub(@ullable String packageName, PackageManager packageManager)318         public boolean isRoleHolderStub(@Nullable String packageName,
319                 PackageManager packageManager) {
320             // TODO(b/207377785): Add check for whether the role holder is a stub
321             return false;
322         }
323     }
324 }
325