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