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