ExtrasProvisioningDataParser.java revision 7079df5a3fb155947004843ee8ec25b36127e3ed
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_MAIN_COLOR;
37import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_SKIP_ENCRYPTION;
38import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_SKIP_USER_SETUP;
39import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_TIME_ZONE;
40import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_WIFI_HIDDEN;
41import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_WIFI_PAC_URL;
42import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_WIFI_PASSWORD;
43import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_WIFI_PROXY_BYPASS;
44import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_WIFI_PROXY_HOST;
45import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_WIFI_PROXY_PORT;
46import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_WIFI_SECURITY_TYPE;
47import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_WIFI_SSID;
48import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_LOGO_URI;
49import static com.android.internal.util.Preconditions.checkNotNull;
50import static com.android.managedprovisioning.common.Globals.ACTION_RESUME_PROVISIONING;
51import static com.android.managedprovisioning.parser.MessageParser.EXTRA_PROVISIONING_ACTION;
52import static com.android.managedprovisioning.parser.MessageParser.EXTRA_PROVISIONING_DEVICE_ADMIN_SUPPORT_SHA1_PACKAGE_CHECKSUM;
53import static com.android.managedprovisioning.parser.MessageParser.EXTRA_PROVISIONING_STARTED_BY_TRUSTED_SOURCE;
54
55import android.accounts.Account;
56import android.content.ComponentName;
57import android.content.Context;
58import android.content.Intent;
59import android.net.Uri;
60import android.graphics.Color;
61import android.os.Bundle;
62import android.os.PersistableBundle;
63import android.support.annotation.Nullable;
64import android.support.annotation.VisibleForTesting;
65
66import com.android.managedprovisioning.LogoUtils;
67import com.android.managedprovisioning.ProvisionLogger;
68import com.android.managedprovisioning.common.IllegalProvisioningArgumentException;
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.IllformedLocaleException;
77import java.util.Locale;
78import java.util.Properties;
79import java.util.Set;
80
81/**
82 * A parser which parses provisioning data from intent which stores in {@link Bundle} extras.
83 */
84
85@VisibleForTesting
86public 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
149        try {
150            String provisioningAction = isResumeProvisioningIntent(intent)
151                    ? intent.getStringExtra(EXTRA_PROVISIONING_ACTION)
152                    : 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 NFC provisioning (parsing logic is not in this parser).
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 main color
176            Integer mainColor = ProvisioningParams.DEFAULT_MAIN_COLOR;
177            if (intent.hasExtra(EXTRA_PROVISIONING_MAIN_COLOR)) {
178                mainColor = intent.getIntExtra(EXTRA_PROVISIONING_MAIN_COLOR, 0 /* not used */);
179            }
180
181            // Parse skip user setup in ACTION_PROVISION_MANAGED_USER and
182            // ACTION_PROVISION_MANAGED_DEVICE only.
183            boolean skipUserSetup = ProvisioningParams.DEFAULT_SKIP_USER_SETUP;
184            if (provisioningAction.equals(ACTION_PROVISION_MANAGED_USER)
185                    || provisioningAction.equals(ACTION_PROVISION_MANAGED_DEVICE)) {
186                skipUserSetup = intent.getBooleanExtra(EXTRA_PROVISIONING_SKIP_USER_SETUP,
187                        ProvisioningParams.DEFAULT_SKIP_USER_SETUP);
188            }
189
190            parseOrganizationLogoUrlFromExtras(context, intent);
191
192            return ProvisioningParams.Builder.builder()
193                    .setProvisioningAction(provisioningAction)
194                    .setDeviceAdminComponentName(deviceAdminComponentName)
195                    .setDeviceAdminPackageName(deviceAdminPackageName)
196                    .setSkipEncryption(intent.getBooleanExtra(EXTRA_PROVISIONING_SKIP_ENCRYPTION,
197                            ProvisioningParams.DEFAULT_EXTRA_PROVISIONING_SKIP_ENCRYPTION))
198                    .setLeaveAllSystemAppsEnabled(intent.getBooleanExtra(
199                            EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED,
200                            ProvisioningParams.DEFAULT_LEAVE_ALL_SYSTEM_APPS_ENABLED))
201                    .setAdminExtrasBundle((PersistableBundle) intent.getParcelableExtra(
202                            EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE))
203                    .setMainColor(mainColor)
204                    .setSkipUserSetup(skipUserSetup)
205                    .setAccountToMigrate((Account) intent.getParcelableExtra(
206                            EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE));
207        } catch (ClassCastException e) {
208            throw new IllegalProvisioningArgumentException("Extra "
209                    + EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE
210                    + " must be of type PersistableBundle.", e);
211        } catch (IllegalArgumentException e) {
212            throw new IllegalProvisioningArgumentException("Invalid parameter found!", e);
213        } catch (NullPointerException e) {
214            throw new IllegalProvisioningArgumentException("Compulsory parameter not found!", e);
215        }
216    }
217
218    /**
219     * Parses an intent and return a corresponding {@link ProvisioningParams} object.
220     *
221     * @param intent intent to be parsed.
222     * @param context a context
223     */
224    private ProvisioningParams parseAllSupportedProvisioningData(Intent intent, Context context)
225            throws IllegalProvisioningArgumentException {
226        try {
227            ProvisionLogger.logi("Processing all supported extras intent: " + intent.getAction());
228            return parseMinimalistSupportedProvisioningDataInternal(intent, context)
229                    // Parse time zone, local time and locale.
230                    .setTimeZone(intent.getStringExtra(EXTRA_PROVISIONING_TIME_ZONE))
231                    .setLocalTime(intent.getLongExtra(EXTRA_PROVISIONING_LOCAL_TIME,
232                            ProvisioningParams.DEFAULT_LOCAL_TIME))
233                    .setLocale(MessageParser.stringToLocale(
234                            intent.getStringExtra(EXTRA_PROVISIONING_LOCALE)))
235                    // Parse WiFi configuration.
236                    .setWifiInfo(parseWifiInfoFromExtras(intent))
237                    // Parse device admin package download info.
238                    .setDeviceAdminDownloadInfo(parsePackageDownloadInfoFromExtras(intent))
239                    // Cases where startedByTrustedSource can be true are
240                    // 1. We are reloading a stored provisioning intent, either Nfc bump or
241                    //    PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE, after encryption reboot,
242                    //    which is a self-originated intent.
243                    // 2. the intent is from a trusted source, for example QR provisioning.
244                    .setStartedByTrustedSource(isResumeProvisioningIntent(intent)
245                            ? intent.getBooleanExtra(EXTRA_PROVISIONING_STARTED_BY_TRUSTED_SOURCE,
246                                    ProvisioningParams.DEFAULT_STARTED_BY_TRUSTED_SOURCE)
247                            : ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE.equals(
248                                    intent.getAction()))
249                    .build();
250        }  catch (IllegalArgumentException e) {
251            throw new IllegalProvisioningArgumentException("Invalid parameter found!", e);
252        } catch (NullPointerException e) {
253            throw new IllegalProvisioningArgumentException("Compulsory parameter not found!", e);
254        }
255    }
256
257    /**
258     * Parses Wifi configuration from an Intent and returns the result in {@link WifiInfo}.
259     */
260    @Nullable
261    private WifiInfo parseWifiInfoFromExtras(Intent intent) {
262        if (intent.getStringExtra(EXTRA_PROVISIONING_WIFI_SSID) == null) {
263            return null;
264        }
265        return WifiInfo.Builder.builder()
266                .setSsid(intent.getStringExtra(EXTRA_PROVISIONING_WIFI_SSID))
267                .setSecurityType(intent.getStringExtra(EXTRA_PROVISIONING_WIFI_SECURITY_TYPE))
268                .setPassword(intent.getStringExtra(EXTRA_PROVISIONING_WIFI_PASSWORD))
269                .setProxyHost(intent.getStringExtra(EXTRA_PROVISIONING_WIFI_PROXY_HOST))
270                .setProxyBypassHosts(intent.getStringExtra(EXTRA_PROVISIONING_WIFI_PROXY_BYPASS))
271                .setPacUrl(intent.getStringExtra(EXTRA_PROVISIONING_WIFI_PAC_URL))
272                .setProxyPort(intent.getIntExtra(EXTRA_PROVISIONING_WIFI_PROXY_PORT,
273                        WifiInfo.DEFAULT_WIFI_PROXY_PORT))
274                .setHidden(intent.getBooleanExtra(EXTRA_PROVISIONING_WIFI_HIDDEN,
275                        WifiInfo.DEFAULT_WIFI_HIDDEN))
276                .build();
277    }
278
279    /**
280     * Parses device admin package download info configuration from an Intent and returns the result
281     * in {@link PackageDownloadInfo}.
282     */
283    @Nullable
284    private PackageDownloadInfo parsePackageDownloadInfoFromExtras(Intent intent) {
285        if (intent.getStringExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION)
286                == null) {
287            return null;
288        }
289        PackageDownloadInfo.Builder downloadInfoBuilder = PackageDownloadInfo.Builder.builder()
290                .setMinVersion(intent.getIntExtra(
291                        EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE,
292                        PackageDownloadInfo.DEFAULT_MINIMUM_VERSION))
293                .setLocation(intent.getStringExtra(
294                        EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION))
295                .setCookieHeader(intent.getStringExtra(
296                        EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER));
297        String packageHash =
298                intent.getStringExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM);
299        if (packageHash != null) {
300            downloadInfoBuilder.setPackageChecksum(mUtils.stringToByteArray(packageHash));
301            if (isResumeProvisioningIntent(intent)) {
302                // PackageChecksumSupportsSha1 is only supported in NFC provisioning. But if the
303                // device is rebooted after encryption as part of the NFC provisioning flow, the
304                // value should be restored.
305                downloadInfoBuilder.setPackageChecksumSupportsSha1(
306                        intent.getBooleanExtra(
307                                EXTRA_PROVISIONING_DEVICE_ADMIN_SUPPORT_SHA1_PACKAGE_CHECKSUM,
308                                false));
309            }
310        }
311        String sigHash = intent.getStringExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM);
312        if (sigHash != null) {
313            downloadInfoBuilder.setSignatureChecksum(mUtils.stringToByteArray(sigHash));
314        }
315        return downloadInfoBuilder.build();
316    }
317
318    /**
319     * Parses the organization logo url from intent.
320     */
321    private void parseOrganizationLogoUrlFromExtras(Context context, Intent intent) {
322        Uri logoUri = intent.getParcelableExtra(EXTRA_PROVISIONING_LOGO_URI);
323        if (logoUri != null) {
324            // If we go through encryption, and if the uri is a content uri:
325            // We'll lose the grant to this uri. So we need to save it to a local file.
326            LogoUtils.saveOrganisationLogo(context, logoUri);
327        } else if (!isResumeProvisioningIntent(intent)) {
328            // If the intent is not from managed provisioning app, there is a slight possibility
329            // that the logo is still kept on the file system from a previous provisioning. In
330            // this case, remove it.
331            LogoUtils.cleanUp(context);
332        }
333    }
334
335    private boolean isResumeProvisioningIntent(Intent intent) {
336        return ACTION_RESUME_PROVISIONING.equals(intent.getAction());
337    }
338}
339