1 /* 2 * Copyright 2016, 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_MANAGED_DEVICE; 20 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE; 21 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE; 22 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE; 23 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER; 24 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE; 25 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE; 26 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME; 27 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE; 28 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM; 29 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER; 30 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION; 31 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME; 32 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM; 33 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED; 34 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_LOCALE; 35 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_LOCAL_TIME; 36 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_MAIN_COLOR; 37 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_SKIP_ENCRYPTION; 38 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_SKIP_USER_SETUP; 39 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_TIME_ZONE; 40 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_WIFI_HIDDEN; 41 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_WIFI_PAC_URL; 42 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_WIFI_PASSWORD; 43 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_WIFI_PROXY_BYPASS; 44 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_WIFI_PROXY_HOST; 45 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_WIFI_PROXY_PORT; 46 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_WIFI_SECURITY_TYPE; 47 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_WIFI_SSID; 48 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_LOGO_URI; 49 import static com.android.internal.util.Preconditions.checkNotNull; 50 import static com.android.managedprovisioning.common.Globals.ACTION_RESUME_PROVISIONING; 51 import static com.android.managedprovisioning.parser.MessageParser.EXTRA_PROVISIONING_ACTION; 52 import static com.android.managedprovisioning.parser.MessageParser.EXTRA_PROVISIONING_DEVICE_ADMIN_SUPPORT_SHA1_PACKAGE_CHECKSUM; 53 import static com.android.managedprovisioning.parser.MessageParser.EXTRA_PROVISIONING_STARTED_BY_TRUSTED_SOURCE; 54 55 import android.accounts.Account; 56 import android.content.ComponentName; 57 import android.content.Context; 58 import android.content.Intent; 59 import android.net.Uri; 60 import android.graphics.Color; 61 import android.os.Bundle; 62 import android.os.PersistableBundle; 63 import android.support.annotation.Nullable; 64 import android.support.annotation.VisibleForTesting; 65 66 import com.android.managedprovisioning.LogoUtils; 67 import com.android.managedprovisioning.ProvisionLogger; 68 import com.android.managedprovisioning.common.IllegalProvisioningArgumentException; 69 import com.android.managedprovisioning.common.Utils; 70 import com.android.managedprovisioning.model.PackageDownloadInfo; 71 import com.android.managedprovisioning.model.ProvisioningParams; 72 import com.android.managedprovisioning.model.WifiInfo; 73 74 import java.util.Arrays; 75 import java.util.HashSet; 76 import java.util.IllformedLocaleException; 77 import java.util.Locale; 78 import java.util.Properties; 79 import java.util.Set; 80 81 /** 82 * A parser which parses provisioning data from intent which stores in {@link Bundle} extras. 83 */ 84 85 @VisibleForTesting 86 public class ExtrasProvisioningDataParser implements ProvisioningDataParser { 87 private static final Set<String> PROVISIONING_ACTIONS_SUPPORT_ALL_PROVISIONING_DATA = 88 new HashSet(Arrays.asList( 89 ACTION_RESUME_PROVISIONING, 90 ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE)); 91 92 private static final Set<String> PROVISIONING_ACTIONS_SUPPORT_MIN_PROVISIONING_DATA = 93 new HashSet(Arrays.asList( 94 ACTION_PROVISION_MANAGED_DEVICE, 95 ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE, 96 ACTION_PROVISION_MANAGED_USER, 97 ACTION_PROVISION_MANAGED_PROFILE)); 98 99 private final Utils mUtils; 100 ExtrasProvisioningDataParser(Utils utils)101 ExtrasProvisioningDataParser(Utils utils) { 102 mUtils = checkNotNull(utils); 103 } 104 105 @Override parse(Intent provisioningIntent, Context context)106 public ProvisioningParams parse(Intent provisioningIntent, Context context) 107 throws IllegalProvisioningArgumentException{ 108 String provisioningAction = provisioningIntent.getAction(); 109 110 if (PROVISIONING_ACTIONS_SUPPORT_MIN_PROVISIONING_DATA.contains(provisioningAction)) { 111 ProvisionLogger.logi("Processing mininalist extras intent."); 112 return parseMinimalistSupportedProvisioningDataInternal(provisioningIntent, context) 113 .build(); 114 } else if (PROVISIONING_ACTIONS_SUPPORT_ALL_PROVISIONING_DATA.contains( 115 provisioningAction)) { 116 return parseAllSupportedProvisioningData(provisioningIntent, context); 117 } else { 118 throw new IllegalProvisioningArgumentException("Unsupported provisioning action: " 119 + provisioningAction); 120 } 121 } 122 123 /** 124 * Parses minimal supported set of parameters from bundle extras of a provisioning intent. 125 * 126 * <p>Here is the list of supported parameters. 127 * <ul> 128 * <li>{@link EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME}</li> 129 * <li> 130 * {@link EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME} only in 131 * {@link ACTION_PROVISION_MANAGED_PROFILE}. 132 * </li> 133 * <li>{@link EXTRA_PROVISIONING_LOGO_URI}</li> 134 * <li>{@link EXTRA_PROVISIONING_MAIN_COLOR}</li> 135 * <li> 136 * {@link EXTRA_PROVISIONING_SKIP_USER_SETUP} only in 137 * {@link ACTION_PROVISION_MANAGED_USER} and {@link ACTION_PROVISION_MANAGED_DEVICE}. 138 * </li> 139 * <li>{@link EXTRA_PROVISIONING_SKIP_ENCRYPTION}</li> 140 * <li>{@link EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED}</li> 141 * <li>{@link EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE}</li> 142 * <li>{@link EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}</li> 143 * </ul> 144 */ parseMinimalistSupportedProvisioningDataInternal( Intent intent, Context context)145 private ProvisioningParams.Builder parseMinimalistSupportedProvisioningDataInternal( 146 Intent intent, Context context) 147 throws IllegalProvisioningArgumentException { 148 boolean isProvisionManagedDeviceFromTrustedSourceIntent = 149 ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE.equals(intent.getAction()); 150 try { 151 String provisioningAction = isResumeProvisioningIntent(intent) 152 ? intent.getStringExtra(EXTRA_PROVISIONING_ACTION) 153 : mUtils.mapIntentToDpmAction(intent); 154 155 // Parse device admin package name and component name. 156 ComponentName deviceAdminComponentName = (ComponentName) intent.getParcelableExtra( 157 EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME); 158 // Device admin package name is deprecated. It is only supported in Profile Owner 159 // provisioning and when resuming NFC provisioning. 160 String deviceAdminPackageName = null; 161 if (ACTION_PROVISION_MANAGED_PROFILE.equals(provisioningAction)) { 162 // In L, we only support package name. This means some DPC may still send us the 163 // device admin package name only. Attempts to obtain the package name from extras. 164 deviceAdminPackageName = intent.getStringExtra( 165 EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME); 166 // For profile owner, the device admin package should be installed. Verify the 167 // device admin package. 168 deviceAdminComponentName = mUtils.findDeviceAdmin( 169 deviceAdminPackageName, deviceAdminComponentName, context); 170 // Since the device admin package must be installed at this point and its component 171 // name has been obtained, it should be safe to set the deprecated package name 172 // value to null. 173 deviceAdminPackageName = null; 174 } else if (isResumeProvisioningIntent(intent)) { 175 deviceAdminPackageName = intent.getStringExtra( 176 EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME); 177 } 178 179 // Parse skip user setup in ACTION_PROVISION_MANAGED_USER and 180 // ACTION_PROVISION_MANAGED_DEVICE (sync auth) only. This extra is not supported if 181 // provisioning was started by trusted source, as it is not clear where SUW should 182 // continue from. 183 boolean skipUserSetup = ProvisioningParams.DEFAULT_SKIP_USER_SETUP; 184 if (!isProvisionManagedDeviceFromTrustedSourceIntent 185 && (provisioningAction.equals(ACTION_PROVISION_MANAGED_USER) 186 || provisioningAction.equals(ACTION_PROVISION_MANAGED_DEVICE))) { 187 skipUserSetup = intent.getBooleanExtra(EXTRA_PROVISIONING_SKIP_USER_SETUP, 188 ProvisioningParams.DEFAULT_SKIP_USER_SETUP); 189 } 190 191 // Parse main color and organization's logo. This is not supported in managed device 192 // from trusted source provisioning because, currently, there is no way to send 193 // organization logo to the device at this stage. 194 Integer mainColor = ProvisioningParams.DEFAULT_MAIN_COLOR; 195 if (!isProvisionManagedDeviceFromTrustedSourceIntent) { 196 if (intent.hasExtra(EXTRA_PROVISIONING_MAIN_COLOR)) { 197 mainColor = intent.getIntExtra(EXTRA_PROVISIONING_MAIN_COLOR, 0 /* not used */); 198 } 199 parseOrganizationLogoUrlFromExtras(context, intent); 200 } 201 202 return ProvisioningParams.Builder.builder() 203 .setProvisioningAction(provisioningAction) 204 .setDeviceAdminComponentName(deviceAdminComponentName) 205 .setDeviceAdminPackageName(deviceAdminPackageName) 206 .setSkipEncryption(intent.getBooleanExtra(EXTRA_PROVISIONING_SKIP_ENCRYPTION, 207 ProvisioningParams.DEFAULT_EXTRA_PROVISIONING_SKIP_ENCRYPTION)) 208 .setLeaveAllSystemAppsEnabled(intent.getBooleanExtra( 209 EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED, 210 ProvisioningParams.DEFAULT_LEAVE_ALL_SYSTEM_APPS_ENABLED)) 211 .setAdminExtrasBundle((PersistableBundle) intent.getParcelableExtra( 212 EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE)) 213 .setMainColor(mainColor) 214 .setSkipUserSetup(skipUserSetup) 215 .setAccountToMigrate((Account) intent.getParcelableExtra( 216 EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE)); 217 } catch (ClassCastException e) { 218 throw new IllegalProvisioningArgumentException("Extra " 219 + EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE 220 + " must be of type PersistableBundle.", e); 221 } catch (IllegalArgumentException e) { 222 throw new IllegalProvisioningArgumentException("Invalid parameter found!", e); 223 } catch (NullPointerException e) { 224 throw new IllegalProvisioningArgumentException("Compulsory parameter not found!", e); 225 } 226 } 227 228 /** 229 * Parses an intent and return a corresponding {@link ProvisioningParams} object. 230 * 231 * @param intent intent to be parsed. 232 * @param context a context 233 */ parseAllSupportedProvisioningData(Intent intent, Context context)234 private ProvisioningParams parseAllSupportedProvisioningData(Intent intent, Context context) 235 throws IllegalProvisioningArgumentException { 236 try { 237 ProvisionLogger.logi("Processing all supported extras intent: " + intent.getAction()); 238 return parseMinimalistSupportedProvisioningDataInternal(intent, context) 239 // Parse time zone, local time and locale. 240 .setTimeZone(intent.getStringExtra(EXTRA_PROVISIONING_TIME_ZONE)) 241 .setLocalTime(intent.getLongExtra(EXTRA_PROVISIONING_LOCAL_TIME, 242 ProvisioningParams.DEFAULT_LOCAL_TIME)) 243 .setLocale(MessageParser.stringToLocale( 244 intent.getStringExtra(EXTRA_PROVISIONING_LOCALE))) 245 // Parse WiFi configuration. 246 .setWifiInfo(parseWifiInfoFromExtras(intent)) 247 // Parse device admin package download info. 248 .setDeviceAdminDownloadInfo(parsePackageDownloadInfoFromExtras(intent)) 249 // Cases where startedByTrustedSource can be true are 250 // 1. We are reloading a stored provisioning intent, either Nfc bump or 251 // PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE, after encryption reboot, 252 // which is a self-originated intent. 253 // 2. the intent is from a trusted source, for example QR provisioning. 254 .setStartedByTrustedSource(isResumeProvisioningIntent(intent) 255 ? intent.getBooleanExtra(EXTRA_PROVISIONING_STARTED_BY_TRUSTED_SOURCE, 256 ProvisioningParams.DEFAULT_STARTED_BY_TRUSTED_SOURCE) 257 : ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE.equals( 258 intent.getAction())) 259 .build(); 260 } catch (IllegalArgumentException e) { 261 throw new IllegalProvisioningArgumentException("Invalid parameter found!", e); 262 } catch (NullPointerException e) { 263 throw new IllegalProvisioningArgumentException("Compulsory parameter not found!", e); 264 } 265 } 266 267 /** 268 * Parses Wifi configuration from an Intent and returns the result in {@link WifiInfo}. 269 */ 270 @Nullable parseWifiInfoFromExtras(Intent intent)271 private WifiInfo parseWifiInfoFromExtras(Intent intent) { 272 if (intent.getStringExtra(EXTRA_PROVISIONING_WIFI_SSID) == null) { 273 return null; 274 } 275 return WifiInfo.Builder.builder() 276 .setSsid(intent.getStringExtra(EXTRA_PROVISIONING_WIFI_SSID)) 277 .setSecurityType(intent.getStringExtra(EXTRA_PROVISIONING_WIFI_SECURITY_TYPE)) 278 .setPassword(intent.getStringExtra(EXTRA_PROVISIONING_WIFI_PASSWORD)) 279 .setProxyHost(intent.getStringExtra(EXTRA_PROVISIONING_WIFI_PROXY_HOST)) 280 .setProxyBypassHosts(intent.getStringExtra(EXTRA_PROVISIONING_WIFI_PROXY_BYPASS)) 281 .setPacUrl(intent.getStringExtra(EXTRA_PROVISIONING_WIFI_PAC_URL)) 282 .setProxyPort(intent.getIntExtra(EXTRA_PROVISIONING_WIFI_PROXY_PORT, 283 WifiInfo.DEFAULT_WIFI_PROXY_PORT)) 284 .setHidden(intent.getBooleanExtra(EXTRA_PROVISIONING_WIFI_HIDDEN, 285 WifiInfo.DEFAULT_WIFI_HIDDEN)) 286 .build(); 287 } 288 289 /** 290 * Parses device admin package download info configuration from an Intent and returns the result 291 * in {@link PackageDownloadInfo}. 292 */ 293 @Nullable parsePackageDownloadInfoFromExtras(Intent intent)294 private PackageDownloadInfo parsePackageDownloadInfoFromExtras(Intent intent) { 295 if (intent.getStringExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION) 296 == null) { 297 return null; 298 } 299 PackageDownloadInfo.Builder downloadInfoBuilder = PackageDownloadInfo.Builder.builder() 300 .setMinVersion(intent.getIntExtra( 301 EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE, 302 PackageDownloadInfo.DEFAULT_MINIMUM_VERSION)) 303 .setLocation(intent.getStringExtra( 304 EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION)) 305 .setCookieHeader(intent.getStringExtra( 306 EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER)); 307 String packageHash = 308 intent.getStringExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM); 309 if (packageHash != null) { 310 downloadInfoBuilder.setPackageChecksum(mUtils.stringToByteArray(packageHash)); 311 if (isResumeProvisioningIntent(intent)) { 312 // PackageChecksumSupportsSha1 is only supported in NFC provisioning. But if the 313 // device is rebooted after encryption as part of the NFC provisioning flow, the 314 // value should be restored. 315 downloadInfoBuilder.setPackageChecksumSupportsSha1( 316 intent.getBooleanExtra( 317 EXTRA_PROVISIONING_DEVICE_ADMIN_SUPPORT_SHA1_PACKAGE_CHECKSUM, 318 false)); 319 } 320 } 321 String sigHash = intent.getStringExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM); 322 if (sigHash != null) { 323 downloadInfoBuilder.setSignatureChecksum(mUtils.stringToByteArray(sigHash)); 324 } 325 return downloadInfoBuilder.build(); 326 } 327 328 /** 329 * Parses the organization logo url from intent. 330 */ parseOrganizationLogoUrlFromExtras(Context context, Intent intent)331 private void parseOrganizationLogoUrlFromExtras(Context context, Intent intent) { 332 Uri logoUri = intent.getParcelableExtra(EXTRA_PROVISIONING_LOGO_URI); 333 if (logoUri != null) { 334 // If we go through encryption, and if the uri is a content uri: 335 // We'll lose the grant to this uri. So we need to save it to a local file. 336 LogoUtils.saveOrganisationLogo(context, logoUri); 337 } else if (!isResumeProvisioningIntent(intent)) { 338 // If the intent is not from managed provisioning app, there is a slight possibility 339 // that the logo is still kept on the file system from a previous provisioning. In 340 // this case, remove it. 341 LogoUtils.cleanUp(context); 342 } 343 } 344 isResumeProvisioningIntent(Intent intent)345 private boolean isResumeProvisioningIntent(Intent intent) { 346 return ACTION_RESUME_PROVISIONING.equals(intent.getAction()); 347 } 348 } 349