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 
17 package com.android.managedprovisioning.uiflows;
18 
19 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE;
20 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE;
21 import static com.android.internal.util.Preconditions.checkNotNull;
22 import static com.android.managedprovisioning.common.Globals.ACTION_RESUME_PROVISIONING;
23 
24 import android.annotation.NonNull;
25 import android.annotation.Nullable;
26 import android.app.ActivityManager;
27 import android.app.admin.DevicePolicyManager;
28 import android.app.KeyguardManager;
29 import android.content.ComponentName;
30 import android.content.Context;
31 import android.content.Intent;
32 import android.content.pm.ApplicationInfo;
33 import android.content.pm.PackageManager;
34 import android.content.pm.UserInfo;
35 import android.os.AsyncTask;
36 import android.os.UserHandle;
37 import android.os.UserManager;
38 import android.provider.Settings.Global;
39 import android.service.persistentdata.PersistentDataBlockManager;
40 import android.text.TextUtils;
41 
42 import com.android.internal.annotations.VisibleForTesting;
43 import com.android.managedprovisioning.common.IllegalProvisioningArgumentException;
44 import com.android.managedprovisioning.common.Utils;
45 import com.android.managedprovisioning.model.ProvisioningParams;
46 import com.android.managedprovisioning.parser.MessageParser;
47 import com.android.managedprovisioning.ProvisionLogger;
48 import com.android.managedprovisioning.R;
49 
50 import java.util.List;
51 
52 public class PreProvisioningController {
53     private final Context mContext;
54     private final Ui mUi;
55     private final MessageParser mMessageParser;
56     private final Utils mUtils;
57     private final EncryptionController mEncryptionController;
58 
59     // used system services
60     private final DevicePolicyManager mDevicePolicyManager;
61     private final UserManager mUserManager;
62     private final PackageManager mPackageManager;
63     private final ActivityManager mActivityManager;
64     private final KeyguardManager mKeyguardManager;
65     private final PersistentDataBlockManager mPdbManager;
66 
67     private ProvisioningParams mParams;
68     private boolean mIsProfileOwnerProvisioning;
69 
PreProvisioningController( @onNull Context context, @NonNull Ui ui)70     public PreProvisioningController(
71             @NonNull Context context,
72             @NonNull Ui ui) {
73         this(context, ui, new MessageParser(), new Utils(),
74                 EncryptionController.getInstance(context));
75     }
76 
77     @VisibleForTesting
PreProvisioningController( @onNull Context context, @NonNull Ui ui, @NonNull MessageParser parser, @NonNull Utils utils, @NonNull EncryptionController encryptionController)78     PreProvisioningController(
79             @NonNull Context context,
80             @NonNull Ui ui,
81             @NonNull MessageParser parser,
82             @NonNull Utils utils,
83             @NonNull EncryptionController encryptionController) {
84         mContext = checkNotNull(context, "Context must not be null");
85         mUi = checkNotNull(ui, "Ui must not be null");
86         mMessageParser = checkNotNull(parser, "MessageParser must not be null");
87         mUtils = checkNotNull(utils, "Utils must not be null");
88         mEncryptionController = checkNotNull(encryptionController,
89                 "EncryptionController must not be null");
90 
91         mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(
92                 Context.DEVICE_POLICY_SERVICE);
93         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
94         mPackageManager = mContext.getPackageManager();
95         mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
96         mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
97         mPdbManager = (PersistentDataBlockManager) mContext.getSystemService(
98                 Context.PERSISTENT_DATA_BLOCK_SERVICE);
99     }
100 
101     interface Ui {
102         /**
103          * Show an error message and cancel provisioning.
104          *
105          * @param resId resource id used to form the user facing error message
106          * @param errorMessage an error message that gets logged for debugging
107          */
showErrorAndClose(int resId, String errorMessage)108         void showErrorAndClose(int resId, String errorMessage);
109 
110         /**
111          * Request the user to encrypt the device.
112          *
113          * @param params the {@link ProvisioningParams} object related to the ongoing provisioning
114          */
requestEncryption(ProvisioningParams params)115         void requestEncryption(ProvisioningParams params);
116 
117         /**
118          * Request the user to choose a wifi network.
119          */
requestWifiPick()120         void requestWifiPick();
121 
122         /**
123          * Initialize the pre provisioning UI with the mdm info and the relevant strings.
124          *
125          * @param headerRes resource id for the header text
126          * @param titleRes resource id for the title text
127          * @param consentRes resource id of the consent text
128          * @param mdmInfoRes resource id for the mdm info text
129          * @param params the {@link ProvisioningParams} object related to the ongoing provisioning
130          */
initiateUi(int headerRes, int titleRes, int consentRes, int mdmInfoRes, ProvisioningParams params)131         void initiateUi(int headerRes, int titleRes, int consentRes, int mdmInfoRes,
132                 ProvisioningParams params);
133 
134         /**
135          * Start device owner provisioning.
136          *
137          * @param userId the id of the user we want to start provisioning on
138          * @param params the {@link ProvisioningParams} object related to the ongoing provisioning
139          */
startDeviceOwnerProvisioning(int userId, ProvisioningParams params)140         void startDeviceOwnerProvisioning(int userId, ProvisioningParams params);
141 
142         /**
143          * Start profile owner provisioning.
144          *
145          * @param params the {@link ProvisioningParams} object related to the ongoing provisioning
146          */
startProfileOwnerProvisioning(ProvisioningParams params)147         void startProfileOwnerProvisioning(ProvisioningParams params);
148 
149         /**
150          * Show a user consent dialog.
151          *
152          * @param params the {@link ProvisioningParams} object related to the ongoing provisioning
153          * @param isProfileOwnerProvisioning whether we're provisioning a profile owner
154          */
showUserConsentDialog(ProvisioningParams params, boolean isProfileOwnerProvisioning)155         void showUserConsentDialog(ProvisioningParams params, boolean isProfileOwnerProvisioning);
156 
157         /**
158          * Show a dialog to delete an existing managed profile.
159          *
160          * @param mdmPackageName the {@link ComponentName} of the existing profile's profile owner
161          * @param domainName domain name of the organization which owns the managed profile
162          *
163          * @param userId the user id of the existing profile
164          */
showDeleteManagedProfileDialog(ComponentName mdmPackageName, String domainName, int userId)165         void showDeleteManagedProfileDialog(ComponentName mdmPackageName, String domainName,
166                 int userId);
167 
168         /**
169          * Show an error dialog indicating that the current launcher does not support managed
170          * profiles and ask the user to choose a different one.
171          */
showCurrentLauncherInvalid()172         void showCurrentLauncherInvalid();
173     }
174 
initiateProvisioning(Intent intent, String callingPackage)175     public void initiateProvisioning(Intent intent, String callingPackage) {
176         // Check factory reset protection as the first thing
177         if (factoryResetProtected()) {
178             mUi.showErrorAndClose(R.string.device_owner_error_frp,
179                     "Factory reset protection blocks provisioning.");
180             return;
181         }
182 
183         try {
184             // Read the provisioning params from the provisioning intent
185             mParams = mMessageParser.parse(intent, mContext);
186 
187             // If this is a resume after encryption or trusted intent, we don't need to verify the
188             // caller. Otherwise, verify that the calling app is trying to set itself as
189             // Device/ProfileOwner
190             if (!ACTION_RESUME_PROVISIONING.equals(intent.getAction()) &&
191                     !mParams.startedByTrustedSource) {
192                 verifyCaller(callingPackage);
193             }
194         } catch (IllegalProvisioningArgumentException e) {
195             // TODO: make this a generic error message
196             mUi.showErrorAndClose(R.string.device_owner_error_general, e.getMessage());
197             return;
198         }
199 
200         mIsProfileOwnerProvisioning = mUtils.isProfileOwnerAction(mParams.provisioningAction);
201         // Check whether provisioning is allowed for the current action
202         if (!mDevicePolicyManager.isProvisioningAllowed(mParams.provisioningAction)) {
203             showProvisioningError(mParams.provisioningAction);
204             return;
205         }
206 
207         // Initiate the corresponding provisioning mode
208         if (mIsProfileOwnerProvisioning) {
209             initiateProfileOwnerProvisioning(intent);
210         } else {
211             initiateDeviceOwnerProvisioning(intent);
212         }
213     }
214 
215     /**
216      * Verify that the caller is trying to set itself as owner.
217      *
218      * @throws IllegalProvisioningArgumentException if the caller is trying to set a different
219      * package as owner.
220      */
verifyCaller(@onNull String callingPackage)221     private void verifyCaller(@NonNull String callingPackage)
222             throws IllegalProvisioningArgumentException {
223         checkNotNull(callingPackage,
224                 "Calling package is null. Was startActivityForResult used to start this activity?");
225         if (!callingPackage.equals(mParams.inferDeviceAdminPackageName())) {
226             throw new IllegalProvisioningArgumentException("Permission denied, "
227                     + "calling package tried to set a different package as owner. ");
228         }
229     }
230 
initiateDeviceOwnerProvisioning(Intent intent)231     private void initiateDeviceOwnerProvisioning(Intent intent) {
232         if (!mParams.startedByTrustedSource) {
233             mUi.initiateUi(
234                     R.string.setup_work_device,
235                     R.string.setup_device_start_setup,
236                     R.string.company_controls_device,
237                     R.string.the_following_is_your_mdm_for_device,
238                     mParams);
239         }
240 
241         // Ask to encrypt the device before proceeding
242         if (isEncryptionRequired()) {
243             maybeTriggerEncryption();
244             return;
245         }
246 
247         // Have the user pick a wifi network if necessary.
248         // It is not possible to ask the user to pick a wifi network if
249         // the screen is locked.
250         // TODO: remove this check once we know the screen will not be locked.
251         if (mKeyguardManager.inKeyguardRestrictedInputMode()) {
252             ProvisionLogger.logi("Cannot pick wifi because the screen is locked.");
253             // Have the user pick a wifi network if necessary.
254         } else if (!mUtils.isConnectedToNetwork(mContext) && mParams.wifiInfo == null) {
255             if (canRequestWifiPick()) {
256                 mUi.requestWifiPick();
257                 return;
258             } else {
259                 ProvisionLogger.logi(
260                         "Cannot pick wifi because there is no handler to the intent");
261             }
262         }
263         askForConsentOrStartDeviceOwnerProvisioning();
264     }
265 
initiateProfileOwnerProvisioning(Intent intent)266     private void initiateProfileOwnerProvisioning(Intent intent) {
267         mUi.initiateUi(
268                 R.string.setup_work_profile,
269                 R.string.setup_profile_start_setup,
270                 R.string.company_controls_workspace,
271                 R.string.the_following_is_your_mdm,
272                 mParams);
273 
274         // If there is already a managed profile, setup the profile deletion dialog.
275         int existingManagedProfileUserId = mUtils.alreadyHasManagedProfile(mContext);
276         if (existingManagedProfileUserId != -1) {
277             ComponentName mdmPackageName = mDevicePolicyManager
278                     .getProfileOwnerAsUser(existingManagedProfileUserId);
279             String domainName = mDevicePolicyManager
280                     .getProfileOwnerNameAsUser(existingManagedProfileUserId);
281             mUi.showDeleteManagedProfileDialog(mdmPackageName, domainName,
282                     existingManagedProfileUserId);
283         }
284     }
285 
286     /**
287      * Start provisioning for real. In profile owner case, double check that the launcher
288      * supports managed profiles if necessary. In device owner case, possibly create a new user
289      * before starting provisioning.
290      */
continueProvisioningAfterUserConsent()291     public void continueProvisioningAfterUserConsent() {
292         if (isProfileOwnerProvisioning()) {
293             checkLauncherAndStartProfileOwnerProvisioning();
294         } else {
295             maybeCreateUserAndStartDeviceOwnerProvisioning();
296         }
297     }
298 
299     /**
300      * Invoked when the user continues provisioning by pressing the next button.
301      *
302      * <p>If device hasn't been encrypted yet, invoke the encryption flow. Otherwise, show a user
303      * consent before starting provisioning.
304      */
afterNavigateNext()305     public void afterNavigateNext() {
306         if (isEncryptionRequired()) {
307             maybeTriggerEncryption();
308         } else {
309             // Notify the user once more that the admin will have full control over the profile,
310             // then start provisioning.
311             mUi.showUserConsentDialog(mParams, mIsProfileOwnerProvisioning);
312         }
313     }
314 
315     /**
316      * Returns whether the device needs encryption.
317      *
318      * @param skip indicating whether the parameter to skip encryption was given.
319      */
isEncryptionRequired()320     private boolean isEncryptionRequired() {
321         return !mParams.skipEncryption && mUtils.isEncryptionRequired();
322     }
323 
324     /**
325      * Check whether the device supports encryption. If it does not support encryption, but
326      * encryption is requested, show an error dialog.
327      */
maybeTriggerEncryption()328     private void maybeTriggerEncryption() {
329         if (mDevicePolicyManager.getStorageEncryptionStatus() ==
330                 DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED) {
331             mUi.showErrorAndClose(R.string.preprovisioning_error_encryption_not_supported,
332                     "This device does not support encryption, but "
333                     + DevicePolicyManager.EXTRA_PROVISIONING_SKIP_ENCRYPTION
334                     + " was not passed.");
335         } else {
336             mUi.requestEncryption(mParams);
337         }
338     }
339 
checkLauncherAndStartProfileOwnerProvisioning()340     private void checkLauncherAndStartProfileOwnerProvisioning() {
341         // Check whether the current launcher supports managed profiles.
342         if (!mUtils.currentLauncherSupportsManagedProfiles(mContext)) {
343             mUi.showCurrentLauncherInvalid();
344         } else {
345             // Cancel the boot reminder as provisioning has now started.
346             mEncryptionController.cancelEncryptionReminder();
347             mUi.startProfileOwnerProvisioning(mParams);
348         }
349     }
350 
askForConsentOrStartDeviceOwnerProvisioning()351     public void askForConsentOrStartDeviceOwnerProvisioning() {
352         // If we are started by Nfc and the device supports FRP, we need to ask for user consent
353         // since FRP will not be activated at the end of the flow.
354         if (mParams.startedByTrustedSource) {
355             if (mUtils.isFrpSupported(mContext)) {
356                 mUi.showUserConsentDialog(mParams, false);
357             } else {
358                 maybeCreateUserAndStartDeviceOwnerProvisioning();
359             }
360         }
361         // In other provisioning modes we wait for the user to press next.
362     }
363 
maybeCreateUserAndStartDeviceOwnerProvisioning()364     private void maybeCreateUserAndStartDeviceOwnerProvisioning() {
365         // Cancel the boot reminder as provisioning has now started.
366         mEncryptionController.cancelEncryptionReminder();
367         if (isMeatUserCreationRequired(mParams.provisioningAction)) {
368             // Create the primary user, and continue the provisioning in this user.
369             new CreatePrimaryUserTask().execute();
370         } else {
371             mUi.startDeviceOwnerProvisioning(mUserManager.getUserHandle(), mParams);
372         }
373     }
374 
factoryResetProtected()375     private boolean factoryResetProtected() {
376         // If we are started during setup wizard, check for factory reset protection.
377         // If the device is already setup successfully, do not check factory reset protection.
378         if (mUtils.isDeviceProvisioned(mContext)) {
379             ProvisionLogger.logd("Device is provisioned, FRP not required.");
380             return false;
381         }
382 
383         if (mPdbManager == null) {
384             ProvisionLogger.logd("Reset protection not supported.");
385             return false;
386         }
387         int size = mPdbManager.getDataBlockSize();
388         ProvisionLogger.logd("Data block size: " + size);
389         return size > 0;
390     }
391 
isMeatUserCreationRequired(String action)392     public boolean isMeatUserCreationRequired(String action) {
393         if (mUtils.isSplitSystemUser()
394                 && ACTION_PROVISION_MANAGED_DEVICE.equals(action)) {
395             List<UserInfo> users = mUserManager.getUsers();
396             if (users.size() > 1) {
397                 mUi.showErrorAndClose(R.string.device_owner_error_general,
398                         "Cannot start Device Owner Provisioning because there are already "
399                         + users.size() + " users");
400                 return false;
401             }
402             return true;
403         } else {
404             return false;
405         }
406     }
407 
canRequestWifiPick()408     private boolean canRequestWifiPick() {
409         return mPackageManager.resolveActivity(mUtils.getWifiPickIntent(), 0) != null;
410     }
411 
systemHasManagedProfileFeature()412     private boolean systemHasManagedProfileFeature() {
413         return mPackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS);
414     }
415 
416     /**
417      * Returns whether the provisioning process is a profile owner provisioning process.
418      */
isProfileOwnerProvisioning()419     public boolean isProfileOwnerProvisioning() {
420         return mIsProfileOwnerProvisioning;
421     }
422 
423     @NonNull
getParams()424     public ProvisioningParams getParams() {
425         if (mParams == null) {
426             throw new IllegalStateException("ProvisioningParams are null");
427         }
428         return mParams;
429     }
430 
431     // TODO: review the use of async task for the case where the activity might have got killed
432     private class CreatePrimaryUserTask extends AsyncTask<Void, Void, UserInfo> {
433         @Override
doInBackground(Void... args)434         protected UserInfo doInBackground(Void... args) {
435             // Create the user where we're going to install the device owner.
436             UserInfo userInfo = mUserManager.createUser(
437                     mContext.getString(R.string.default_first_meat_user_name),
438                     UserInfo.FLAG_PRIMARY | UserInfo.FLAG_ADMIN);
439 
440             if (userInfo != null) {
441                 ProvisionLogger.logi("Created user " + userInfo.id + " to hold the device owner");
442             }
443             return userInfo;
444         }
445 
446         @Override
onPostExecute(UserInfo userInfo)447         protected void onPostExecute(UserInfo userInfo) {
448             if (userInfo == null) {
449                 mUi.showErrorAndClose(R.string.device_owner_error_general,
450                         "Could not create user to hold the device owner");
451             } else {
452                 mActivityManager.switchUser(userInfo.id);
453                 mUi.startDeviceOwnerProvisioning(userInfo.id, mParams);
454             }
455         }
456     }
457 
showProvisioningError(String action)458     private void showProvisioningError(String action) {
459         UserInfo userInfo = mUserManager.getUserInfo(mUserManager.getUserHandle());
460         if (DevicePolicyManager.ACTION_PROVISION_MANAGED_USER.equals(action)) {
461             mUi.showErrorAndClose(R.string.user_setup_incomplete,
462                         "Exiting managed user provisioning, setup incomplete");
463         } else if (DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE.equals(action)) {
464             // Try to show an error message explaining why provisioning is not allowed.
465             if (!systemHasManagedProfileFeature()) {
466                 mUi.showErrorAndClose(R.string.managed_provisioning_not_supported,
467                         "Exiting managed profile provisioning, "
468                         + "managed profiles feature is not available");
469             } else if (!userInfo.canHaveProfile()) {
470                 mUi.showErrorAndClose(R.string.user_cannot_have_work_profile,
471                         "Exiting managed profile provisioning, calling user cannot have managed"
472                         + "profiles.");
473             } else if (mUtils.isDeviceManaged(mContext)) {
474                 // The actual check in isProvisioningAllowed() is more than just "is there DO?",
475                 // but for error message showing purpose, isDeviceManaged() will do.
476                 mUi.showErrorAndClose(R.string.device_owner_exists,
477                         "Exiting managed profile provisioning, a device owner exists");
478             } else if (!mUserManager.canAddMoreManagedProfiles(UserHandle.myUserId(),
479                     true /* after removing one eventual existing managed profile */)) {
480                 mUi.showErrorAndClose(R.string.maximum_user_limit_reached,
481                         "Exiting managed profile provisioning, cannot add more managed profiles.");
482             } else {
483                 mUi.showErrorAndClose(R.string.managed_provisioning_error_text, "Managed profile"
484                         + " provisioning not allowed for an unknown reason.");
485             }
486         } else if (mUtils.isDeviceProvisioned(mContext)) {
487             mUi.showErrorAndClose(R.string.device_owner_error_already_provisioned,
488                     "Device already provisioned.");
489         } else if (!mUtils.isCurrentUserSystem()) {
490             mUi.showErrorAndClose(R.string.device_owner_error_general,
491                     "Device owner can only be set up for USER_SYSTEM.");
492         } else if (action.equals(ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE) &&
493                 !UserManager.isSplitSystemUser()) {
494             mUi.showErrorAndClose(R.string.device_owner_error_general,
495                     "System User Device owner can only be set on a split-user system.");
496         } else {
497             // TODO: show generic error
498             mUi.showErrorAndClose(R.string.device_owner_error_general,
499                     "Device Owner provisioning not allowed for an unknown reason.");
500         }
501     }
502 }
503