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.preprovisioning;
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.CODE_ADD_MANAGED_PROFILE_DISALLOWED;
25import static android.app.admin.DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE;
26import static android.app.admin.DevicePolicyManager.CODE_HAS_DEVICE_OWNER;
27import static android.app.admin.DevicePolicyManager.CODE_MANAGED_USERS_NOT_SUPPORTED;
28import static android.app.admin.DevicePolicyManager.CODE_NOT_SYSTEM_USER;
29import static android.app.admin.DevicePolicyManager.CODE_NOT_SYSTEM_USER_SPLIT;
30import static android.app.admin.DevicePolicyManager.CODE_OK;
31import static android.app.admin.DevicePolicyManager.CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER;
32import static android.app.admin.DevicePolicyManager.CODE_USER_SETUP_COMPLETED;
33import static android.nfc.NfcAdapter.ACTION_NDEF_DISCOVERED;
34
35import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_PREPROVISIONING_ACTIVITY_TIME_MS;
36import static com.android.internal.util.Preconditions.checkNotNull;
37import static com.android.managedprovisioning.analytics.ProvisioningAnalyticsTracker.CANCELLED_BEFORE_PROVISIONING;
38import static com.android.managedprovisioning.common.Globals.ACTION_RESUME_PROVISIONING;
39
40import android.annotation.NonNull;
41import android.annotation.Nullable;
42import android.app.ActivityManager;
43import android.app.KeyguardManager;
44import android.app.admin.DevicePolicyManager;
45import android.content.ComponentName;
46import android.content.Context;
47import android.content.Intent;
48import android.content.pm.PackageInfo;
49import android.content.pm.PackageManager;
50import android.content.pm.UserInfo;
51import android.graphics.Bitmap;
52import android.graphics.BitmapFactory;
53import android.graphics.drawable.BitmapDrawable;
54import android.graphics.drawable.Drawable;
55import android.os.AsyncTask;
56import android.os.UserManager;
57import android.service.persistentdata.PersistentDataBlockManager;
58import android.text.TextUtils;
59
60import com.android.internal.annotations.VisibleForTesting;
61import com.android.managedprovisioning.R;
62import com.android.managedprovisioning.analytics.ProvisioningAnalyticsTracker;
63import com.android.managedprovisioning.analytics.TimeLogger;
64import com.android.managedprovisioning.common.IllegalProvisioningArgumentException;
65import com.android.managedprovisioning.common.MdmPackageInfo;
66import com.android.managedprovisioning.common.ProvisionLogger;
67import com.android.managedprovisioning.common.SettingsFacade;
68import com.android.managedprovisioning.common.StoreUtils;
69import com.android.managedprovisioning.common.Utils;
70import com.android.managedprovisioning.model.CustomizationParams;
71import com.android.managedprovisioning.model.ProvisioningParams;
72import com.android.managedprovisioning.parser.MessageParser;
73import com.android.managedprovisioning.preprovisioning.terms.TermsDocument;
74import com.android.managedprovisioning.preprovisioning.terms.TermsProvider;
75
76import java.util.List;
77import java.util.stream.Collectors;
78
79public class PreProvisioningController {
80    private final Context mContext;
81    private final Ui mUi;
82    private final MessageParser mMessageParser;
83    private final Utils mUtils;
84    private final SettingsFacade mSettingsFacade;
85    private final EncryptionController mEncryptionController;
86
87    // used system services
88    private final DevicePolicyManager mDevicePolicyManager;
89    private final UserManager mUserManager;
90    private final PackageManager mPackageManager;
91    private final ActivityManager mActivityManager;
92    private final KeyguardManager mKeyguardManager;
93    private final PersistentDataBlockManager mPdbManager;
94    private final TimeLogger mTimeLogger;
95    private final ProvisioningAnalyticsTracker mProvisioningAnalyticsTracker;
96
97    private ProvisioningParams mParams;
98
99    public PreProvisioningController(
100            @NonNull Context context,
101            @NonNull Ui ui) {
102        this(context, ui,
103                new TimeLogger(context, PROVISIONING_PREPROVISIONING_ACTIVITY_TIME_MS),
104                new MessageParser(context), new Utils(), new SettingsFacade(),
105                EncryptionController.getInstance(context));
106    }
107    @VisibleForTesting
108    PreProvisioningController(
109            @NonNull Context context,
110            @NonNull Ui ui,
111            @NonNull TimeLogger timeLogger,
112            @NonNull MessageParser parser,
113            @NonNull Utils utils,
114            @NonNull SettingsFacade settingsFacade,
115            @NonNull EncryptionController encryptionController) {
116        mContext = checkNotNull(context, "Context must not be null");
117        mUi = checkNotNull(ui, "Ui must not be null");
118        mTimeLogger = checkNotNull(timeLogger, "Time logger must not be null");
119        mMessageParser = checkNotNull(parser, "MessageParser must not be null");
120        mSettingsFacade = checkNotNull(settingsFacade);
121        mUtils = checkNotNull(utils, "Utils must not be null");
122        mEncryptionController = checkNotNull(encryptionController,
123                "EncryptionController must not be null");
124
125        mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(
126                Context.DEVICE_POLICY_SERVICE);
127        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
128        mPackageManager = mContext.getPackageManager();
129        mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
130        mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
131        mPdbManager = (PersistentDataBlockManager) mContext.getSystemService(
132                Context.PERSISTENT_DATA_BLOCK_SERVICE);
133        mProvisioningAnalyticsTracker = ProvisioningAnalyticsTracker.getInstance();
134    }
135
136    interface Ui {
137        /**
138         * Show an error message and cancel provisioning.
139         * @param titleId resource id used to form the user facing error title
140         * @param messageId resource id used to form the user facing error message
141         * @param errorMessage an error message that gets logged for debugging
142         */
143        void showErrorAndClose(Integer titleId, int messageId, String errorMessage);
144
145        /**
146         * Request the user to encrypt the device.
147         * @param params the {@link ProvisioningParams} object related to the ongoing provisioning
148         */
149        void requestEncryption(ProvisioningParams params);
150
151        /**
152         * Request the user to choose a wifi network.
153         */
154        void requestWifiPick();
155
156        /**
157         * Initialize the pre provisioning UI
158         * @param layoutRes resource id for the layout
159         * @param titleRes resource id for the title text
160         * @param packageLabel package label
161         * @param packageIcon package icon
162         * @param isProfileOwnerProvisioning false for Device Owner provisioning
163         * @param isComp true if in COMP provisioning mode
164         * @param termsHeaders list of terms headers
165         * @param customization customization parameters
166         */
167        void initiateUi(int layoutRes, int titleRes, @Nullable String packageLabel,
168                @Nullable Drawable packageIcon, boolean isProfileOwnerProvisioning, boolean isComp,
169                @NonNull List<String> termsHeaders, @NonNull CustomizationParams customization);
170
171        /**
172         * Start provisioning.
173         * @param userId the id of the user we want to start provisioning on
174         * @param params the {@link ProvisioningParams} object related to the ongoing provisioning
175         */
176        void startProvisioning(int userId, ProvisioningParams params);
177
178        /**
179         * Show a dialog to delete an existing managed profile.
180         * @param mdmPackageName the {@link ComponentName} of the existing profile's profile owner
181         * @param domainName domain name of the organization which owns the managed profile
182         * @param userId the user id of the existing profile
183         */
184        void showDeleteManagedProfileDialog(ComponentName mdmPackageName, String domainName,
185                int userId);
186
187        /**
188         * Show an error dialog indicating that the current launcher does not support managed
189         * profiles and ask the user to choose a different one.
190         */
191        void showCurrentLauncherInvalid();
192    }
193
194    /**
195     * Initiates Profile owner and device owner provisioning.
196     * @param intent Intent that started provisioning.
197     * @param params cached ProvisioningParams if it has been parsed from Intent
198     * @param callingPackage Package that started provisioning.
199     */
200    public void initiateProvisioning(Intent intent, ProvisioningParams params,
201            String callingPackage) {
202        mProvisioningAnalyticsTracker.logProvisioningSessionStarted(mContext);
203
204        if (!tryParseParameters(intent, params)) {
205            return;
206        }
207
208        if (!checkFactoryResetProtection(mParams, callingPackage)) {
209            return;
210        }
211
212        if (!verifyActionAndCaller(intent, callingPackage)) {
213            return;
214        }
215
216        // Check whether provisioning is allowed for the current action
217        if (!checkDevicePolicyPreconditions()) {
218            return;
219        }
220
221        // PO preconditions
222        boolean waitForUserDelete = false;
223        if (isProfileOwnerProvisioning()) {
224            // If there is already a managed profile, setup the profile deletion dialog.
225            int existingManagedProfileUserId = mUtils.alreadyHasManagedProfile(mContext);
226            if (existingManagedProfileUserId != -1) {
227                ComponentName mdmPackageName = mDevicePolicyManager
228                        .getProfileOwnerAsUser(existingManagedProfileUserId);
229                String domainName = mDevicePolicyManager
230                        .getProfileOwnerNameAsUser(existingManagedProfileUserId);
231                mUi.showDeleteManagedProfileDialog(mdmPackageName, domainName,
232                        existingManagedProfileUserId);
233                waitForUserDelete = true;
234            }
235        }
236
237        // DO preconditions
238        if (!isProfileOwnerProvisioning()) {
239            // TODO: make a general test based on deviceAdminDownloadInfo field
240            // PO doesn't ever initialize that field, so OK as a general case
241            if (!mUtils.isConnectedToNetwork(mContext) && mParams.wifiInfo == null
242                    && mParams.deviceAdminDownloadInfo != null
243                    && !mParams.useMobileData) {
244                // Have the user pick a wifi network if necessary.
245                // It is not possible to ask the user to pick a wifi network if
246                // the screen is locked.
247                // TODO: remove this check once we know the screen will not be locked.
248                if (mKeyguardManager.inKeyguardRestrictedInputMode()) {
249                    // TODO: decide on what to do in that case; fail? retry on screen unlock?
250                    ProvisionLogger.logi("Cannot pick wifi because the screen is locked.");
251                } else if (canRequestWifiPick()) {
252                    // we resume this method after a successful WiFi pick
253                    // TODO: refactor as evil - logic should be less spread out
254                    mUi.requestWifiPick();
255                    return;
256                } else {
257                    mUi.showErrorAndClose(R.string.cant_set_up_device,
258                            R.string.contact_your_admin_for_help,
259                            "Cannot pick WiFi because there is no handler to the intent");
260                }
261            }
262        }
263
264        mTimeLogger.start();
265        mProvisioningAnalyticsTracker.logPreProvisioningStarted(mContext, intent);
266
267        // as of now this is only true for COMP provisioning, where we already have a user consent
268        // since the DPC is DO already
269        if (mParams.skipUserConsent || isSilentProvisioningForTestingDeviceOwner()
270                || isSilentProvisioningForTestingManagedProfile()) {
271            if (!waitForUserDelete) {
272                continueProvisioningAfterUserConsent();
273            }
274            return;
275        }
276
277        CustomizationParams customization =
278                CustomizationParams.createInstance(mParams, mContext, mUtils);
279
280        // show UI so we can get user's consent to continue
281        if (isProfileOwnerProvisioning()) {
282            boolean isComp = mDevicePolicyManager.isDeviceManaged();
283            mUi.initiateUi(R.layout.intro_profile_owner, R.string.setup_profile, null, null, true,
284                    isComp, getDisclaimerHeadings(), customization);
285        } else {
286            String packageName = mParams.inferDeviceAdminPackageName();
287            MdmPackageInfo packageInfo = MdmPackageInfo.createFromPackageName(mContext,
288                    packageName);
289            // Always take packageInfo first for installed app since PackageManager is more reliable
290            String packageLabel = packageInfo != null ? packageInfo.appLabel
291                    : mParams.deviceAdminLabel != null ? mParams.deviceAdminLabel : packageName;
292            Drawable packageIcon = packageInfo != null ? packageInfo.packageIcon
293                    : getDeviceAdminIconDrawable(mParams.deviceAdminIconFilePath);
294            mUi.initiateUi(R.layout.intro_device_owner,
295                    R.string.setup_device,
296                    packageLabel,
297                    packageIcon,
298                    false  /* isProfileOwnerProvisioning */,
299                    false, /* isComp */
300                    getDisclaimerHeadings(),
301                    customization);
302        }
303    }
304
305    private @NonNull List<String> getDisclaimerHeadings() {
306        // TODO: only fetch headings, no need to fetch content; now not fast, but at least correct
307        return new TermsProvider(mContext, StoreUtils::readString, mUtils)
308                .getTerms(mParams, TermsProvider.Flags.SKIP_GENERAL_DISCLAIMER)
309                .stream()
310                .map(TermsDocument::getHeading)
311                .collect(Collectors.toList());
312    }
313
314    private Drawable getDeviceAdminIconDrawable(String deviceAdminIconFilePath) {
315        if (deviceAdminIconFilePath == null) {
316            return null;
317        }
318
319        Bitmap bitmap = BitmapFactory.decodeFile(mParams.deviceAdminIconFilePath);
320        if (bitmap == null) {
321            return null;
322        }
323        return new BitmapDrawable(mContext.getResources(), bitmap);
324    }
325
326    /**
327     * Start provisioning for real. In profile owner case, double check that the launcher
328     * supports managed profiles if necessary. In device owner case, possibly create a new user
329     * before starting provisioning.
330     */
331    public void continueProvisioningAfterUserConsent() {
332        // check if encryption is required
333        if (isEncryptionRequired()) {
334            if (mDevicePolicyManager.getStorageEncryptionStatus()
335                    == DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED) {
336                mUi.showErrorAndClose(R.string.cant_set_up_device,
337                        R.string.device_doesnt_allow_encryption_contact_admin,
338                        "This device does not support encryption, and "
339                                + DevicePolicyManager.EXTRA_PROVISIONING_SKIP_ENCRYPTION
340                                + " was not passed.");
341            } else {
342                mUi.requestEncryption(mParams);
343                // we come back to this method after returning from encryption dialog
344                // TODO: refactor as evil - logic should be less spread out
345            }
346            return;
347        }
348
349        if (isProfileOwnerProvisioning()) { // PO case
350            // Check whether the current launcher supports managed profiles.
351            if (!mUtils.currentLauncherSupportsManagedProfiles(mContext)) {
352                mUi.showCurrentLauncherInvalid();
353                // we come back to this method after returning from launcher dialog
354                // TODO: refactor as evil - logic should be less spread out
355                return;
356            } else {
357                // Cancel the boot reminder as provisioning has now started.
358                mEncryptionController.cancelEncryptionReminder();
359                stopTimeLogger();
360                mUi.startProvisioning(mUserManager.getUserHandle(), mParams);
361            }
362        } else { // DO case
363            // Cancel the boot reminder as provisioning has now started.
364            mEncryptionController.cancelEncryptionReminder();
365            if (isMeatUserCreationRequired(mParams.provisioningAction)) {
366                // Create the primary user, and continue the provisioning in this user.
367                // successful end of this task triggers provisioning
368                // TODO: refactor as evil - logic should be less spread out
369                new CreatePrimaryUserTask().execute();
370            } else {
371                stopTimeLogger();
372                mUi.startProvisioning(mUserManager.getUserHandle(), mParams);
373            }
374        }
375    }
376
377    /** @return False if condition preventing further provisioning */
378    @VisibleForTesting
379    boolean checkFactoryResetProtection(ProvisioningParams params, String callingPackage) {
380        if (skipFactoryResetProtectionCheck(params, callingPackage)) {
381            return true;
382        }
383        if (factoryResetProtected()) {
384            mUi.showErrorAndClose(R.string.cant_set_up_device,
385                    R.string.device_has_reset_protection_contact_admin,
386                    "Factory reset protection blocks provisioning.");
387            return false;
388        }
389        return true;
390    }
391
392    private boolean skipFactoryResetProtectionCheck(
393            ProvisioningParams params, String callingPackage) {
394        if (TextUtils.isEmpty(callingPackage)) {
395            return false;
396        }
397        String persistentDataPackageName = mContext.getResources()
398                .getString(com.android.internal.R.string.config_persistentDataPackageName);
399        try {
400            // Only skip the FRP check if the caller is the package responsible for maintaining FRP
401            // - i.e. if this is a flow for restoring device owner after factory reset.
402            PackageInfo callingPackageInfo = mPackageManager.getPackageInfo(callingPackage, 0);
403            return callingPackageInfo != null
404                    && callingPackageInfo.applicationInfo != null
405                    && callingPackageInfo.applicationInfo.isSystemApp()
406                    && !TextUtils.isEmpty(persistentDataPackageName)
407                    && callingPackage.equals(persistentDataPackageName)
408                    && params != null
409                    && params.startedByTrustedSource;
410        } catch (PackageManager.NameNotFoundException e) {
411            ProvisionLogger.loge("Calling package not found.", e);
412            return false;
413        }
414    }
415
416    /** @return False if condition preventing further provisioning */
417    @VisibleForTesting protected boolean checkDevicePolicyPreconditions() {
418        // If isSilentProvisioningForTestingDeviceOwner returns true, the component must be
419        // current device owner, and we can safely ignore isProvisioningAllowed as we don't call
420        // setDeviceOwner.
421        if (isSilentProvisioningForTestingDeviceOwner()) {
422            return true;
423        }
424
425        int provisioningPreCondition = mDevicePolicyManager.checkProvisioningPreCondition(
426                mParams.provisioningAction, mParams.inferDeviceAdminPackageName());
427        // Check whether provisioning is allowed for the current action.
428        if (provisioningPreCondition != CODE_OK) {
429            mProvisioningAnalyticsTracker.logProvisioningNotAllowed(mContext,
430                    provisioningPreCondition);
431            showProvisioningErrorAndClose(mParams.provisioningAction, provisioningPreCondition);
432            return false;
433        }
434        return true;
435    }
436
437    /** @return False if condition preventing further provisioning */
438    private boolean tryParseParameters(Intent intent, ProvisioningParams params) {
439        try {
440            // Read the provisioning params from the provisioning intent
441            mParams = params == null ? mMessageParser.parse(intent) : params;
442        } catch (IllegalProvisioningArgumentException e) {
443            mUi.showErrorAndClose(R.string.cant_set_up_device, R.string.contact_your_admin_for_help,
444                    e.getMessage());
445            return false;
446        }
447        return true;
448    }
449
450    /** @return False if condition preventing further provisioning */
451    @VisibleForTesting protected boolean verifyActionAndCaller(Intent intent,
452            String callingPackage) {
453        if (verifyActionAndCallerInner(intent, callingPackage)) {
454            return true;
455        } else {
456            mUi.showErrorAndClose(R.string.cant_set_up_device, R.string.contact_your_admin_for_help,
457                    "invalid intent or calling package");
458            return false;
459        }
460    }
461
462    private boolean verifyActionAndCallerInner(Intent intent, String callingPackage) {
463        // If this is a resume after encryption or trusted intent, we verify the activity alias.
464        // Otherwise, verify that the calling app is trying to set itself as Device/ProfileOwner
465        if (ACTION_RESUME_PROVISIONING.equals(intent.getAction())) {
466            return verifyActivityAlias(intent, "PreProvisioningActivityAfterEncryption");
467        } else if (ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
468            return verifyActivityAlias(intent, "PreProvisioningActivityViaNfc");
469        } else if (ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE.equals(intent.getAction())) {
470            return verifyActivityAlias(intent, "PreProvisioningActivityViaTrustedApp");
471        } else {
472            return verifyCaller(callingPackage);
473        }
474    }
475
476    private boolean verifyActivityAlias(Intent intent, String activityAlias) {
477        ComponentName componentName = intent.getComponent();
478        if (componentName == null || componentName.getClassName() == null) {
479            ProvisionLogger.loge("null class in component when verifying activity alias "
480                    + activityAlias);
481            return false;
482        }
483
484        if (!componentName.getClassName().endsWith(activityAlias)) {
485            ProvisionLogger.loge("Looking for activity alias " + activityAlias + ", but got "
486                    + componentName.getClassName());
487            return false;
488        }
489
490        return true;
491    }
492
493    /**
494     * Verify that the caller is trying to set itself as owner.
495     * @return false if the caller is trying to set a different package as owner.
496     */
497    private boolean verifyCaller(@NonNull String callingPackage) {
498        if (callingPackage == null) {
499            ProvisionLogger.loge("Calling package is null. Was startActivityForResult used to "
500                    + "start this activity?");
501            return false;
502        }
503
504        if (!callingPackage.equals(mParams.inferDeviceAdminPackageName())) {
505            ProvisionLogger.loge("Permission denied, "
506                    + "calling package tried to set a different package as owner. ");
507            return false;
508        }
509
510        return true;
511    }
512
513    /**
514     * Returns whether the device needs encryption.
515     */
516    private boolean isEncryptionRequired() {
517        return !mParams.skipEncryption && mUtils.isEncryptionRequired();
518    }
519
520    private boolean isSilentProvisioningForTestingDeviceOwner() {
521        final ComponentName currentDeviceOwner =
522                mDevicePolicyManager.getDeviceOwnerComponentOnCallingUser();
523        final ComponentName targetDeviceAdmin = mParams.deviceAdminComponentName;
524
525        switch (mParams.provisioningAction) {
526            case DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE:
527                return isPackageTestOnly()
528                        && currentDeviceOwner != null
529                        && targetDeviceAdmin != null
530                        && currentDeviceOwner.equals(targetDeviceAdmin);
531            default:
532                return false;
533        }
534    }
535
536    private boolean isSilentProvisioningForTestingManagedProfile() {
537        return DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE.equals(
538                mParams.provisioningAction) && isPackageTestOnly();
539    }
540
541    private boolean isPackageTestOnly() {
542        return mUtils.isPackageTestOnly(mContext.getPackageManager(),
543                mParams.inferDeviceAdminPackageName(), mUserManager.getUserHandle());
544    }
545
546    /**
547     * Returns whether the device is frp protected during setup wizard.
548     */
549    private boolean factoryResetProtected() {
550        // If we are started during setup wizard, check for factory reset protection.
551        // If the device is already setup successfully, do not check factory reset protection.
552        if (mSettingsFacade.isDeviceProvisioned(mContext)) {
553            ProvisionLogger.logd("Device is provisioned, FRP not required.");
554            return false;
555        }
556
557        if (mPdbManager == null) {
558            ProvisionLogger.logd("Reset protection not supported.");
559            return false;
560        }
561        int size = mPdbManager.getDataBlockSize();
562        ProvisionLogger.logd("Data block size: " + size);
563        return size > 0;
564    }
565
566    /**
567     * Returns whether meat user creation is required or not.
568     * @param action Intent action that started provisioning
569     */
570    public boolean isMeatUserCreationRequired(String action) {
571        if (mUtils.isSplitSystemUser()
572                && ACTION_PROVISION_MANAGED_DEVICE.equals(action)) {
573            List<UserInfo> users = mUserManager.getUsers();
574            if (users.size() > 1) {
575                mUi.showErrorAndClose(R.string.cant_set_up_device,
576                        R.string.contact_your_admin_for_help,
577                        "Cannot start Device Owner Provisioning because there are already "
578                                + users.size() + " users");
579                return false;
580            }
581            return true;
582        } else {
583            return false;
584        }
585    }
586
587    /**
588     * Returns whether activity to pick wifi can be requested or not.
589     */
590    private boolean canRequestWifiPick() {
591        return mPackageManager.resolveActivity(mUtils.getWifiPickIntent(), 0) != null;
592    }
593
594    /**
595     * Returns whether the provisioning process is a profile owner provisioning process.
596     */
597    public boolean isProfileOwnerProvisioning() {
598        return mUtils.isProfileOwnerAction(mParams.provisioningAction);
599    }
600
601    @Nullable
602    public ProvisioningParams getParams() {
603        return mParams;
604    }
605
606    /**
607     * Notifies the time logger to stop.
608     */
609    public void stopTimeLogger() {
610        mTimeLogger.stop();
611    }
612
613    /**
614     * Log if PreProvisioning was cancelled.
615     */
616    public void logPreProvisioningCancelled() {
617        mProvisioningAnalyticsTracker.logProvisioningCancelled(mContext,
618                CANCELLED_BEFORE_PROVISIONING);
619    }
620
621    /**
622     * Removes a user profile. If we are in COMP case, and were blocked by having to delete a user,
623     * resumes COMP provisioning.
624     */
625    public void removeUser(int userProfileId) {
626        // There is a possibility that the DO has set the disallow remove managed profile user
627        // restriction, but is initiating the provisioning. In this case, we still want to remove
628        // the managed profile.
629        // We know that we can remove the managed profile because we checked
630        // DevicePolicyManager.checkProvisioningPreCondition
631        mUserManager.removeUserEvenWhenDisallowed(userProfileId);
632    }
633
634    /**
635     * See comment in place of usage. Check if we were in silent provisioning, got blocked, and now
636     * can resume.
637     */
638    public void checkResumeSilentProvisioning() {
639        if (mParams.skipUserConsent || isSilentProvisioningForTestingDeviceOwner()
640                || isSilentProvisioningForTestingManagedProfile()) {
641            continueProvisioningAfterUserConsent();
642        }
643    }
644
645    // TODO: review the use of async task for the case where the activity might have got killed
646    private class CreatePrimaryUserTask extends AsyncTask<Void, Void, UserInfo> {
647        @Override
648        protected UserInfo doInBackground(Void... args) {
649            // Create the user where we're going to install the device owner.
650            UserInfo userInfo = mUserManager.createUser(
651                    mContext.getString(R.string.default_first_meat_user_name),
652                    UserInfo.FLAG_PRIMARY | UserInfo.FLAG_ADMIN);
653
654            if (userInfo != null) {
655                ProvisionLogger.logi("Created user " + userInfo.id + " to hold the device owner");
656            }
657            return userInfo;
658        }
659
660        @Override
661        protected void onPostExecute(UserInfo userInfo) {
662            if (userInfo == null) {
663                mUi.showErrorAndClose(R.string.cant_set_up_device,
664                        R.string.contact_your_admin_for_help,
665                        "Could not create user to hold the device owner");
666            } else {
667                mActivityManager.switchUser(userInfo.id);
668                stopTimeLogger();
669                // TODO: refactor as evil - logic should be less spread out
670                mUi.startProvisioning(userInfo.id, mParams);
671            }
672        }
673    }
674
675    private void showProvisioningErrorAndClose(String action, int provisioningPreCondition) {
676        // Try to show an error message explaining why provisioning is not allowed.
677        switch (action) {
678            case ACTION_PROVISION_MANAGED_USER:
679                mUi.showErrorAndClose(R.string.cant_set_up_device,
680                        R.string.contact_your_admin_for_help,
681                        "Exiting managed user provisioning, setup incomplete");
682                return;
683            case ACTION_PROVISION_MANAGED_PROFILE:
684                showManagedProfileErrorAndClose(provisioningPreCondition);
685                return;
686            case ACTION_PROVISION_MANAGED_DEVICE:
687            case ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE:
688                showDeviceOwnerErrorAndClose(provisioningPreCondition);
689                return;
690        }
691        // This should never be the case, as showProvisioningError is always called after
692        // verifying the supported provisioning actions.
693    }
694
695    private void showManagedProfileErrorAndClose(int provisioningPreCondition) {
696        UserInfo userInfo = mUserManager.getUserInfo(mUserManager.getUserHandle());
697        ProvisionLogger.logw("DevicePolicyManager.checkProvisioningPreCondition returns code: "
698                + provisioningPreCondition);
699        switch (provisioningPreCondition) {
700            case CODE_ADD_MANAGED_PROFILE_DISALLOWED:
701            case CODE_MANAGED_USERS_NOT_SUPPORTED:
702                mUi.showErrorAndClose(R.string.cant_add_work_profile,
703                        R.string.work_profile_cant_be_added_contact_admin,
704                        "Exiting managed profile provisioning, managed profiles feature is not available");
705                break;
706            case CODE_CANNOT_ADD_MANAGED_PROFILE:
707                if (!userInfo.canHaveProfile()) {
708                    mUi.showErrorAndClose(R.string.cant_add_work_profile,
709                            R.string.work_profile_cant_be_added_contact_admin,
710                            "Exiting managed profile provisioning, calling user cannot have managed profiles");
711                } else if (isRemovingManagedProfileDisallowed()){
712                    mUi.showErrorAndClose(R.string.cant_replace_or_remove_work_profile,
713                            R.string.for_help_contact_admin,
714                            "Exiting managed profile provisioning, removing managed profile is disallowed");
715                } else {
716                    mUi.showErrorAndClose(R.string.cant_add_work_profile,
717                            R.string.work_profile_cant_be_added_contact_admin,
718                            "Exiting managed profile provisioning, cannot add more managed profiles");
719                }
720                break;
721            case CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER:
722                mUi.showErrorAndClose(R.string.cant_add_work_profile,
723                        R.string.contact_your_admin_for_help,
724                        "Exiting managed profile provisioning, a device owner exists");
725                break;
726            default:
727                mUi.showErrorAndClose(R.string.cant_add_work_profile,
728                        R.string.contact_your_admin_for_help,
729                        "Managed profile provisioning not allowed for an unknown " +
730                        "reason, code: " + provisioningPreCondition);
731        }
732    }
733
734    private boolean isRemovingManagedProfileDisallowed() {
735        return mUtils.alreadyHasManagedProfile(mContext) != -1
736                && mUserManager.hasUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE);
737    }
738
739    private void showDeviceOwnerErrorAndClose(int provisioningPreCondition) {
740        switch (provisioningPreCondition) {
741            case CODE_HAS_DEVICE_OWNER:
742            case CODE_USER_SETUP_COMPLETED:
743                mUi.showErrorAndClose(R.string.device_already_set_up,
744                        R.string.if_questions_contact_admin, "Device already provisioned.");
745                return;
746            case CODE_NOT_SYSTEM_USER:
747                mUi.showErrorAndClose(R.string.cant_set_up_device,
748                        R.string.contact_your_admin_for_help,
749                        "Device owner can only be set up for USER_SYSTEM.");
750                return;
751            case CODE_NOT_SYSTEM_USER_SPLIT:
752                mUi.showErrorAndClose(R.string.cant_set_up_device,
753                        R.string.contact_your_admin_for_help,
754                        "System User Device owner can only be set on a split-user system.");
755                return;
756        }
757        mUi.showErrorAndClose(R.string.cant_set_up_device, R.string.contact_your_admin_for_help,
758                "Device Owner provisioning not allowed for an unknown reason.");
759    }
760}
761