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