1/*
2 * Copyright 2014, 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;
18
19import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE;
20import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_TIME_ZONE;
21import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_LOCAL_TIME;
22import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_LOCALE;
23import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_WIFI_SSID;
24import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_WIFI_HIDDEN;
25import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_WIFI_SECURITY_TYPE;
26import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_WIFI_PASSWORD;
27import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_WIFI_PROXY_HOST;
28import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_WIFI_PROXY_PORT;
29import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_WIFI_PROXY_BYPASS;
30import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_WIFI_PAC_URL;
31import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE;
32import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE;
33import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME;
34import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE;
35import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM;
36import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION;
37import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER;
38import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME;
39import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM;
40import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM;
41import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME;
42import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_INITIALIZER_MINIMUM_VERSION_CODE;
43import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION;
44import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER;
45import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM;
46import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_INITIALIZER_SIGNATURE_CHECKSUM;
47import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED;
48import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_SKIP_ENCRYPTION;
49import static android.app.admin.DevicePolicyManager.MIME_TYPE_PROVISIONING_NFC;
50import static android.app.admin.DevicePolicyManager.MIME_TYPE_PROVISIONING_NFC_V2;
51import static java.nio.charset.StandardCharsets.UTF_8;
52
53import android.accounts.Account;
54import android.content.ComponentName;
55import android.content.Context;
56import android.content.Intent;
57import android.nfc.NdefMessage;
58import android.nfc.NdefRecord;
59import android.nfc.NfcAdapter;
60import android.os.Bundle;
61import android.os.Parcelable;
62import android.os.PersistableBundle;
63import android.text.TextUtils;
64
65import com.android.managedprovisioning.Utils.IllegalProvisioningArgumentException;
66
67import java.io.IOException;
68import java.io.StringReader;
69import java.util.Enumeration;
70import java.util.HashMap;
71import java.util.IllformedLocaleException;
72import java.util.Locale;
73import java.util.Properties;
74
75/**
76 * This class can initialize a {@link ProvisioningParams} object from an intent.
77 * A {@link ProvisioningParams} object stores various parameters both for the device owner
78 * provisioning and profile owner provisioning.
79 * There are two kinds of intents that can be parsed it into {@link ProvisioningParams}:
80 *
81 * <p>
82 * Intent was received via Nfc.
83 * The intent contains the extra {@link NfcAdapter.EXTRA_NDEF_MESSAGES}, which indicates that
84 * provisioning was started via Nfc bump. This extra contains an NDEF message, which contains an
85 * NfcRecord with mime type {@link MIME_TYPE_PROVISIONING_NFC}. This record stores a serialized
86 * properties object, which contains the serialized extra's described in the next option.
87 * A typical use case would be a programmer application that sends an Nfc bump to start Nfc
88 * provisioning from a programmer device.
89 *
90 * <p>
91 * Intent was received directly.
92 * The intent contains the extra {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME} or
93 * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME} (which is deprecated and supported for
94 * legacy reasons only), and may contain {@link #EXTRA_PROVISIONING_TIME_ZONE},
95 * {@link #EXTRA_PROVISIONING_LOCAL_TIME}, and {@link #EXTRA_PROVISIONING_LOCALE}. A download
96 * location for the device admin may be specified in
97 * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}, together with an optional
98 * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE}, an optional
99 * http cookie header {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER}, and
100 * the SHA-256 hash of the target file {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM} or
101 * the SHA-256 hash of any signature of the android package in the target file
102 * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_CERTIFICATE_CHECKSUM}.
103 * Additional information to send through to the device initializer and admin may be specified in
104 * {@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}.
105 * The optional boolean {@link #EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED} indicates whether
106 * system apps should not be disabled. The optional boolean
107 * {@link #EXTRA_PROVISIONING_SKIP_ENCRYPTION} specifies whether the device should be encrypted.
108 * Furthermore a wifi network may be specified in {@link #EXTRA_PROVISIONING_WIFI_SSID}, and if
109 * applicable {@link #EXTRA_PROVISIONING_WIFI_HIDDEN},
110 * {@link #EXTRA_PROVISIONING_WIFI_SECURITY_TYPE}, {@link #EXTRA_PROVISIONING_WIFI_PASSWORD},
111 * {@link #EXTRA_PROVISIONING_WIFI_PROXY_HOST}, {@link #EXTRA_PROVISIONING_WIFI_PROXY_PORT},
112 * {@link #EXTRA_PROVISIONING_WIFI_PROXY_BYPASS},
113 * The optional parcelable account {@link #EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE} specifies the
114 * account that has to be migrated from primary user to managed user in case of
115 * profile owner provisioning.
116 * A typical use case would be the {@link BootReminder} sending the intent after device encryption
117 * and reboot.
118 *
119 * <p>
120 * Furthermore this class can construct the bundle of extras for the second kind of intent given a
121 * {@link ProvisioningParams}, and it keeps track of the types of the extras in the
122 * DEVICE_OWNER_x_EXTRAS and PROFILE_OWNER_x_EXTRAS, with x the appropriate type.
123 */
124public class MessageParser {
125    private static final String EXTRA_PROVISIONING_STARTED_BY_NFC  =
126            "com.android.managedprovisioning.extra.started_by_nfc";
127    private static final String EXTRA_PROVISIONING_DEVICE_ADMIN_SUPPORT_SHA1_PACKAGE_CHECKSUM =
128            "com.android.managedprovisioning.extra.device_admin_support_sha1_package_checksum";
129
130    /* package */ static final String[] PROFILE_OWNER_STRING_EXTRAS = {
131        // Key for the device admin package name
132        EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME
133    };
134
135    /* package */ static final String[] PROFILE_OWNER_ACCOUNT_EXTRAS = {
136        // Key for the account extras
137        EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE
138    };
139
140    /* package */ static final String[] PROFILE_OWNER_PERSISTABLE_BUNDLE_EXTRAS = {
141        // Key for the admin extras bundle
142        EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE
143    };
144
145    /* package */ static final String[] PROFILE_OWNER_COMPONENT_NAME_EXTRAS = {
146        // Key for the device admin component name
147        EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME
148    };
149
150    /* package */ static final String[] DEVICE_OWNER_STRING_EXTRAS = {
151        EXTRA_PROVISIONING_TIME_ZONE,
152        EXTRA_PROVISIONING_LOCALE,
153        EXTRA_PROVISIONING_WIFI_SSID,
154        EXTRA_PROVISIONING_WIFI_SECURITY_TYPE,
155        EXTRA_PROVISIONING_WIFI_PASSWORD,
156        EXTRA_PROVISIONING_WIFI_PROXY_HOST,
157        EXTRA_PROVISIONING_WIFI_PROXY_BYPASS,
158        EXTRA_PROVISIONING_WIFI_PAC_URL,
159        EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME,
160        EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION,
161        EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER,
162        EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM,
163        EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM,
164        EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION,
165        EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER,
166        EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM,
167        EXTRA_PROVISIONING_DEVICE_INITIALIZER_SIGNATURE_CHECKSUM
168    };
169
170    /* package */ static final String[] DEVICE_OWNER_LONG_EXTRAS = {
171        EXTRA_PROVISIONING_LOCAL_TIME
172    };
173
174    /* package */ static final String[] DEVICE_OWNER_INT_EXTRAS = {
175        EXTRA_PROVISIONING_WIFI_PROXY_PORT,
176        EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE,
177        EXTRA_PROVISIONING_DEVICE_INITIALIZER_MINIMUM_VERSION_CODE
178    };
179
180    /* package */ static final String[] DEVICE_OWNER_BOOLEAN_EXTRAS = {
181        EXTRA_PROVISIONING_WIFI_HIDDEN,
182        EXTRA_PROVISIONING_STARTED_BY_NFC,
183        EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED,
184        EXTRA_PROVISIONING_SKIP_ENCRYPTION,
185        EXTRA_PROVISIONING_DEVICE_ADMIN_SUPPORT_SHA1_PACKAGE_CHECKSUM
186    };
187
188    /* package */ static final String[] DEVICE_OWNER_PERSISTABLE_BUNDLE_EXTRAS = {
189        EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE
190    };
191
192    /* package */ static final String[] DEVICE_OWNER_COMPONENT_NAME_EXTRAS = {
193        EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME,
194        EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME
195    };
196
197    public void addProvisioningParamsToBundle(Bundle bundle, ProvisioningParams params) {
198        bundle.putString(EXTRA_PROVISIONING_TIME_ZONE, params.timeZone);
199        bundle.putString(EXTRA_PROVISIONING_LOCALE, localeToString(params.locale));
200        bundle.putString(EXTRA_PROVISIONING_WIFI_SSID, params.wifiInfo.ssid);
201        bundle.putString(EXTRA_PROVISIONING_WIFI_SECURITY_TYPE, params.wifiInfo.securityType);
202        bundle.putString(EXTRA_PROVISIONING_WIFI_PASSWORD, params.wifiInfo.password);
203        bundle.putString(EXTRA_PROVISIONING_WIFI_PROXY_HOST, params.wifiInfo.proxyHost);
204        bundle.putString(EXTRA_PROVISIONING_WIFI_PROXY_BYPASS, params.wifiInfo.proxyBypassHosts);
205        bundle.putString(EXTRA_PROVISIONING_WIFI_PAC_URL, params.wifiInfo.pacUrl);
206        bundle.putInt(EXTRA_PROVISIONING_WIFI_PROXY_PORT, params.wifiInfo.proxyPort);
207        bundle.putBoolean(EXTRA_PROVISIONING_WIFI_HIDDEN, params.wifiInfo.hidden);
208        bundle.putString(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME,
209                params.deviceAdminPackageName);
210        bundle.putParcelable(EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME,
211                params.deviceAdminComponentName);
212        bundle.putInt(EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE,
213                params.deviceAdminDownloadInfo.minVersion);
214        bundle.putString(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION,
215                params.deviceAdminDownloadInfo.location);
216        bundle.putString(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER,
217                params.deviceAdminDownloadInfo.cookieHeader);
218        bundle.putString(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM,
219                Utils.byteArrayToString(params.deviceAdminDownloadInfo.packageChecksum));
220        bundle.putBoolean(EXTRA_PROVISIONING_DEVICE_ADMIN_SUPPORT_SHA1_PACKAGE_CHECKSUM,
221                params.deviceAdminDownloadInfo.packageChecksumSupportsSha1);
222        bundle.putString(EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM,
223                Utils.byteArrayToString(params.deviceAdminDownloadInfo.signatureChecksum));
224        bundle.putParcelable(EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME,
225                params.deviceInitializerComponentName);
226        bundle.putInt(EXTRA_PROVISIONING_DEVICE_INITIALIZER_MINIMUM_VERSION_CODE,
227                params.deviceInitializerDownloadInfo.minVersion);
228        bundle.putString(EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION,
229                params.deviceInitializerDownloadInfo.location);
230        bundle.putString(EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER,
231                params.deviceInitializerDownloadInfo.cookieHeader);
232        bundle.putString(EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM,
233                Utils.byteArrayToString(params.deviceInitializerDownloadInfo.packageChecksum));
234        bundle.putString(EXTRA_PROVISIONING_DEVICE_INITIALIZER_SIGNATURE_CHECKSUM,
235                Utils.byteArrayToString(params.deviceInitializerDownloadInfo.signatureChecksum));
236
237        bundle.putLong(EXTRA_PROVISIONING_LOCAL_TIME, params.localTime);
238        bundle.putBoolean(EXTRA_PROVISIONING_STARTED_BY_NFC, params.startedByNfc);
239        bundle.putBoolean(EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED,
240                params.leaveAllSystemAppsEnabled);
241        bundle.putParcelable(EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE, params.adminExtrasBundle);
242        bundle.putBoolean(EXTRA_PROVISIONING_SKIP_ENCRYPTION, params.skipEncryption);
243        bundle.putParcelable(EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE, params.accountToMigrate);
244    }
245
246    public ProvisioningParams parseNfcIntent(Intent nfcIntent)
247            throws IllegalProvisioningArgumentException {
248        ProvisionLogger.logi("Processing Nfc Payload.");
249        // Only one first message with NFC_MIME_TYPE is used.
250        for (Parcelable rawMsg : nfcIntent
251                     .getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)) {
252            NdefMessage msg = (NdefMessage) rawMsg;
253
254            // Assume only first record of message is used.
255            NdefRecord firstRecord = msg.getRecords()[0];
256            String mimeType = new String(firstRecord.getType(), UTF_8);
257
258            if (MIME_TYPE_PROVISIONING_NFC.equals(mimeType) ||
259                    MIME_TYPE_PROVISIONING_NFC_V2.equals(mimeType)) {
260                ProvisioningParams params = parseProperties(new String(firstRecord.getPayload()
261                                , UTF_8));
262                params.startedByNfc = true;
263                ProvisionLogger.logi("End processing Nfc Payload.");
264                return params;
265            }
266        }
267        throw new IllegalProvisioningArgumentException(
268                "Intent does not contain NfcRecord with the correct MIME type.");
269    }
270
271    // Note: EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE property contains a Properties object
272    // serialized into String. See Properties.store() and Properties.load() for more details.
273    // The property value is optional.
274    private ProvisioningParams parseProperties(String data)
275            throws IllegalProvisioningArgumentException {
276        ProvisioningParams params = new ProvisioningParams();
277        try {
278            Properties props = new Properties();
279            props.load(new StringReader(data));
280
281            String s; // Used for parsing non-Strings.
282            params.timeZone
283                    = props.getProperty(EXTRA_PROVISIONING_TIME_ZONE);
284            if ((s = props.getProperty(EXTRA_PROVISIONING_LOCALE)) != null) {
285                params.locale = stringToLocale(s);
286            }
287            params.wifiInfo.ssid = props.getProperty(EXTRA_PROVISIONING_WIFI_SSID);
288            params.wifiInfo.securityType = props.getProperty(EXTRA_PROVISIONING_WIFI_SECURITY_TYPE);
289            params.wifiInfo.password = props.getProperty(EXTRA_PROVISIONING_WIFI_PASSWORD);
290            params.wifiInfo.proxyHost = props.getProperty(EXTRA_PROVISIONING_WIFI_PROXY_HOST);
291            params.wifiInfo.proxyBypassHosts =
292                    props.getProperty(EXTRA_PROVISIONING_WIFI_PROXY_BYPASS);
293            params.wifiInfo.pacUrl = props.getProperty(EXTRA_PROVISIONING_WIFI_PAC_URL);
294            if ((s = props.getProperty(EXTRA_PROVISIONING_WIFI_PROXY_PORT)) != null) {
295                params.wifiInfo.proxyPort = Integer.parseInt(s);
296            }
297            if ((s = props.getProperty(EXTRA_PROVISIONING_WIFI_HIDDEN)) != null) {
298                params.wifiInfo.hidden = Boolean.parseBoolean(s);
299            }
300
301            params.deviceAdminPackageName
302                    = props.getProperty(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME);
303            String componentNameString = props.getProperty(
304                    EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME);
305            if (componentNameString != null) {
306                params.deviceAdminComponentName = ComponentName.unflattenFromString(
307                        componentNameString);
308            }
309            if ((s = props.getProperty(
310                    EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE)) != null) {
311                params.deviceAdminDownloadInfo.minVersion = Integer.parseInt(s);
312            } else {
313                params.deviceAdminDownloadInfo.minVersion =
314                        ProvisioningParams.DEFAULT_MINIMUM_VERSION;
315            }
316            params.deviceAdminDownloadInfo.location
317                    = props.getProperty(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION);
318            params.deviceAdminDownloadInfo.cookieHeader = props.getProperty(
319                    EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER);
320            if ((s = props.getProperty(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM)) != null) {
321                params.deviceAdminDownloadInfo.packageChecksum = Utils.stringToByteArray(s);
322                // Still support SHA-1 for device admin package hash if we are provisioned by a Nfc
323                // programmer.
324                // TODO: remove once SHA-1 is fully deprecated.
325                params.deviceAdminDownloadInfo.packageChecksumSupportsSha1 = true;
326            }
327            if ((s = props.getProperty(EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM))
328                    != null) {
329                params.deviceAdminDownloadInfo.signatureChecksum = Utils.stringToByteArray(s);
330            }
331            String name = props.getProperty(
332                    EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME);
333            if (name != null) {
334                params.deviceInitializerComponentName = ComponentName.unflattenFromString(name);
335            }
336            if ((s = props.getProperty(
337                    EXTRA_PROVISIONING_DEVICE_INITIALIZER_MINIMUM_VERSION_CODE)) != null) {
338                params.deviceInitializerDownloadInfo.minVersion = Integer.parseInt(s);
339            } else {
340                params.deviceInitializerDownloadInfo.minVersion =
341                        ProvisioningParams.DEFAULT_MINIMUM_VERSION;
342            }
343            params.deviceInitializerDownloadInfo.location = props.getProperty(
344                    EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION);
345            params.deviceInitializerDownloadInfo.cookieHeader = props.getProperty(
346                    EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER);
347            if ((s = props.getProperty(
348                    EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM)) != null) {
349                params.deviceInitializerDownloadInfo.packageChecksum = Utils.stringToByteArray(s);
350            }
351            if ((s = props.getProperty(EXTRA_PROVISIONING_DEVICE_INITIALIZER_SIGNATURE_CHECKSUM))
352                    != null) {
353                params.deviceInitializerDownloadInfo.signatureChecksum =
354                        Utils.stringToByteArray(s);
355            }
356
357            if ((s = props.getProperty(EXTRA_PROVISIONING_LOCAL_TIME)) != null) {
358                params.localTime = Long.parseLong(s);
359            }
360
361            if ((s = props.getProperty(EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED)) != null) {
362                params.leaveAllSystemAppsEnabled = Boolean.parseBoolean(s);
363            }
364            if ((s = props.getProperty(EXTRA_PROVISIONING_SKIP_ENCRYPTION)) != null) {
365                params.skipEncryption = Boolean.parseBoolean(s);
366            }
367
368            params.adminExtrasBundle = deserializeExtrasBundle(props,
369                    EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE);
370
371            checkValidityOfProvisioningParams(params);
372            return params;
373        } catch (IOException e) {
374            throw new Utils.IllegalProvisioningArgumentException("Couldn't load payload", e);
375        } catch (NumberFormatException e) {
376            throw new Utils.IllegalProvisioningArgumentException("Incorrect numberformat.", e);
377        } catch (IllformedLocaleException e) {
378            throw new Utils.IllegalProvisioningArgumentException("Invalid locale.", e);
379        }
380    }
381
382    /**
383     * Get a {@link PersistableBundle} from a String property in a Properties object.
384     * @param props the source of the extra
385     * @param extraName key into the Properties object
386     * @return the bundle or {@code null} if there was no property with the given name
387     * @throws IOException if there was an error parsing the propery
388     */
389    private PersistableBundle deserializeExtrasBundle(Properties props, String extraName)
390            throws IOException {
391        PersistableBundle extrasBundle = null;
392        String serializedExtras = props.getProperty(extraName);
393        if (serializedExtras != null) {
394            Properties extrasProp = new Properties();
395            extrasProp.load(new StringReader(serializedExtras));
396            extrasBundle = new PersistableBundle(extrasProp.size());
397            for (String propName : extrasProp.stringPropertyNames()) {
398                extrasBundle.putString(propName, extrasProp.getProperty(propName));
399            }
400        }
401        return extrasBundle;
402    }
403
404    public ProvisioningParams parseMinimalistNonNfcIntent(Intent intent)
405            throws IllegalProvisioningArgumentException {
406        ProvisionLogger.logi("Processing mininalist non-nfc intent.");
407        ProvisioningParams params = parseMinimalistNonNfcIntentInternal(intent);
408        if (params.deviceAdminComponentName == null) {
409            throw new IllegalProvisioningArgumentException("Must provide the component name of the"
410                    + " device admin");
411        }
412        return params;
413    }
414
415    private ProvisioningParams parseMinimalistNonNfcIntentInternal(Intent intent)
416            throws IllegalProvisioningArgumentException {
417        ProvisioningParams params = new ProvisioningParams();
418        params.deviceAdminComponentName = (ComponentName) intent.getParcelableExtra(
419                EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME);
420        params.skipEncryption = intent.getBooleanExtra(
421                EXTRA_PROVISIONING_SKIP_ENCRYPTION,
422                ProvisioningParams.DEFAULT_EXTRA_PROVISIONING_SKIP_ENCRYPTION);
423        params.leaveAllSystemAppsEnabled = intent.getBooleanExtra(
424                EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED,
425                ProvisioningParams.DEFAULT_LEAVE_ALL_SYSTEM_APPS_ENABLED);
426        params.accountToMigrate = (Account) intent.getParcelableExtra(
427                EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE);
428        try {
429            params.adminExtrasBundle = (PersistableBundle) intent.getParcelableExtra(
430                    EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE);
431        } catch (ClassCastException e) {
432            throw new IllegalProvisioningArgumentException("Extra "
433                    + EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE
434                    + " must be of type PersistableBundle.", e);
435        }
436        return params;
437    }
438
439    /**
440     * Parse an intent and return a corresponding {@link ProvisioningParams} object.
441     *
442     * @param intent intent to be parsed.
443     * @param trusted whether the intent is trusted or not. A trusted intent can contain internal
444     * extras which are not part of the public API. These extras often control sensitive aspects of
445     * ManagedProvisioning such as whether deprecated SHA-1 is supported, or whether MP was started
446     * from NFC (hence no user consent dialog). Intents used by other apps to start MP should always
447     * be untrusted.
448     */
449    public ProvisioningParams parseNonNfcIntent(Intent intent, boolean trusted)
450            throws IllegalProvisioningArgumentException {
451        ProvisionLogger.logi("Processing non-nfc intent.");
452        ProvisioningParams params = parseMinimalistNonNfcIntentInternal(intent);
453
454        params.timeZone = intent.getStringExtra(EXTRA_PROVISIONING_TIME_ZONE);
455        String localeString = intent.getStringExtra(EXTRA_PROVISIONING_LOCALE);
456        if (localeString != null) {
457            params.locale = stringToLocale(localeString);
458        }
459        params.wifiInfo.ssid = intent.getStringExtra(EXTRA_PROVISIONING_WIFI_SSID);
460        params.wifiInfo.securityType = intent.getStringExtra(EXTRA_PROVISIONING_WIFI_SECURITY_TYPE);
461        params.wifiInfo.password = intent.getStringExtra(EXTRA_PROVISIONING_WIFI_PASSWORD);
462        params.wifiInfo.proxyHost = intent.getStringExtra(EXTRA_PROVISIONING_WIFI_PROXY_HOST);
463        params.wifiInfo.proxyBypassHosts =
464                intent.getStringExtra(EXTRA_PROVISIONING_WIFI_PROXY_BYPASS);
465        params.wifiInfo.pacUrl = intent.getStringExtra(EXTRA_PROVISIONING_WIFI_PAC_URL);
466        params.wifiInfo.proxyPort = intent.getIntExtra(EXTRA_PROVISIONING_WIFI_PROXY_PORT,
467                ProvisioningParams.DEFAULT_WIFI_PROXY_PORT);
468        params.wifiInfo.hidden = intent.getBooleanExtra(EXTRA_PROVISIONING_WIFI_HIDDEN,
469                ProvisioningParams.DEFAULT_WIFI_HIDDEN);
470
471        params.deviceAdminPackageName
472                = intent.getStringExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME);
473        params.deviceAdminDownloadInfo.minVersion = intent.getIntExtra(
474                EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE,
475                ProvisioningParams.DEFAULT_MINIMUM_VERSION);
476        params.deviceAdminDownloadInfo.location
477                = intent.getStringExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION);
478        params.deviceAdminDownloadInfo.cookieHeader = intent.getStringExtra(
479                EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER);
480        String packageHash =
481                intent.getStringExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM);
482        if (packageHash != null) {
483            params.deviceAdminDownloadInfo.packageChecksum = Utils.stringToByteArray(packageHash);
484            // If we are restarted after an encryption reboot, use stored (trusted) value for this.
485            if (trusted) {
486                params.deviceAdminDownloadInfo.packageChecksumSupportsSha1 = intent.getBooleanExtra(
487                        EXTRA_PROVISIONING_DEVICE_ADMIN_SUPPORT_SHA1_PACKAGE_CHECKSUM, false);
488            } else {
489                // legacy action (activation code flow) still uses SHA-1.
490                // TODO: remove once that flow is deprecated.
491                params.deviceAdminDownloadInfo.packageChecksumSupportsSha1 =
492                        DeviceOwnerPreProvisioningActivity.LEGACY_ACTION_PROVISION_MANAGED_DEVICE
493                                .equals(intent.getAction());
494            }
495        }
496        String sigHash =
497                intent.getStringExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM);
498        if (sigHash != null) {
499            params.deviceAdminDownloadInfo.signatureChecksum = Utils.stringToByteArray(sigHash);
500        }
501        params.deviceInitializerComponentName = (ComponentName) intent.getParcelableExtra(
502                EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME);
503        params.deviceInitializerDownloadInfo.minVersion = intent.getIntExtra(
504                EXTRA_PROVISIONING_DEVICE_INITIALIZER_MINIMUM_VERSION_CODE,
505                ProvisioningParams.DEFAULT_MINIMUM_VERSION);
506        params.deviceInitializerDownloadInfo.location = intent.getStringExtra(
507                EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION);
508        params.deviceInitializerDownloadInfo.cookieHeader = intent.getStringExtra(
509                EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER);
510        packageHash = intent.getStringExtra(EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM);
511        if (packageHash != null) {
512            params.deviceInitializerDownloadInfo.packageChecksum =
513                    Utils.stringToByteArray(packageHash);
514        }
515        sigHash =
516                intent.getStringExtra(EXTRA_PROVISIONING_DEVICE_INITIALIZER_SIGNATURE_CHECKSUM);
517        if (sigHash != null) {
518            params.deviceInitializerDownloadInfo.signatureChecksum =
519                    Utils.stringToByteArray(sigHash);
520        }
521
522        params.localTime = intent.getLongExtra(EXTRA_PROVISIONING_LOCAL_TIME,
523                ProvisioningParams.DEFAULT_LOCAL_TIME);
524        if (trusted) {
525            // The only case where startedByNfc can be true in this code path is we are reloading
526            // a stored Nfc bump intent after encryption reboot, which is a trusted intent.
527            params.startedByNfc = intent.getBooleanExtra(EXTRA_PROVISIONING_STARTED_BY_NFC,
528                    false);
529        }
530
531        params.accountToMigrate = (Account) intent.getParcelableExtra(
532                EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE);
533        params.deviceAdminComponentName = (ComponentName) intent.getParcelableExtra(
534                EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME);
535
536        checkValidityOfProvisioningParams(params);
537        return params;
538    }
539
540    /**
541     * Check whether necessary fields are set.
542     */
543    private void checkValidityOfProvisioningParams(ProvisioningParams params)
544            throws IllegalProvisioningArgumentException  {
545        if (TextUtils.isEmpty(params.deviceAdminPackageName)
546                && params.deviceAdminComponentName == null) {
547            throw new IllegalProvisioningArgumentException("Must provide the name of the device"
548                    + " admin package or component name");
549        }
550        checkDownloadInfoHasChecksum(params.deviceAdminDownloadInfo, "device admin");
551        checkDownloadInfoHasChecksum(params.deviceInitializerDownloadInfo, "device initializer");
552    }
553
554    private void checkDownloadInfoHasChecksum(ProvisioningParams.PackageDownloadInfo info,
555            String downloadName) throws IllegalProvisioningArgumentException {
556        if (!TextUtils.isEmpty(info.location)) {
557            if ((info.packageChecksum == null || info.packageChecksum.length == 0)
558                    && (info.signatureChecksum == null || info.signatureChecksum.length == 0)) {
559                throw new IllegalProvisioningArgumentException("Checksum of installer file"
560                        + " or its signature is required for downloading " + downloadName
561                        + ", but neither is provided.");
562            }
563        }
564    }
565
566    public static Locale stringToLocale(String string)
567        throws IllformedLocaleException {
568        return new Locale.Builder().setLanguageTag(string.replace("_", "-")).build();
569    }
570
571    public static String localeToString(Locale locale) {
572        if (locale != null) {
573            return locale.getLanguage() + "_" + locale.getCountry();
574        } else {
575            return null;
576        }
577    }
578}
579