Home | History | Annotate | Download | only in managedprovisioning
      1 /*
      2  * Copyright 2015, 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;
     18 
     19 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE;
     20 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE;
     21 import static android.nfc.NfcAdapter.ACTION_NDEF_DISCOVERED;
     22 
     23 import android.app.Activity;
     24 import android.app.AlertDialog;
     25 import android.content.Context;
     26 import android.content.DialogInterface;
     27 import android.content.Intent;
     28 import android.os.Bundle;
     29 import android.os.SystemProperties;
     30 import android.provider.Settings.Global;
     31 import android.provider.Settings.Secure;
     32 import android.service.persistentdata.PersistentDataBlockManager;
     33 import android.text.TextUtils;
     34 import android.view.View;
     35 import android.widget.ImageView;
     36 import android.widget.TextView;
     37 
     38 import com.android.managedprovisioning.task.AddWifiNetworkTask;
     39 import com.android.managedprovisioning.Utils.IllegalProvisioningArgumentException;
     40 import com.android.managedprovisioning.Utils.MdmPackageInfo;
     41 
     42 /**
     43  * This activity makes necessary checks before starting {@link DeviceOwnerProvisioningActivity}.
     44  * It checks if the device or user is already setup. It makes sure the device is encrypted, the
     45  * provisioning intent extras are valid and the device is connected to a wifi network.
     46  */
     47 public class DeviceOwnerPreProvisioningActivity extends SetupLayoutActivity
     48         implements UserConsentDialog.ConsentCallback {
     49     private static final boolean DEBUG = false; // To control logging.
     50 
     51     private static final String KEY_USER_CONSENTED = "user_consented";
     52 
     53     private static final int ENCRYPT_DEVICE_REQUEST_CODE = 1;
     54     private static final int WIFI_REQUEST_CODE = 2;
     55     private static final int PROVISIONING_REQUEST_CODE = 3;
     56 
     57     // Indicates whether user consented by clicking on positive button of consent dialog.
     58     private boolean mUserConsented = false;
     59 
     60     // Params that will be used after user consent.
     61     // Extracted from the starting intent.
     62     private ProvisioningParams mParams;
     63 
     64     // Legacy action, internal only, but that we still want to support.
     65     static final String LEGACY_ACTION_PROVISION_MANAGED_DEVICE
     66             = "com.android.managedprovisioning.ACTION_PROVISION_MANAGED_DEVICE";
     67 
     68     /**
     69      * Action used to start DeviceOwnerPreProvisioningActivity on a secondary user.
     70      */
     71     protected static final String ACTION_PROVISION_SECONDARY_USER
     72             = "com.android.managedprovisioning.ACTION_PROVISION_SECONDARY_USER";
     73 
     74     @Override
     75     public void onCreate(Bundle savedInstanceState) {
     76         super.onCreate(savedInstanceState);
     77         if (DEBUG) ProvisionLogger.logd("DeviceOwnerPreProvisioningActivity ONCREATE");
     78 
     79         if (savedInstanceState != null) {
     80             mUserConsented = savedInstanceState.getBoolean(KEY_USER_CONSENTED, false);
     81         }
     82 
     83         // Setup the UI.
     84         initializeLayoutParams(R.layout.user_consent, R.string.setup_work_device, false);
     85         configureNavigationButtons(R.string.set_up, View.INVISIBLE, View.VISIBLE);
     86 
     87         // Check whether we have already provisioned this user.
     88         if (Utils.isCurrentUserOwner()) {
     89             int provisioned =
     90                     Global.getInt(getContentResolver(), Global.DEVICE_PROVISIONED, 0 /*defaults*/);
     91             if (provisioned != 0) {
     92                 showErrorAndClose(R.string.device_owner_error_already_provisioned,
     93                         "Device already provisioned.");
     94                 return;
     95             }
     96         } else {
     97              int provisioned =
     98                     Secure.getInt(getContentResolver(), Secure.USER_SETUP_COMPLETE, 0 /*default*/);
     99             if (provisioned != 0) {
    100                 showErrorAndClose(R.string.device_owner_error_already_provisioned_user,
    101                         "User already provisioned.");
    102                 return;
    103             }
    104         }
    105 
    106         if (factoryResetProtected()) {
    107             showErrorAndClose(R.string.device_owner_error_frp,
    108                     "Factory reset protection blocks provisioning.");
    109             return;
    110         }
    111 
    112         // Parse the incoming intent.
    113         MessageParser parser = new MessageParser();
    114         try {
    115             mParams = parseIntentAndMaybeVerifyCaller(getIntent(),
    116                     getPackageName().equals(getCallingPackage()), parser);
    117         } catch (Utils.IllegalProvisioningArgumentException e) {
    118             showErrorAndClose(R.string.device_owner_error_general,
    119                     e.getMessage());
    120             return;
    121         }
    122 
    123         // Ask to encrypt the device before proceeding
    124         if (!(EncryptDeviceActivity.isDeviceEncrypted()
    125                         || SystemProperties.getBoolean("persist.sys.no_req_encrypt", false)
    126                         || mParams.skipEncryption)) {
    127             requestEncryption(parser, mParams);
    128             return;
    129             // System will reboot. Bootreminder will restart this activity.
    130         }
    131 
    132         // Have the user pick a wifi network if necessary.
    133         if (!NetworkMonitor.isConnectedToNetwork(this)
    134                 && TextUtils.isEmpty(mParams.wifiInfo.ssid)
    135                 // If a device initializer is installed, this shouldn't run on secondary users.
    136                 && (!Utils.hasDeviceInitializer(this) || Utils.isCurrentUserOwner())) {
    137             requestWifiPick();
    138             return;
    139             // Wait for onActivityResult.
    140         }
    141         askForConsentOrStartProvisioning();
    142     }
    143 
    144     @Override
    145     protected void onResume() {
    146         super.onResume();
    147         setTitle(R.string.setup_device_start_setup);
    148     }
    149 
    150     private ProvisioningParams parseIntentAndMaybeVerifyCaller(Intent intent, boolean trusted,
    151             MessageParser parser) throws IllegalProvisioningArgumentException {
    152         if (intent.getAction() == null) {
    153             throw new IllegalProvisioningArgumentException("Null intent action.");
    154         } else if (intent.getAction().equals(ACTION_NDEF_DISCOVERED)) {
    155             return parser.parseNfcIntent(intent);
    156         } else if (intent.getAction().equals(LEGACY_ACTION_PROVISION_MANAGED_DEVICE)) {
    157             return parser.parseNonNfcIntent(intent, trusted);
    158         } else if (intent.getAction().equals(ACTION_PROVISION_SECONDARY_USER)) {
    159             if (Utils.isCurrentUserOwner()) {
    160                 throw new IllegalProvisioningArgumentException("Permission denied. " +
    161                     "Was this activity started for a secondary user?");
    162             }
    163             return parser.parseNonNfcIntent(intent, trusted);
    164         } else if (intent.getAction().equals(ACTION_PROVISION_MANAGED_DEVICE)) {
    165             ProvisioningParams params = parser.parseMinimalistNonNfcIntent(intent);
    166             String callingPackage = getCallingPackage();
    167             if (callingPackage == null) {
    168                 throw new IllegalProvisioningArgumentException("Calling package is null. " +
    169                     "Was startActivityForResult used to start this activity?");
    170             }
    171             if (!callingPackage.equals(params.inferDeviceAdminPackageName())) {
    172                 throw new IllegalProvisioningArgumentException("Permission denied, "
    173                         + "calling package tried to set a different package as device owner. ");
    174             }
    175             return params;
    176         }
    177         throw new IllegalProvisioningArgumentException("Unknown intent action "
    178                 + intent.getAction());
    179     }
    180 
    181     private boolean factoryResetProtected() {
    182         if (!Utils.isCurrentUserOwner()) {
    183             ProvisionLogger.logd("Reset protection check skipped on secondary users.");
    184             return false;
    185         }
    186 
    187         // If we are started by setup wizard, do not check for factory reset protection since setup
    188         // wizard must have already validated it. This avoids provisioning being blocked by FRP
    189         // if an account was added in the setup wizard on devices that enforce FRP as soon as the
    190         // account is added.
    191         if (getIntent() != null) {
    192             String action = getIntent().getAction();
    193             if (ACTION_PROVISION_MANAGED_DEVICE.equals(action) ||
    194                     LEGACY_ACTION_PROVISION_MANAGED_DEVICE.equals(action)) {
    195                 ProvisionLogger.logd("FRP not required if started by SUW");
    196                 return false;
    197             }
    198         }
    199 
    200         PersistentDataBlockManager pdbManager = (PersistentDataBlockManager)
    201                 getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
    202         if (pdbManager == null) {
    203             ProvisionLogger.logd("Reset protection not supported.");
    204             return false;
    205         }
    206         int size = pdbManager.getDataBlockSize();
    207         ProvisionLogger.logd("Data block size: " + size);
    208         return size > 0;
    209     }
    210 
    211     private void askForConsentOrStartProvisioning() {
    212         // If we are started by Nfc and the device supports FRP, we need to ask for user consent
    213         // since FRP will not be activated at the end of the flow.
    214         if (mParams.startedByNfc && Utils.isFrpSupported(this)) {
    215             showUserConsentDialog();
    216         } else if (mUserConsented || mParams.startedByNfc || !Utils.isCurrentUserOwner()) {
    217             startDeviceOwnerProvisioning();
    218         } else {
    219             showStartProvisioningButton();
    220             TextView consentMessageTextView = (TextView) findViewById(R.id.user_consent_message);
    221             consentMessageTextView.setText(R.string.company_controls_device);
    222             TextView mdmInfoTextView = (TextView) findViewById(R.id.mdm_info_message);
    223             mdmInfoTextView.setText(R.string.the_following_is_your_mdm_for_device);
    224             setMdmInfo();
    225         }
    226     }
    227 
    228     private void startDeviceOwnerProvisioning() {
    229         Intent intent = new Intent(this, DeviceOwnerProvisioningActivity.class);
    230         intent.putExtra(ProvisioningParams.EXTRA_PROVISIONING_PARAMS, mParams);
    231         startActivityForResult(intent, PROVISIONING_REQUEST_CODE);
    232         // Set cross-fade transition animation into the interstitial progress activity.
    233         overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
    234     }
    235 
    236     private void showErrorAndClose(int resourceId, String logText) {
    237                 ProvisionLogger.loge(logText);
    238         new AlertDialog.Builder(this)
    239                 .setTitle(R.string.provisioning_error_title)
    240                 .setMessage(resourceId)
    241                 .setCancelable(false)
    242                 .setPositiveButton(R.string.device_owner_error_ok,
    243                         new DialogInterface.OnClickListener() {
    244                             @Override
    245                             public void onClick(DialogInterface dialog,int id) {
    246                                 // Close activity
    247                                 DeviceOwnerPreProvisioningActivity.this.setResult(
    248                                         Activity.RESULT_CANCELED);
    249                                 DeviceOwnerPreProvisioningActivity.this.finish();
    250                             }
    251                         })
    252                 .show();
    253     }
    254 
    255     private void requestEncryption(MessageParser messageParser, ProvisioningParams params) {
    256         Intent encryptIntent = new Intent(this, EncryptDeviceActivity.class);
    257 
    258         Bundle resumeExtras = new Bundle();
    259         resumeExtras.putString(EncryptDeviceActivity.EXTRA_RESUME_TARGET,
    260                 EncryptDeviceActivity.TARGET_DEVICE_OWNER);
    261         messageParser.addProvisioningParamsToBundle(resumeExtras, params);
    262 
    263         encryptIntent.putExtra(EncryptDeviceActivity.EXTRA_RESUME, resumeExtras);
    264 
    265         startActivityForResult(encryptIntent, ENCRYPT_DEVICE_REQUEST_CODE);
    266     }
    267 
    268     private void requestWifiPick() {
    269         startActivityForResult(AddWifiNetworkTask.getWifiPickIntent(), WIFI_REQUEST_CODE);
    270     }
    271 
    272     @Override
    273     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    274         if (requestCode == ENCRYPT_DEVICE_REQUEST_CODE) {
    275             if (resultCode == RESULT_CANCELED) {
    276                 ProvisionLogger.loge("User canceled device encryption.");
    277                 setResult(RESULT_CANCELED);
    278                 finish();
    279             }
    280         } else if (requestCode == WIFI_REQUEST_CODE) {
    281             if (resultCode == RESULT_CANCELED) {
    282                 ProvisionLogger.loge("User canceled wifi picking.");
    283                 setResult(RESULT_CANCELED);
    284                 finish();
    285             } else if (resultCode == RESULT_OK) {
    286                 if (DEBUG) ProvisionLogger.logd("Wifi request result is OK");
    287                 if (NetworkMonitor.isConnectedToWifi(this)) {
    288                     askForConsentOrStartProvisioning();
    289                 } else {
    290                     requestWifiPick();
    291                 }
    292             }
    293         } else if (requestCode == PROVISIONING_REQUEST_CODE) {
    294             setResult(resultCode);
    295             finish();
    296         }
    297     }
    298 
    299     private void setMdmInfo() {
    300         ImageView imageView = (ImageView) findViewById(R.id.mdm_icon_view);
    301         TextView deviceManagerName = (TextView) findViewById(R.id.device_manager_name);
    302         String packageName = mParams.inferDeviceAdminPackageName();
    303         MdmPackageInfo packageInfo = Utils.getMdmPackageInfo(getPackageManager(), packageName);
    304         if (packageInfo != null) {
    305             String appLabel = packageInfo.getAppLabel();
    306             imageView.setImageDrawable(packageInfo.getPackageIcon());
    307             imageView.setContentDescription(
    308                     getResources().getString(R.string.mdm_icon_label, appLabel));
    309             deviceManagerName.setText(appLabel);
    310         } else {
    311             // Should never happen. Package will be on the device by now.
    312             deviceManagerName.setText(packageName);
    313         }
    314     }
    315 
    316     @Override
    317     protected void onSaveInstanceState(Bundle outState) {
    318         outState.putBoolean(KEY_USER_CONSENTED, mUserConsented);
    319     }
    320 
    321     private void showStartProvisioningButton() {
    322         mNextButton.setVisibility(View.VISIBLE);
    323     }
    324 
    325     @Override
    326     public void onDialogConsent() {
    327         // For accessibility purposes: we need to talk back only the title of the
    328         // next screen after user clicks ok.
    329         setTitle("");
    330         mUserConsented = true;
    331         startDeviceOwnerProvisioning();
    332     }
    333 
    334     @Override
    335     public void onDialogCancel() {
    336         // For Nfc provisioning, we automatically show the user consent dialog if applicable.
    337         // If the user then decides to cancel, we should finish the entire activity and exit.
    338         // For other cases, dismissing the consent dialog will lead back to
    339         // DeviceOwnerPreProvisioningActivity, and we do nothing here.
    340         if (mParams.startedByNfc) {
    341             setResult(RESULT_CANCELED);
    342             finish();
    343         }
    344     }
    345 
    346     @Override
    347     public void onNavigateNext() {
    348         showUserConsentDialog();
    349     }
    350 
    351     /**
    352      * Notify the user that the admin will have full control over the device,
    353      * then start provisioning.
    354      */
    355     private void showUserConsentDialog() {
    356         UserConsentDialog.newInstance(UserConsentDialog.DEVICE_OWNER)
    357                 .show(getFragmentManager(), "UserConsentDialogFragment");
    358     }
    359 }
    360