Home | History | Annotate | Download | only in uiflows
      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 
     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
     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          */
    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          */
    115         void requestEncryption(ProvisioningParams params);
    116 
    117         /**
    118          * Request the user to choose a wifi network.
    119          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    172         void showCurrentLauncherInvalid();
    173     }
    174 
    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      */
    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 
    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 
    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      */
    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      */
    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      */
    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      */
    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 
    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 
    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 
    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 
    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 
    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 
    408     private boolean canRequestWifiPick() {
    409         return mPackageManager.resolveActivity(mUtils.getWifiPickIntent(), 0) != null;
    410     }
    411 
    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      */
    419     public boolean isProfileOwnerProvisioning() {
    420         return mIsProfileOwnerProvisioning;
    421     }
    422 
    423     @NonNull
    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
    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
    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 
    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