Home | History | Annotate | Download | only in parser
      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 
    101     ExtrasProvisioningDataParser(Utils utils) {
    102         mUtils = checkNotNull(utils);
    103     }
    104 
    105     @Override
    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      */
    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      */
    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
    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
    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      */
    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 
    345     private boolean isResumeProvisioningIntent(Intent intent) {
    346         return ACTION_RESUME_PROVISIONING.equals(intent.getAction());
    347     }
    348 }
    349