1 /* 2 * Copyright (C) 2020 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.parser; 18 19 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE; 20 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE; 21 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE; 22 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE; 23 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_TRIGGER; 24 import static android.app.admin.DevicePolicyManager.FLAG_SUPPORTED_MODES_DEVICE_OWNER; 25 import static android.app.admin.DevicePolicyManager.FLAG_SUPPORTED_MODES_ORGANIZATION_OWNED; 26 import static android.app.admin.DevicePolicyManager.FLAG_SUPPORTED_MODES_PERSONALLY_OWNED; 27 import static android.app.admin.DevicePolicyManager.MIME_TYPE_PROVISIONING_NFC; 28 import static android.app.admin.DevicePolicyManager.PROVISIONING_MODE_FULLY_MANAGED_DEVICE; 29 import static android.app.admin.DevicePolicyManager.PROVISIONING_MODE_MANAGED_PROFILE; 30 import static android.app.admin.DevicePolicyManager.PROVISIONING_MODE_MANAGED_PROFILE_ON_PERSONAL_DEVICE; 31 import static android.app.admin.DevicePolicyManager.PROVISIONING_TRIGGER_UNSPECIFIED; 32 import static android.nfc.NfcAdapter.ACTION_NDEF_DISCOVERED; 33 34 import static com.android.managedprovisioning.common.Globals.ACTION_PROVISION_MANAGED_DEVICE_SILENTLY; 35 import static com.android.managedprovisioning.model.ProvisioningParams.DEFAULT_EXTRA_PROVISIONING_SUPPORTED_MODES; 36 37 import android.app.admin.DevicePolicyManager; 38 import android.content.Context; 39 import android.content.Intent; 40 import android.content.pm.PackageManager; 41 import android.util.ArraySet; 42 43 import com.android.managedprovisioning.common.IllegalProvisioningArgumentException; 44 import com.android.managedprovisioning.common.ProvisionLogger; 45 import com.android.managedprovisioning.common.SettingsFacade; 46 import com.android.managedprovisioning.common.Utils; 47 48 import java.util.ArrayList; 49 import java.util.List; 50 51 /** 52 * A utility class with methods related to parsing the provisioning extras 53 */ 54 public class ParserUtils { 55 56 private static final ArraySet<Integer> ALLOWED_COMBINATIONS = new ArraySet<>(); 57 { 58 ALLOWED_COMBINATIONS.add(FLAG_SUPPORTED_MODES_ORGANIZATION_OWNED); 59 ALLOWED_COMBINATIONS.add(FLAG_SUPPORTED_MODES_PERSONALLY_OWNED); 60 ALLOWED_COMBINATIONS.add(FLAG_SUPPORTED_MODES_DEVICE_OWNER); 61 ALLOWED_COMBINATIONS.add( 62 FLAG_SUPPORTED_MODES_ORGANIZATION_OWNED | FLAG_SUPPORTED_MODES_PERSONALLY_OWNED); 63 } 64 65 /** 66 * Returns the provisioning trigger supplied in the provisioning extras only if it was supplied 67 * alongside the {@link DevicePolicyManager#ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} 68 * intent action. Otherwise it returns {@link 69 * DevicePolicyManager#PROVISIONING_TRIGGER_UNSPECIFIED}. 70 */ extractProvisioningTrigger(Intent intent)71 int extractProvisioningTrigger(Intent intent) { 72 if (!ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE.equals(intent.getAction())) { 73 return PROVISIONING_TRIGGER_UNSPECIFIED; 74 } 75 return intent.getIntExtra( 76 EXTRA_PROVISIONING_TRIGGER, PROVISIONING_TRIGGER_UNSPECIFIED); 77 } 78 79 /** 80 * Translates a given managed provisioning intent to its corresponding provisioning flow, using 81 * the action from the intent. 82 * 83 * <p>This is necessary because, unlike other provisioning actions which has 1:1 mapping, there 84 * are multiple actions that can trigger the device owner provisioning flow. This includes 85 * {@link ACTION_PROVISION_MANAGED_DEVICE}, {@link ACTION_NDEF_DISCOVERED} and 86 * {@link ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}. These 3 actions are equivalent 87 * except they are sent from a different source. 88 * 89 * @return the appropriate DevicePolicyManager declared action for the given incoming intent. 90 * @throws IllegalProvisioningArgumentException if intent is malformed 91 */ extractProvisioningAction(Intent intent, SettingsFacade settingsFacade, Context context)92 String extractProvisioningAction(Intent intent, 93 SettingsFacade settingsFacade, Context context) 94 throws IllegalProvisioningArgumentException { 95 if (intent == null || intent.getAction() == null) { 96 throw new IllegalProvisioningArgumentException("Null intent action."); 97 } 98 99 // Map the incoming intent to a DevicePolicyManager.ACTION_*, as there is a N:1 mapping in 100 // some cases. 101 switch (intent.getAction()) { 102 // Trivial cases. 103 case ACTION_PROVISION_MANAGED_DEVICE: 104 case ACTION_PROVISION_MANAGED_PROFILE: 105 case ACTION_PROVISION_FINANCED_DEVICE: 106 return intent.getAction(); 107 108 // Silent device owner is same as device owner. 109 case ACTION_PROVISION_MANAGED_DEVICE_SILENTLY: 110 return ACTION_PROVISION_MANAGED_DEVICE; 111 112 // NFC cases which need to take mime-type into account. 113 case ACTION_NDEF_DISCOVERED: 114 String mimeType = intent.getType(); 115 if (mimeType == null) { 116 throw new IllegalProvisioningArgumentException( 117 "Unknown NFC bump mime-type: " + mimeType); 118 } 119 switch (mimeType) { 120 case MIME_TYPE_PROVISIONING_NFC: 121 return ACTION_PROVISION_MANAGED_DEVICE; 122 123 default: 124 throw new IllegalProvisioningArgumentException( 125 "Unknown NFC bump mime-type: " + mimeType); 126 } 127 case ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE: 128 return settingsFacade.isDuringSetupWizard(context) 129 ? ACTION_PROVISION_MANAGED_DEVICE 130 : ACTION_PROVISION_MANAGED_PROFILE; 131 default: 132 throw new IllegalProvisioningArgumentException("Unknown intent action " 133 + intent.getAction()); 134 } 135 } 136 137 /** 138 * Returns an {@link ArrayList} containing the allowed provisioning modes that can be returned 139 * by the DPC for the admin-integrated flow. 140 * 141 * <p>The array will be a subset of {{@link 142 * DevicePolicyManager#PROVISIONING_MODE_MANAGED_PROFILE}, {@link 143 * DevicePolicyManager#PROVISIONING_MODE_FULLY_MANAGED_DEVICE}, {@link 144 * DevicePolicyManager#PROVISIONING_MODE_MANAGED_PROFILE_ON_PERSONAL_DEVICE}}. 145 * 146 * {@code supportedModes} is a validated value passed by the provisioning 147 * initiator via the {@link DevicePolicyManager#EXTRA_PROVISIONING_SUPPORTED_MODES} extra. 148 * Its value can be a combination of {@link 149 * DevicePolicyManager#FLAG_SUPPORTED_MODES_ORGANIZATION_OWNED}, {@link 150 * DevicePolicyManager#FLAG_SUPPORTED_MODES_PERSONALLY_OWNED}, {@link 151 * DevicePolicyManager#FLAG_SUPPORTED_MODES_DEVICE_OWNER} or {@link 152 * com.android.managedprovisioning.model.ProvisioningParams 153 * #DEFAULT_EXTRA_PROVISIONING_SUPPORTED_MODES} if the value is not passed 154 * as part of {@link DevicePolicyManager#ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}. 155 * 156 * <p>If the intent action is not {@link 157 * DevicePolicyManager#ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}, an empty array 158 * is returned, since the result is only relevant to the admin-integrated flow. 159 * 160 * <p>If {@link DevicePolicyManager#EXTRA_PROVISIONING_SUPPORTED_MODES} is not provided, 161 * {@link DevicePolicyManager#FLAG_SUPPORTED_MODES_ORGANIZATION_OWNED} is used as default. 162 * 163 * <ul> 164 * <li> 165 * If only organization-owned provisioning is supported, allow 166 * {@link DevicePolicyManager#PROVISIONING_MODE_FULLY_MANAGED_DEVICE} and 167 * {@link DevicePolicyManager#PROVISIONING_MODE_MANAGED_PROFILE} on an organization 168 * -owned device. 169 * </li> 170 * <li> 171 * If only personally-owned provisioning is supported, allow just 172 * {@link DevicePolicyManager#PROVISIONING_MODE_MANAGED_PROFILE} on a 173 * personally-owned device. 174 * </li> 175 * <li> 176 * If both are supported, allow 177 * {@link DevicePolicyManager#PROVISIONING_MODE_FULLY_MANAGED_DEVICE}, 178 * {@link DevicePolicyManager#PROVISIONING_MODE_MANAGED_PROFILE} on an 179 * organization-owned device, and {@link 180 * DevicePolicyManager#PROVISIONING_MODE_MANAGED_PROFILE_ON_PERSONAL_DEVICE} for a 181 * managed profile on a personally-owned device. 182 * </li> 183 * <li> 184 * If {@link DevicePolicyManager#FLAG_SUPPORTED_MODES_DEVICE_OWNER} is used, allow just 185 * {@link DevicePolicyManager#PROVISIONING_MODE_FULLY_MANAGED_DEVICE}. 186 * </li> 187 * </ul> 188 * 189 * <p>The device serial number and IMEI wil be sent to the DPC with the 190 * {@link DevicePolicyManager#PROVISIONING_MODE_MANAGED_PROFILE_ON_PERSONAL_DEVICE} and 191 * {@link DevicePolicyManager#PROVISIONING_MODE_FULLY_MANAGED_DEVICE} extras 192 * only in the first case when organization-owned provisioning is the only ownership model 193 * supported. 194 */ 195 /* 196 The method's return type is ArrayList because the result is passed to a Bundle} object via its 197 Bundle#putIntegerArrayList method. This is done to avoid using Bundle#putSerializable with 198 HashSet which is not recommended. 199 */ getAllowedProvisioningModes(Context context, int supportedModes, Utils utils)200 public ArrayList<Integer> getAllowedProvisioningModes(Context context, 201 int supportedModes, Utils utils) { 202 if (supportedModes == DEFAULT_EXTRA_PROVISIONING_SUPPORTED_MODES) { 203 ProvisionLogger.logi("Not admin-integrated flow, " 204 + "no allowed provisioning modes necessary."); 205 return new ArrayList<>(); 206 } 207 validateSupportedModes(supportedModes); 208 ArrayList<Integer> result = new ArrayList<>(); 209 if (utils.containsBinaryFlags(supportedModes, FLAG_SUPPORTED_MODES_ORGANIZATION_OWNED)) { 210 result.addAll(List.of( 211 PROVISIONING_MODE_MANAGED_PROFILE, 212 PROVISIONING_MODE_FULLY_MANAGED_DEVICE)); 213 if (utils.containsBinaryFlags(supportedModes, FLAG_SUPPORTED_MODES_PERSONALLY_OWNED)) { 214 result.add(PROVISIONING_MODE_MANAGED_PROFILE_ON_PERSONAL_DEVICE); 215 } 216 } else if (utils.containsBinaryFlags( 217 supportedModes, FLAG_SUPPORTED_MODES_PERSONALLY_OWNED)) { 218 result.addAll(List.of( 219 PROVISIONING_MODE_MANAGED_PROFILE)); 220 } else if (utils.containsBinaryFlags(supportedModes, FLAG_SUPPORTED_MODES_DEVICE_OWNER)) { 221 result.addAll(List.of( 222 PROVISIONING_MODE_FULLY_MANAGED_DEVICE)); 223 } 224 ProvisionLogger.logi("Allowed provisioning modes before checking for managed users " 225 + "support: " + result); 226 boolean supportsManagedUsers = supportsManagedUsers(context); 227 if (!supportsManagedUsers) { 228 result.removeAll(List.of( 229 PROVISIONING_MODE_MANAGED_PROFILE, 230 PROVISIONING_MODE_MANAGED_PROFILE_ON_PERSONAL_DEVICE)); 231 } 232 ProvisionLogger.logi("Supports managed users: " + supportsManagedUsers); 233 if (result.isEmpty()) { 234 throw new IllegalArgumentException( 235 "No available supported provisioning modes. Requested support mode was " 236 + supportedModes); 237 } 238 ProvisionLogger.logi("Allowed provisioning modes: " + result); 239 return result; 240 } 241 242 /** 243 * Throws {@link IllegalArgumentException} if {@code supportedModes} contains an 244 * unsupported binary flag combination. 245 * 246 * @see DevicePolicyManager#FLAG_SUPPORTED_MODES_ORGANIZATION_OWNED 247 * @see DevicePolicyManager#FLAG_SUPPORTED_MODES_PERSONALLY_OWNED 248 * @see DevicePolicyManager#FLAG_SUPPORTED_MODES_DEVICE_OWNER 249 */ validateSupportedModes(int supportedModes)250 public void validateSupportedModes(int supportedModes) { 251 if (!ALLOWED_COMBINATIONS.contains(supportedModes)) { 252 throw new IllegalArgumentException( 253 "Supported modes flag combination not supported. Supported modes: " 254 + supportedModes); 255 } 256 } 257 supportsManagedUsers(Context context)258 private boolean supportsManagedUsers(Context context) { 259 return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS); 260 } 261 } 262