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