• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.annotation.Nullable;
26 import android.app.admin.DevicePolicyManager;
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.text.TextUtils;
33 
34 import com.android.managedprovisioning.provisioning.Constants;
35 
36 import com.google.android.setupcompat.util.WizardManagerHelper;
37 
38 import java.util.Collection;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.stream.Collectors;
42 
43 /**
44  * Helper class for logic related to device management role holder launching.
45  */
46 public final class DeviceManagementRoleHolderHelper {
47     private static final Map<String, String> sManagedProvisioningToRoleHolderIntentAction =
48             createManagedProvisioningToRoleHolderIntentActionMap();
49 
50     private final String mRoleHolderPackageName;
51     private final PackageInstallChecker mPackageInstallChecker;
52     private final ResolveIntentChecker mResolveIntentChecker;
53     private final RoleHolderStubChecker mRoleHolderStubChecker;
54     private final FeatureFlagChecker mFeatureFlagChecker;
55 
DeviceManagementRoleHolderHelper( @ullable String roleHolderPackageName, PackageInstallChecker packageInstallChecker, ResolveIntentChecker resolveIntentChecker, RoleHolderStubChecker roleHolderStubChecker, FeatureFlagChecker featureFlagChecker)56     public DeviceManagementRoleHolderHelper(
57             @Nullable String roleHolderPackageName,
58             PackageInstallChecker packageInstallChecker,
59             ResolveIntentChecker resolveIntentChecker,
60             RoleHolderStubChecker roleHolderStubChecker,
61             FeatureFlagChecker featureFlagChecker) {
62         mRoleHolderPackageName = roleHolderPackageName;
63         mPackageInstallChecker = requireNonNull(packageInstallChecker);
64         mResolveIntentChecker = requireNonNull(resolveIntentChecker);
65         mRoleHolderStubChecker = requireNonNull(roleHolderStubChecker);
66         mFeatureFlagChecker = requireNonNull(featureFlagChecker);
67     }
68 
69     /**
70      * Returns whether the device management role holder is able to carry out the provisioning flow.
71      *
72      * <p>If this method returns {@code false}, then provisioning should be carried out by AOSP
73      * ManagedProvisioning instead.
74      *
75      * <p>If the device management role holder is a stub, it must be updated to the full device
76      * management role holder app via the device management role holder updater prior to carrying
77      * out provisioning.
78      */
isRoleHolderReadyForProvisioning( Context context, Intent managedProvisioningIntent)79     public boolean isRoleHolderReadyForProvisioning(
80             Context context, Intent managedProvisioningIntent) {
81         requireNonNull(context);
82         if (!mFeatureFlagChecker.canDelegateProvisioningToRoleHolder()) {
83             ProvisionLogger.logi("Cannot delegate provisioning to the role holder, because "
84                     + "the feature flag is turned off.");
85             return false;
86         }
87         if (!Constants.isRoleHolderProvisioningAllowedForAction(
88                 managedProvisioningIntent.getAction())) {
89             ProvisionLogger.logi("Cannot delegate provisioning to the role holder, because "
90                     + "intent action " + managedProvisioningIntent.getAction() + " is not "
91                     + "supported by the role holder.");
92             return false;
93         }
94         if (!isRoleHolderPresent(mRoleHolderPackageName, context.getPackageManager())) {
95             ProvisionLogger.logi("Cannot delegate provisioning to the role holder, because "
96                     + "the role holder is not installed.");
97             return false;
98         }
99         if (mRoleHolderStubChecker
100                 .isRoleHolderStub(mRoleHolderPackageName, context.getPackageManager())) {
101             ProvisionLogger.logi("Cannot delegate provisioning to the role holder, because "
102                     + "the role holder is a stub.");
103             return false;
104         }
105         boolean roleHolderValid =
106                 isRoleHolderValid(mRoleHolderPackageName, context.getPackageManager());
107         if (!roleHolderValid) {
108             ProvisionLogger.logi("Cannot delegate provisioning to the role holder, because "
109                     + "the role holder is not valid.");
110             return false;
111         }
112         return true;
113     }
114 
115     /**
116      * Returns a new intent with an equivalent intent action with which AOSP ManagedProvisioning
117      * was started with, which starts the device management role holder.
118      * <p>
119      * For example, if AOSP ManagedProvisioning was started with {@link
120      * DevicePolicyManager#ACTION_PROVISION_MANAGED_PROFILE}, then the resulting intent of this call
121      * will return an intent with action {@link DevicePolicyManager
122      * #ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE}.
123      *
124      * @see DevicePolicyManager#ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE
125      * @see DevicePolicyManager#ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE
126      */
createRoleHolderProvisioningIntent( Intent managedProvisioningIntent, Bundle roleHolderAdditionalExtras, @Nullable String callingPackage, @Nullable PersistableBundle roleHolderState)127     public Intent createRoleHolderProvisioningIntent(
128             Intent managedProvisioningIntent,
129             Bundle roleHolderAdditionalExtras,
130             @Nullable String callingPackage,
131             @Nullable PersistableBundle roleHolderState) {
132         requireNonNull(managedProvisioningIntent);
133         requireNonNull(roleHolderAdditionalExtras);
134         String provisioningAction = managedProvisioningIntent.getAction();
135         if (!Constants.isRoleHolderProvisioningAllowedForAction(provisioningAction)) {
136             throw new IllegalArgumentException("Intent action " + provisioningAction
137                     + " is not a valid provisioning action.");
138         }
139         if (TextUtils.isEmpty(mRoleHolderPackageName)) {
140             throw new IllegalStateException("Role holder package name is null or empty.");
141         }
142         String action = sManagedProvisioningToRoleHolderIntentAction.get(provisioningAction);
143         Intent roleHolderIntent = new Intent(action);
144         if (managedProvisioningIntent.getExtras() != null) {
145             roleHolderIntent.putExtras(managedProvisioningIntent.getExtras());
146         }
147         roleHolderIntent.setPackage(mRoleHolderPackageName);
148         if (roleHolderState != null) {
149             roleHolderIntent.putExtra(EXTRA_ROLE_HOLDER_STATE, roleHolderState);
150         }
151         if (callingPackage != null) {
152             roleHolderIntent.putExtra(
153                     EXTRA_ROLE_HOLDER_PROVISIONING_INITIATOR_PACKAGE, callingPackage);
154         }
155         roleHolderIntent.putExtras(roleHolderAdditionalExtras);
156         WizardManagerHelper.copyWizardManagerExtras(managedProvisioningIntent, roleHolderIntent);
157         return roleHolderIntent;
158     }
159 
160     /**
161      * Returns a new intent which starts the device management role holder finalization.
162      */
createRoleHolderFinalizationIntent(@ullable Intent parentActivityIntent)163     public Intent createRoleHolderFinalizationIntent(@Nullable Intent parentActivityIntent) {
164         if (TextUtils.isEmpty(mRoleHolderPackageName)) {
165             throw new IllegalStateException("Role holder package name is null or empty.");
166         }
167         Intent roleHolderIntent = new Intent(ACTION_ROLE_HOLDER_PROVISION_FINALIZATION);
168         roleHolderIntent.setPackage(mRoleHolderPackageName);
169         if (parentActivityIntent != null) {
170             WizardManagerHelper.copyWizardManagerExtras(parentActivityIntent, roleHolderIntent);
171         }
172         return roleHolderIntent;
173     }
174 
175     /**
176      * Returns {@code true} if role holder-driven provisioning is enabled.
177      *
178      * <p>For role holder-driven provisioning to be enabled, the following criteria must be
179      * met:
180      * <ul>
181      *     <li>The role holder package name must be a non-null, non-empty {@link String}</li>
182      *     <li>The role holder-driven provisioning feature flag must be enabled</li>
183      * </ul>
184      */
isRoleHolderProvisioningEnabled()185     public boolean isRoleHolderProvisioningEnabled() {
186         return !TextUtils.isEmpty(mRoleHolderPackageName)
187                 && mFeatureFlagChecker.canDelegateProvisioningToRoleHolder();
188     }
189 
isRoleHolderValid( String roleHolderPackageName, PackageManager packageManager)190     private boolean isRoleHolderValid(
191             String roleHolderPackageName,
192             PackageManager packageManager) {
193         Collection<String> requiredRoleHolderIntentActions =
194                 sManagedProvisioningToRoleHolderIntentAction.values();
195         List<String> unhandledRequiredActions = requiredRoleHolderIntentActions.parallelStream()
196                 .filter(action -> !canResolveIntent(packageManager, roleHolderPackageName, action))
197                 .collect(Collectors.toList());
198         if (!unhandledRequiredActions.isEmpty()) {
199             ProvisionLogger.logi("Role holder validation failed. Role holder does not implement "
200                     + "the following required intents: "
201                     + String.join(", ", unhandledRequiredActions));
202             return false;
203         }
204         return true;
205     }
206 
canResolveIntent( PackageManager packageManager, String roleHolderPackageName, String requiredAction)207     private boolean canResolveIntent(
208             PackageManager packageManager,
209             String roleHolderPackageName,
210             String requiredAction) {
211         Intent intent = new Intent(requiredAction);
212         intent.setPackage(roleHolderPackageName);
213         return mResolveIntentChecker.canResolveIntent(intent, packageManager);
214     }
215 
isRoleHolderPresent( String roleHolderPackageName, PackageManager packageManager)216     private boolean isRoleHolderPresent(
217             String roleHolderPackageName,
218             PackageManager packageManager) {
219         return !TextUtils.isEmpty(roleHolderPackageName)
220                 && mPackageInstallChecker.isPackageInstalled(roleHolderPackageName);
221     }
222 
createManagedProvisioningToRoleHolderIntentActionMap()223     private static Map<String, String> createManagedProvisioningToRoleHolderIntentActionMap() {
224         return Map.of(
225                 DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE,
226                 DevicePolicyManager.ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE,
227                 DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
228                 DevicePolicyManager.ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE,
229                 DevicePolicyManager.ACTION_PROVISION_FINALIZATION,
230                 ACTION_ROLE_HOLDER_PROVISION_FINALIZATION);
231     }
232 
233     /**
234      * Checker that checks whether an {@link Intent} can be resolved.
235      */
236     public interface ResolveIntentChecker {
237         /**
238          * Returns {@code true} if {@code intent} can be resolved.
239          */
canResolveIntent(Intent intent, PackageManager packageManager)240         boolean canResolveIntent(Intent intent, PackageManager packageManager);
241     }
242 
243     /**
244      * Default implementation of {@link ResolveIntentChecker}.
245      */
246     public static final class DefaultResolveIntentChecker implements ResolveIntentChecker {
247         @Override
canResolveIntent(Intent intent, PackageManager packageManager)248         public boolean canResolveIntent(Intent intent, PackageManager packageManager) {
249             return intent.resolveActivity(packageManager) != null;
250         }
251     }
252 
253     /**
254      * Checker that checks whether the role holder is a stub.
255      */
256     public interface RoleHolderStubChecker {
257         /**
258          * Returns {@code true} if the role holder with package {@code packageName} is a stub.
259          */
isRoleHolderStub(String packageName, PackageManager packageManager)260         boolean isRoleHolderStub(String packageName, PackageManager packageManager);
261     }
262 
263     /**
264      * Default implementation of {@link RoleHolderStubChecker}.
265      */
266     public static final class DefaultRoleHolderStubChecker implements RoleHolderStubChecker {
267         @Override
isRoleHolderStub(String packageName, PackageManager packageManager)268         public boolean isRoleHolderStub(String packageName, PackageManager packageManager) {
269             // TODO(b/207377785): Add check for whether the role holder is a stub
270             return false;
271         }
272     }
273 }
274