1 /*
2  * Copyright (C) 2020 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_ESTABLISH_NETWORK_CONNECTION;
20 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE;
21 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE;
22 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE;
23 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE;
24 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_TRIGGER;
25 import static android.app.admin.DevicePolicyManager.FLAG_SUPPORTED_MODES_DEVICE_OWNER;
26 import static android.app.admin.DevicePolicyManager.FLAG_SUPPORTED_MODES_ORGANIZATION_OWNED;
27 import static android.app.admin.DevicePolicyManager.FLAG_SUPPORTED_MODES_PERSONALLY_OWNED;
28 import static android.app.admin.DevicePolicyManager.MIME_TYPE_PROVISIONING_NFC;
29 import static android.app.admin.DevicePolicyManager.PROVISIONING_MODE_FULLY_MANAGED_DEVICE;
30 import static android.app.admin.DevicePolicyManager.PROVISIONING_MODE_MANAGED_PROFILE;
31 import static android.app.admin.DevicePolicyManager.PROVISIONING_MODE_MANAGED_PROFILE_ON_PERSONAL_DEVICE;
32 import static android.app.admin.DevicePolicyManager.PROVISIONING_TRIGGER_UNSPECIFIED;
33 import static android.nfc.NfcAdapter.ACTION_NDEF_DISCOVERED;
34 
35 import static com.android.managedprovisioning.common.Globals.ACTION_PROVISION_MANAGED_DEVICE_SILENTLY;
36 import static com.android.managedprovisioning.model.ProvisioningParams.DEFAULT_EXTRA_PROVISIONING_SUPPORTED_MODES;
37 
38 import android.app.admin.DevicePolicyManager;
39 import android.content.Context;
40 import android.content.Intent;
41 import android.content.pm.PackageManager;
42 import android.util.ArraySet;
43 
44 import com.android.managedprovisioning.common.IllegalProvisioningArgumentException;
45 import com.android.managedprovisioning.common.ProvisionLogger;
46 import com.android.managedprovisioning.common.SettingsFacade;
47 import com.android.managedprovisioning.common.Utils;
48 
49 import java.util.ArrayList;
50 import java.util.List;
51 
52 /**
53  * A utility class with methods related to parsing the provisioning extras
54  */
55 public class ParserUtils {
56 
57     private static final ArraySet<Integer> ALLOWED_COMBINATIONS = new ArraySet<>();
58     {
59         ALLOWED_COMBINATIONS.add(FLAG_SUPPORTED_MODES_ORGANIZATION_OWNED);
60         ALLOWED_COMBINATIONS.add(FLAG_SUPPORTED_MODES_PERSONALLY_OWNED);
61         ALLOWED_COMBINATIONS.add(FLAG_SUPPORTED_MODES_DEVICE_OWNER);
62         ALLOWED_COMBINATIONS.add(
63                 FLAG_SUPPORTED_MODES_ORGANIZATION_OWNED | FLAG_SUPPORTED_MODES_PERSONALLY_OWNED);
64     }
65 
66     /**
67      * Returns the provisioning trigger supplied in the provisioning extras only if it was supplied
68      * alongside the {@link DevicePolicyManager#ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}
69      * intent action. Otherwise it returns {@link
70      * DevicePolicyManager#PROVISIONING_TRIGGER_UNSPECIFIED}.
71      */
extractProvisioningTrigger(Intent intent)72     int extractProvisioningTrigger(Intent intent) {
73         if (!ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE.equals(intent.getAction())) {
74             return PROVISIONING_TRIGGER_UNSPECIFIED;
75         }
76         return intent.getIntExtra(
77                 EXTRA_PROVISIONING_TRIGGER, PROVISIONING_TRIGGER_UNSPECIFIED);
78     }
79 
80     /**
81      * Translates a given managed provisioning intent to its corresponding provisioning flow, using
82      * the action from the intent.
83      *
84      * <p>This is necessary because, unlike other provisioning actions which has 1:1 mapping, there
85      * are multiple actions that can trigger the device owner provisioning flow. This includes
86      * {@link ACTION_PROVISION_MANAGED_DEVICE}, {@link ACTION_NDEF_DISCOVERED} and
87      * {@link ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}. These 3 actions are equivalent
88      * except they are sent from a different source.
89      *
90      * @return the appropriate DevicePolicyManager declared action for the given incoming intent.
91      * @throws IllegalProvisioningArgumentException if intent is malformed
92      */
extractProvisioningAction(Intent intent, SettingsFacade settingsFacade, Context context)93     String extractProvisioningAction(Intent intent,
94             SettingsFacade settingsFacade, Context context)
95             throws IllegalProvisioningArgumentException {
96         if (intent == null || intent.getAction() == null) {
97             throw new IllegalProvisioningArgumentException("Null intent action.");
98         }
99 
100         // Map the incoming intent to a DevicePolicyManager.ACTION_*, as there is a N:1 mapping in
101         // some cases.
102         switch (intent.getAction()) {
103             // Trivial cases.
104             case ACTION_PROVISION_MANAGED_DEVICE:
105             case ACTION_PROVISION_MANAGED_PROFILE:
106             case ACTION_PROVISION_FINANCED_DEVICE:
107                 return intent.getAction();
108 
109             // Silent device owner is same as device owner.
110             case ACTION_PROVISION_MANAGED_DEVICE_SILENTLY:
111                 return ACTION_PROVISION_MANAGED_DEVICE;
112 
113             // NFC cases which need to take mime-type into account.
114             case ACTION_NDEF_DISCOVERED:
115                 String mimeType = intent.getType();
116                 if (mimeType == null) {
117                     throw new IllegalProvisioningArgumentException(
118                             "Unknown NFC bump mime-type: " + mimeType);
119                 }
120                 switch (mimeType) {
121                     case MIME_TYPE_PROVISIONING_NFC:
122                         return ACTION_PROVISION_MANAGED_DEVICE;
123 
124                     default:
125                         throw new IllegalProvisioningArgumentException(
126                                 "Unknown NFC bump mime-type: " + mimeType);
127                 }
128             case ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE:
129                 return settingsFacade.isDuringSetupWizard(context)
130                         ? ACTION_PROVISION_MANAGED_DEVICE
131                         : ACTION_PROVISION_MANAGED_PROFILE;
132             case ACTION_ESTABLISH_NETWORK_CONNECTION:
133                 return ACTION_ESTABLISH_NETWORK_CONNECTION;
134             default:
135                 throw new IllegalProvisioningArgumentException("Unknown intent action "
136                         + intent.getAction());
137         }
138     }
139 
140     /**
141      * Returns an {@link ArrayList} containing the allowed provisioning modes that can be returned
142      * by the DPC for the admin-integrated flow.
143      *
144      * <p>The array will be a subset of {{@link
145      * DevicePolicyManager#PROVISIONING_MODE_MANAGED_PROFILE}, {@link
146      * DevicePolicyManager#PROVISIONING_MODE_FULLY_MANAGED_DEVICE}, {@link
147      * DevicePolicyManager#PROVISIONING_MODE_MANAGED_PROFILE_ON_PERSONAL_DEVICE}}.
148      *
149      * {@code supportedModes} is a validated value passed by the provisioning
150      * initiator via the {@link DevicePolicyManager#EXTRA_PROVISIONING_SUPPORTED_MODES} extra.
151      * Its value can be a combination of {@link
152      * DevicePolicyManager#FLAG_SUPPORTED_MODES_ORGANIZATION_OWNED}, {@link
153      * DevicePolicyManager#FLAG_SUPPORTED_MODES_PERSONALLY_OWNED}, {@link
154      * DevicePolicyManager#FLAG_SUPPORTED_MODES_DEVICE_OWNER} or {@link
155      * com.android.managedprovisioning.model.ProvisioningParams
156      * #DEFAULT_EXTRA_PROVISIONING_SUPPORTED_MODES} if the value is not passed
157      * as part of {@link DevicePolicyManager#ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}.
158      *
159      * <p>If the intent action is not {@link
160      * DevicePolicyManager#ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}, an empty array
161      * is returned, since the result is only relevant to the admin-integrated flow.
162      *
163      * <p>If {@link DevicePolicyManager#EXTRA_PROVISIONING_SUPPORTED_MODES} is not provided,
164      * {@link DevicePolicyManager#FLAG_SUPPORTED_MODES_ORGANIZATION_OWNED} is used as default.
165      *
166      * <ul>
167      *     <li>
168      *         If only organization-owned provisioning is supported, allow
169      *         {@link DevicePolicyManager#PROVISIONING_MODE_FULLY_MANAGED_DEVICE} and
170      *         {@link DevicePolicyManager#PROVISIONING_MODE_MANAGED_PROFILE} on an organization
171      *         -owned device.
172      *     </li>
173      *     <li>
174      *          If only personally-owned provisioning is supported, allow just
175      *          {@link DevicePolicyManager#PROVISIONING_MODE_MANAGED_PROFILE} on a
176      *          personally-owned device.
177      *     </li>
178      *     <li>
179      *          If both are supported, allow
180      *          {@link DevicePolicyManager#PROVISIONING_MODE_FULLY_MANAGED_DEVICE},
181      *          {@link DevicePolicyManager#PROVISIONING_MODE_MANAGED_PROFILE} on an
182      *          organization-owned device, and {@link
183      *          DevicePolicyManager#PROVISIONING_MODE_MANAGED_PROFILE_ON_PERSONAL_DEVICE} for a
184      *          managed profile on a personally-owned device.
185      *     </li>
186      *     <li>
187      *         If {@link DevicePolicyManager#FLAG_SUPPORTED_MODES_DEVICE_OWNER} is used, allow just
188      *         {@link DevicePolicyManager#PROVISIONING_MODE_FULLY_MANAGED_DEVICE}.
189      *     </li>
190      * </ul>
191      *
192      * <p>The device serial number and IMEI wil be sent to the DPC with the
193      * {@link DevicePolicyManager#PROVISIONING_MODE_MANAGED_PROFILE_ON_PERSONAL_DEVICE} and
194      * {@link DevicePolicyManager#PROVISIONING_MODE_FULLY_MANAGED_DEVICE} extras
195      * only in the first case when organization-owned provisioning is the only ownership model
196      * supported.
197      */
198     /*
199      The method's return type is ArrayList because the result is passed to a Bundle} object via its
200      Bundle#putIntegerArrayList method. This is done to avoid using Bundle#putSerializable with
201      HashSet which is not recommended.
202      */
getAllowedProvisioningModes(Context context, int supportedModes, Utils utils)203     public ArrayList<Integer> getAllowedProvisioningModes(Context context,
204             int supportedModes, Utils utils) {
205         if (supportedModes == DEFAULT_EXTRA_PROVISIONING_SUPPORTED_MODES) {
206             ProvisionLogger.logi("Not admin-integrated flow, "
207                     + "no allowed provisioning modes necessary.");
208             return new ArrayList<>();
209         }
210         validateSupportedModes(supportedModes);
211         ArrayList<Integer> result = new ArrayList<>();
212         if (utils.containsBinaryFlags(supportedModes, FLAG_SUPPORTED_MODES_ORGANIZATION_OWNED)) {
213             result.addAll(List.of(
214                     PROVISIONING_MODE_MANAGED_PROFILE,
215                     PROVISIONING_MODE_FULLY_MANAGED_DEVICE));
216             if (utils.containsBinaryFlags(supportedModes, FLAG_SUPPORTED_MODES_PERSONALLY_OWNED)) {
217                 result.add(PROVISIONING_MODE_MANAGED_PROFILE_ON_PERSONAL_DEVICE);
218             }
219         } else if (utils.containsBinaryFlags(
220                 supportedModes, FLAG_SUPPORTED_MODES_PERSONALLY_OWNED)) {
221             result.addAll(List.of(
222                     PROVISIONING_MODE_MANAGED_PROFILE));
223         } else if (utils.containsBinaryFlags(supportedModes, FLAG_SUPPORTED_MODES_DEVICE_OWNER)) {
224             result.addAll(List.of(
225                     PROVISIONING_MODE_FULLY_MANAGED_DEVICE));
226         }
227         ProvisionLogger.logi("Allowed provisioning modes before checking for managed users "
228                 + "support: " + result);
229         boolean supportsManagedUsers = supportsManagedUsers(context);
230         if (!supportsManagedUsers) {
231             result.removeAll(List.of(
232                     PROVISIONING_MODE_MANAGED_PROFILE,
233                     PROVISIONING_MODE_MANAGED_PROFILE_ON_PERSONAL_DEVICE));
234         }
235         ProvisionLogger.logi("Supports managed users: " + supportsManagedUsers);
236         if (result.isEmpty()) {
237             throw new IllegalArgumentException(
238                     "No available supported provisioning modes. Requested support mode was "
239                             + supportedModes);
240         }
241         ProvisionLogger.logi("Allowed provisioning modes: " + result);
242         return result;
243     }
244 
245     /**
246      * Throws {@link IllegalArgumentException} if {@code supportedModes} contains an
247      * unsupported binary flag combination.
248      *
249      * @see DevicePolicyManager#FLAG_SUPPORTED_MODES_ORGANIZATION_OWNED
250      * @see DevicePolicyManager#FLAG_SUPPORTED_MODES_PERSONALLY_OWNED
251      * @see DevicePolicyManager#FLAG_SUPPORTED_MODES_DEVICE_OWNER
252      */
validateSupportedModes(int supportedModes)253     public void validateSupportedModes(int supportedModes) {
254         if (!ALLOWED_COMBINATIONS.contains(supportedModes)) {
255             throw new IllegalArgumentException(
256                     "Supported modes flag combination not supported. Supported modes: "
257                             + supportedModes);
258         }
259     }
260 
supportsManagedUsers(Context context)261     private boolean supportsManagedUsers(Context context) {
262         return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS);
263     }
264 }
265