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