Home | History | Annotate | Download | only in managedprovisioning
      1 /*
      2  * Copyright 2014, 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 android.app.Activity;
     20 import android.app.AlertDialog;
     21 import android.content.BroadcastReceiver;
     22 import android.content.Context;
     23 import android.content.DialogInterface;
     24 import android.content.Intent;
     25 import android.content.IntentFilter;
     26 import android.os.Bundle;
     27 import android.os.UserHandle;
     28 import android.os.SystemProperties;
     29 import android.provider.Settings.Global;
     30 import android.provider.Settings.Secure;
     31 import android.service.persistentdata.PersistentDataBlockManager;
     32 import android.support.v4.content.LocalBroadcastManager;
     33 import android.text.TextUtils;
     34 import android.view.LayoutInflater;
     35 import android.view.View;
     36 import android.widget.TextView;
     37 
     38 import com.android.managedprovisioning.task.AddWifiNetworkTask;
     39 
     40 import java.util.ArrayList;
     41 
     42 /**
     43  * This activity starts device owner provisioning:
     44  * It downloads a mobile device management application(mdm) from a given url and installs it,
     45  * or a given mdm is already present on the device. The mdm is set as the owner of the device so
     46  * that it has full control over the device:
     47  * TODO: put link here with documentation on how a device owner has control over the device
     48  * The mdm can then execute further setup steps.
     49  *
     50  * <p>
     51  * An example use case might be when a company wants to set up a device for a single use case
     52  * (such as giving instructions).
     53  * </p>
     54  *
     55  * <p>
     56  * Provisioning is triggered by a programmer device that sends required provisioning parameters via
     57  * nfc. For an example of a programmer app see:
     58  * com.example.android.apis.app.DeviceProvisioningProgrammerSample.
     59  * </p>
     60  *
     61  * <p>
     62  * In the unlikely case that this activity is killed the whole provisioning process so far is
     63  * repeated. We made sure that all tasks can be done twice without causing any problems.
     64  * </p>
     65  */
     66 public class DeviceOwnerProvisioningActivity extends Activity
     67         implements UserConsentDialog.ConsentCallback {
     68     private static final boolean DEBUG = false; // To control logging.
     69 
     70     private static final String KEY_USER_CONSENTED = "user_consented";
     71     private static final String KEY_CANCEL_DIALOG_SHOWN = "cancel_dialog_shown";
     72     private static final String KEY_PENDING_INTENTS = "pending_intents";
     73 
     74     private static final int ENCRYPT_DEVICE_REQUEST_CODE = 1;
     75     private static final int WIFI_REQUEST_CODE = 2;
     76 
     77     private BroadcastReceiver mServiceMessageReceiver;
     78     private TextView mProgressTextView;
     79 
     80     // Indicates whether user consented by clicking on positive button of interstitial.
     81     private boolean mUserConsented = false;
     82 
     83     // Params that will be used after user consent.
     84     // Extracted from the starting intent.
     85     private ProvisioningParams mParams;
     86 
     87     // Indicates that the cancel dialog is shown.
     88     private boolean mCancelDialogShown = false;
     89 
     90     // List of intents received while cancel dialog is shown.
     91     private ArrayList<Intent> mPendingProvisioningIntents = new ArrayList<Intent>();
     92 
     93     @Override
     94     public void onCreate(Bundle savedInstanceState) {
     95         super.onCreate(savedInstanceState);
     96         if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONCREATE");
     97 
     98         if (savedInstanceState != null) {
     99             mUserConsented = savedInstanceState.getBoolean(KEY_USER_CONSENTED, false);
    100             mCancelDialogShown = savedInstanceState.getBoolean(KEY_CANCEL_DIALOG_SHOWN, false);
    101             mPendingProvisioningIntents = savedInstanceState
    102                     .getParcelableArrayList(KEY_PENDING_INTENTS);
    103         }
    104 
    105         // Setup the UI.
    106         final LayoutInflater inflater = getLayoutInflater();
    107         final View contentView = inflater.inflate(R.layout.progress, null);
    108         setContentView(contentView);
    109         mProgressTextView = (TextView) findViewById(R.id.prog_text);
    110         TextView titleText = (TextView) findViewById(R.id.title);
    111         if (titleText != null) titleText.setText(getString(R.string.setup_device));
    112         if (mCancelDialogShown) showCancelResetDialog();
    113 
    114         // Check whether we can provision.
    115         if (Global.getInt(getContentResolver(), Global.DEVICE_PROVISIONED, 0 /* default */) != 0) {
    116             ProvisionLogger.loge("Device already provisioned.");
    117             error(R.string.device_owner_error_already_provisioned, false /* no factory reset */);
    118             return;
    119         }
    120 
    121         if (UserHandle.myUserId() != UserHandle.USER_OWNER) {
    122             ProvisionLogger.loge("Device owner can only be set up for USER_OWNER.");
    123             error(R.string.device_owner_error_general, false /* no factory reset */);
    124             return;
    125         }
    126 
    127         if (factoryResetProtected()) {
    128             ProvisionLogger.loge("Factory reset protection blocks provisioning.");
    129             error(R.string.device_owner_error_already_provisioned, false /* no factory reset */);
    130             return;
    131         }
    132 
    133         // Setup broadcast receiver for feedback from service.
    134         mServiceMessageReceiver = new ServiceMessageReceiver();
    135         IntentFilter filter = new IntentFilter();
    136         filter.addAction(DeviceOwnerProvisioningService.ACTION_PROVISIONING_SUCCESS);
    137         filter.addAction(DeviceOwnerProvisioningService.ACTION_PROVISIONING_ERROR);
    138         filter.addAction(DeviceOwnerProvisioningService.ACTION_PROGRESS_UPDATE);
    139         LocalBroadcastManager.getInstance(this).registerReceiver(mServiceMessageReceiver, filter);
    140 
    141         // Parse the incoming intent.
    142         MessageParser parser = new MessageParser();
    143         try {
    144             mParams = parser.parseIntent(getIntent());
    145         } catch (MessageParser.ParseException e) {
    146             ProvisionLogger.loge("Could not read data from intent", e);
    147             error(e.getErrorMessageId(), false /* no factory reset */);
    148             return;
    149         }
    150 
    151         // Ask to encrypt the device before proceeding
    152         if (!(EncryptDeviceActivity.isDeviceEncrypted()
    153                         || SystemProperties.getBoolean("persist.sys.no_req_encrypt", false))) {
    154             requestEncryption(parser, mParams);
    155             finish();
    156             return;
    157             // System will reboot. Bootreminder will restart this activity.
    158         }
    159 
    160         // Have the user pick a wifi network if necessary.
    161         if (!AddWifiNetworkTask.isConnectedToWifi(this) && TextUtils.isEmpty(mParams.mWifiSsid)) {
    162             requestWifiPick();
    163             return;
    164             // Wait for onActivityResult.
    165         }
    166 
    167         showInterstitialAndProvision(mParams);
    168     }
    169 
    170     private boolean factoryResetProtected() {
    171         // Can't refer to type directly here and API is hidden, so
    172         // get it via reflection.
    173         PersistentDataBlockManager pdbManager = (PersistentDataBlockManager)
    174                 getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
    175         if (pdbManager == null) {
    176             ProvisionLogger.loge("Unable to get persistent data block service");
    177             return false;
    178         }
    179         return pdbManager.getDataBlockSize() > 0;
    180     }
    181 
    182     private void showInterstitialAndProvision(final ProvisioningParams params) {
    183         if (mUserConsented || params.mStartedByNfc) {
    184             startDeviceOwnerProvisioningService(params);
    185         } else {
    186             // Notify the user that the admin will have full control over the device,
    187             // then start provisioning.
    188             UserConsentDialog.newInstance(UserConsentDialog.DEVICE_OWNER)
    189                     .show(getFragmentManager(), "UserConsentDialogFragment");
    190         }
    191     }
    192 
    193     @Override
    194     public void onDialogConsent() {
    195         mUserConsented = true;
    196         startDeviceOwnerProvisioningService(mParams);
    197     }
    198 
    199     @Override
    200     public void onDialogCancel() {
    201         finish();
    202     }
    203 
    204     private void startDeviceOwnerProvisioningService(ProvisioningParams params) {
    205         Intent intent = new Intent(this, DeviceOwnerProvisioningService.class);
    206         intent.putExtra(DeviceOwnerProvisioningService.EXTRA_PROVISIONING_PARAMS, params);
    207         intent.putExtras(getIntent());
    208         startService(intent);
    209     }
    210 
    211     class ServiceMessageReceiver extends BroadcastReceiver
    212     {
    213         @Override
    214         public void onReceive(Context context, Intent intent)
    215         {
    216             if (mCancelDialogShown) {
    217 
    218                 // Postpone handling the intent.
    219                 mPendingProvisioningIntents.add(intent);
    220                 return;
    221             }
    222             handleProvisioningIntent(intent);
    223         }
    224     }
    225 
    226     private void handleProvisioningIntent(Intent intent) {
    227         String action = intent.getAction();
    228         if (action.equals(DeviceOwnerProvisioningService.ACTION_PROVISIONING_SUCCESS)) {
    229             if (DEBUG) ProvisionLogger.logd("Successfully provisioned");
    230             onProvisioningSuccess();
    231         } else if (action.equals(DeviceOwnerProvisioningService.ACTION_PROVISIONING_ERROR)) {
    232             int errorMessageId = intent.getIntExtra(
    233                     DeviceOwnerProvisioningService.EXTRA_USER_VISIBLE_ERROR_ID_KEY,
    234                     R.string.device_owner_error_general);
    235 
    236             if (DEBUG) {
    237                 ProvisionLogger.logd("Error reported with code "
    238                         + getResources().getString(errorMessageId));
    239             }
    240             error(errorMessageId, true /* always factory reset */);
    241         } else if (action.equals(DeviceOwnerProvisioningService.ACTION_PROGRESS_UPDATE)) {
    242             int progressMessage = intent.getIntExtra(
    243                     DeviceOwnerProvisioningService.EXTRA_PROGRESS_MESSAGE_ID_KEY, -1);
    244             if (DEBUG) {
    245                 ProvisionLogger.logd("Progress update reported with code "
    246                     + getResources().getString(progressMessage));
    247             }
    248             if (progressMessage >= 0) {
    249                 progressUpdate(progressMessage);
    250             }
    251         }
    252     }
    253 
    254 
    255     private void onProvisioningSuccess() {
    256         // The Setup wizards listens to this flag and finishes itself when it is set.
    257         // It then fires a home intent, which we catch in the HomeReceiverActivity before sending
    258         // the intent to notify the mdm that provisioning is complete.
    259         Global.putInt(getContentResolver(), Global.DEVICE_PROVISIONED, 1);
    260         Secure.putInt(getContentResolver(), Secure.USER_SETUP_COMPLETE, 1);
    261 
    262         // Note: the DeviceOwnerProvisioningService will stop itself.
    263         setResult(Activity.RESULT_OK);
    264         finish();
    265     }
    266 
    267     private void requestEncryption(MessageParser messageParser, ProvisioningParams params) {
    268         Intent encryptIntent = new Intent(DeviceOwnerProvisioningActivity.this,
    269                 EncryptDeviceActivity.class);
    270 
    271         Bundle resumeExtras = new Bundle();
    272         resumeExtras.putString(EncryptDeviceActivity.EXTRA_RESUME_TARGET,
    273                 EncryptDeviceActivity.TARGET_DEVICE_OWNER);
    274         messageParser.addProvisioningParamsToBundle(resumeExtras, params);
    275 
    276         encryptIntent.putExtra(EncryptDeviceActivity.EXTRA_RESUME, resumeExtras);
    277 
    278         startActivityForResult(encryptIntent, ENCRYPT_DEVICE_REQUEST_CODE);
    279     }
    280 
    281     private void requestWifiPick() {
    282         startActivityForResult(AddWifiNetworkTask.getWifiPickIntent(), WIFI_REQUEST_CODE);
    283     }
    284 
    285     @Override
    286     public void onBackPressed() {
    287         if (mCancelDialogShown) {
    288             return;
    289         }
    290 
    291         mCancelDialogShown = true;
    292         showCancelResetDialog();
    293     }
    294 
    295     private void showCancelResetDialog() {
    296         new AlertDialog.Builder(DeviceOwnerProvisioningActivity.this)
    297                 .setCancelable(false)
    298                 .setTitle(R.string.device_owner_cancel_title)
    299                 .setMessage(R.string.device_owner_cancel_message)
    300                 .setNegativeButton(R.string.device_owner_cancel_cancel,
    301                         new DialogInterface.OnClickListener() {
    302                             @Override
    303                             public void onClick(DialogInterface dialog,int id) {
    304                                 dialog.dismiss();
    305                                 handlePendingIntents();
    306                                 mCancelDialogShown = false;
    307                             }
    308                         })
    309                 .setPositiveButton(R.string.device_owner_error_reset,
    310                         new DialogInterface.OnClickListener() {
    311                             @Override
    312                             public void onClick(DialogInterface dialog,int id) {
    313                                 dialog.dismiss();
    314 
    315                                 // Factory reset the device.
    316                                 Intent intent = new Intent(Intent.ACTION_MASTER_CLEAR);
    317                                 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
    318                                 intent.putExtra(Intent.EXTRA_REASON,
    319                                         "DeviceOwnerProvisioningActivity.showCancelResetDialog()");
    320                                 sendBroadcast(intent);
    321                                 stopService(new Intent(DeviceOwnerProvisioningActivity.this,
    322                                                 DeviceOwnerProvisioningService.class));
    323                                 finish();
    324                             }
    325                         }).show();
    326     }
    327 
    328     private void handlePendingIntents() {
    329         for (Intent intent : mPendingProvisioningIntents) {
    330             if (DEBUG) ProvisionLogger.logd("Handling pending intent " + intent.getAction());
    331             handleProvisioningIntent(intent);
    332         }
    333         mPendingProvisioningIntents.clear();
    334     }
    335 
    336     private void progressUpdate(int progressMessage) {
    337         mProgressTextView.setText(progressMessage);
    338     }
    339 
    340     @Override
    341     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    342         if (requestCode == ENCRYPT_DEVICE_REQUEST_CODE) {
    343             if (resultCode == RESULT_CANCELED) {
    344                 ProvisionLogger.loge("User canceled device encryption.");
    345                 finish();
    346             }
    347         } else if (requestCode == WIFI_REQUEST_CODE) {
    348             if (resultCode == RESULT_CANCELED) {
    349                 ProvisionLogger.loge("User canceled wifi picking.");
    350                 stopService(new Intent(DeviceOwnerProvisioningActivity.this,
    351                                 DeviceOwnerProvisioningService.class));
    352                 finish();
    353             } else if (resultCode == RESULT_OK) {
    354                 if (DEBUG) ProvisionLogger.logd("Wifi request result is OK");
    355                 if (AddWifiNetworkTask.isConnectedToWifi(this)) {
    356                     showInterstitialAndProvision(mParams);
    357                 } else {
    358                     requestWifiPick();
    359                 }
    360             }
    361         }
    362     }
    363 
    364     private void error(int dialogMessage, boolean resetRequired) {
    365         AlertDialog.Builder alertBuilder = new AlertDialog.Builder(this)
    366                 .setTitle(R.string.provisioning_error_title)
    367                 .setMessage(dialogMessage)
    368                 .setCancelable(false);
    369         if (resetRequired) {
    370             alertBuilder.setPositiveButton(R.string.device_owner_error_reset,
    371                     new DialogInterface.OnClickListener() {
    372                         @Override
    373                         public void onClick(DialogInterface dialog,int id) {
    374                             dialog.dismiss();
    375 
    376                             // Factory reset the device.
    377                             Intent intent = new Intent(Intent.ACTION_MASTER_CLEAR);
    378                             intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
    379                             intent.putExtra(Intent.EXTRA_REASON,
    380                                     "DeviceOwnerProvisioningActivity.error()");
    381                             sendBroadcast(intent);
    382                             stopService(new Intent(DeviceOwnerProvisioningActivity.this,
    383                                             DeviceOwnerProvisioningService.class));
    384                             finish();
    385                         }
    386                     });
    387         } else {
    388             alertBuilder.setPositiveButton(R.string.device_owner_error_ok,
    389                     new DialogInterface.OnClickListener() {
    390                         @Override
    391                         public void onClick(DialogInterface dialog,int id) {
    392                             dialog.dismiss();
    393 
    394                             // Close activity.
    395                             stopService(new Intent(DeviceOwnerProvisioningActivity.this,
    396                                             DeviceOwnerProvisioningService.class));
    397                             finish();
    398                         }
    399                     });
    400         }
    401         alertBuilder.show();
    402     }
    403 
    404     @Override
    405     protected void onSaveInstanceState(Bundle outState) {
    406         outState.putBoolean(KEY_USER_CONSENTED, mUserConsented);
    407         outState.putBoolean(KEY_CANCEL_DIALOG_SHOWN, mCancelDialogShown);
    408         outState.putParcelableArrayList(KEY_PENDING_INTENTS, mPendingProvisioningIntents);
    409     }
    410 
    411     @Override
    412     public void onDestroy() {
    413         if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONDESTROY");
    414         if (mServiceMessageReceiver != null) {
    415             LocalBroadcastManager.getInstance(this).unregisterReceiver(mServiceMessageReceiver);
    416             mServiceMessageReceiver = null;
    417         }
    418         super.onDestroy();
    419     }
    420 
    421     @Override
    422     protected void onRestart() {
    423         if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONRESTART");
    424         super.onRestart();
    425     }
    426 
    427     @Override
    428     protected void onResume() {
    429         if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONRESUME");
    430         super.onResume();
    431     }
    432 
    433     @Override
    434     protected void onPause() {
    435         if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONPAUSE");
    436         super.onPause();
    437     }
    438 
    439     @Override
    440     protected void onStop() {
    441         if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONSTOP");
    442         super.onStop();
    443     }
    444 }
    445 
    446