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.support.v4.content.LocalBroadcastManager;
     29 import android.view.accessibility.AccessibilityEvent;
     30 import android.view.View;
     31 import android.widget.TextView;
     32 
     33 import com.android.managedprovisioning.model.ProvisioningParams;
     34 
     35 import java.util.ArrayList;
     36 
     37 /**
     38  * This activity starts device owner provisioning:
     39  * It downloads a mobile device management application(mdm) from a given url and installs it,
     40  * or a given mdm is already present on the device. The mdm is set as the owner of the device so
     41  * that it has full control over the device:
     42  * TODO: put link here with documentation on how a device owner has control over the device
     43  * The mdm can then execute further setup steps.
     44  *
     45  * <p>
     46  * An example use case might be when a company wants to set up a device for a single use case
     47  * (such as giving instructions).
     48  * </p>
     49  *
     50  * <p>
     51  * Provisioning is triggered by a programmer device that sends required provisioning parameters via
     52  * nfc. For an example of a programmer app see:
     53  * com.example.android.apis.app.DeviceProvisioningProgrammerSample.
     54  * </p>
     55  *
     56  * <p>
     57  * In the unlikely case that this activity is killed the whole provisioning process so far is
     58  * repeated. We made sure that all tasks can be done twice without causing any problems.
     59  * </p>
     60  */
     61 public class DeviceOwnerProvisioningActivity extends SetupLayoutActivity {
     62     private static final boolean DEBUG = false; // To control logging.
     63 
     64     private static final String KEY_CANCEL_DIALOG_SHOWN = "cancel_dialog_shown";
     65     private static final String KEY_PENDING_INTENTS = "pending_intents";
     66 
     67     private BroadcastReceiver mServiceMessageReceiver;
     68     private TextView mProgressTextView;
     69 
     70     private ProvisioningParams mParams;
     71 
     72     // Indicates that the cancel dialog is shown.
     73     private boolean mCancelDialogShown = false;
     74 
     75     // List of intents received while cancel dialog is shown.
     76     private ArrayList<Intent> mPendingProvisioningIntents = new ArrayList<Intent>();
     77 
     78     @Override
     79     public void onCreate(Bundle savedInstanceState) {
     80         super.onCreate(savedInstanceState);
     81         if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONCREATE");
     82 
     83         if (savedInstanceState != null) {
     84             mCancelDialogShown = savedInstanceState.getBoolean(KEY_CANCEL_DIALOG_SHOWN, false);
     85             mPendingProvisioningIntents = savedInstanceState
     86                     .getParcelableArrayList(KEY_PENDING_INTENTS);
     87         }
     88 
     89         // Setup the UI.
     90         initializeLayoutParams(R.layout.progress, R.string.setup_work_device, true);
     91         setTitle(R.string.setup_device_progress);
     92 
     93         mProgressTextView = (TextView) findViewById(R.id.prog_text);
     94         if (mCancelDialogShown) showCancelResetDialog();
     95 
     96         // Setup broadcast receiver for feedback from service.
     97         mServiceMessageReceiver = new ServiceMessageReceiver();
     98         IntentFilter filter = new IntentFilter();
     99         filter.addAction(DeviceOwnerProvisioningService.ACTION_PROVISIONING_SUCCESS);
    100         filter.addAction(DeviceOwnerProvisioningService.ACTION_PROVISIONING_ERROR);
    101         filter.addAction(DeviceOwnerProvisioningService.ACTION_PROGRESS_UPDATE);
    102         LocalBroadcastManager.getInstance(this).registerReceiver(mServiceMessageReceiver, filter);
    103 
    104         // Load the ProvisioningParams (from message in Intent).
    105         mParams = (ProvisioningParams) getIntent().getParcelableExtra(
    106                 ProvisioningParams.EXTRA_PROVISIONING_PARAMS);
    107         if (mParams != null) {
    108             maybeSetLogoAndMainColor(mParams.mainColor);
    109         }
    110         startDeviceOwnerProvisioningService();
    111     }
    112 
    113     private void startDeviceOwnerProvisioningService() {
    114         Intent intent = new Intent(this, DeviceOwnerProvisioningService.class);
    115         intent.putExtras(getIntent());
    116         startService(intent);
    117     }
    118 
    119     class ServiceMessageReceiver extends BroadcastReceiver
    120     {
    121         @Override
    122         public void onReceive(Context context, Intent intent)
    123         {
    124             if (mCancelDialogShown) {
    125 
    126                 // Postpone handling the intent.
    127                 mPendingProvisioningIntents.add(intent);
    128                 return;
    129             }
    130             handleProvisioningIntent(intent);
    131         }
    132     }
    133 
    134     private void handleProvisioningIntent(Intent intent) {
    135         String action = intent.getAction();
    136         if (action.equals(DeviceOwnerProvisioningService.ACTION_PROVISIONING_SUCCESS)) {
    137             if (DEBUG) ProvisionLogger.logd("Successfully provisioned");
    138             onProvisioningSuccess();
    139         } else if (action.equals(DeviceOwnerProvisioningService.ACTION_PROVISIONING_ERROR)) {
    140             int errorMessageId = intent.getIntExtra(
    141                     DeviceOwnerProvisioningService.EXTRA_USER_VISIBLE_ERROR_ID_KEY,
    142                     R.string.device_owner_error_general);
    143             boolean factoryResetRequired = intent.getBooleanExtra(
    144                     DeviceOwnerProvisioningService.EXTRA_FACTORY_RESET_REQUIRED,
    145                     true);
    146 
    147             if (DEBUG) {
    148                 ProvisionLogger.logd("Error reported with code "
    149                         + getResources().getString(errorMessageId));
    150             }
    151             error(errorMessageId, factoryResetRequired);
    152         } else if (action.equals(DeviceOwnerProvisioningService.ACTION_PROGRESS_UPDATE)) {
    153             int progressMessage = intent.getIntExtra(
    154                     DeviceOwnerProvisioningService.EXTRA_PROGRESS_MESSAGE_ID_KEY, -1);
    155             if (DEBUG) {
    156                 ProvisionLogger.logd("Progress update reported with code "
    157                     + getResources().getString(progressMessage));
    158             }
    159             if (progressMessage >= 0) {
    160                 progressUpdate(progressMessage);
    161             }
    162         }
    163     }
    164 
    165 
    166     private void onProvisioningSuccess() {
    167         stopService(new Intent(this, DeviceOwnerProvisioningService.class));
    168         // Note: the DeviceOwnerProvisioningService will stop itself.
    169         setResult(Activity.RESULT_OK);
    170         finish();
    171     }
    172 
    173     @Override
    174     public void onBackPressed() {
    175         if (mCancelDialogShown) {
    176             return;
    177         }
    178 
    179         mCancelDialogShown = true;
    180         showCancelResetDialog();
    181     }
    182 
    183     private void showCancelResetDialog() {
    184         new AlertDialog.Builder(DeviceOwnerProvisioningActivity.this)
    185                 .setCancelable(false)
    186                 .setMessage(R.string.device_owner_cancel_message)
    187                 .setNegativeButton(R.string.device_owner_cancel_cancel,
    188                         new DialogInterface.OnClickListener() {
    189                             @Override
    190                             public void onClick(DialogInterface dialog, int id) {
    191                                 dialog.dismiss();
    192                                 handlePendingIntents();
    193                                 mCancelDialogShown = false;
    194                             }
    195                         })
    196                 .setPositiveButton(R.string.device_owner_error_reset,
    197                         new DialogInterface.OnClickListener() {
    198                             @Override
    199                             public void onClick(DialogInterface dialog, int id) {
    200                                 dialog.dismiss();
    201 
    202                                 // Factory reset the device.
    203                                 mUtils.sendFactoryResetBroadcast(
    204                                         DeviceOwnerProvisioningActivity.this,
    205                                         "DeviceOwnerProvisioningActivity.showCancelResetDialog()");
    206                             }
    207                         })
    208                 .show();
    209     }
    210 
    211     private void handlePendingIntents() {
    212         for (Intent intent : mPendingProvisioningIntents) {
    213             if (DEBUG) ProvisionLogger.logd("Handling pending intent " + intent.getAction());
    214             handleProvisioningIntent(intent);
    215         }
    216         mPendingProvisioningIntents.clear();
    217     }
    218 
    219     private void progressUpdate(int progressMessage) {
    220         mProgressTextView.setText(progressMessage);
    221         mProgressTextView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
    222     }
    223 
    224     private void error(int dialogMessage, boolean resetRequired) {
    225         AlertDialog.Builder alertBuilder = new AlertDialog.Builder(this)
    226                 .setTitle(R.string.provisioning_error_title)
    227                 .setMessage(dialogMessage)
    228                 .setCancelable(false);
    229         if (resetRequired) {
    230             alertBuilder.setPositiveButton(R.string.device_owner_error_reset,
    231                     new DialogInterface.OnClickListener() {
    232                         @Override
    233                         public void onClick(DialogInterface dialog,int id) {
    234                             dialog.dismiss();
    235 
    236                             // Factory reset the device.
    237                             Intent intent = new Intent(Intent.ACTION_MASTER_CLEAR);
    238                             intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
    239                             intent.putExtra(Intent.EXTRA_REASON,
    240                                     "DeviceOwnerProvisioningActivity.error()");
    241                             sendBroadcast(intent);
    242                             stopService(new Intent(DeviceOwnerProvisioningActivity.this,
    243                                             DeviceOwnerProvisioningService.class));
    244                             setResult(RESULT_CANCELED);
    245                             finish();
    246                         }
    247                     });
    248         } else {
    249             alertBuilder.setPositiveButton(R.string.device_owner_error_ok,
    250                     new DialogInterface.OnClickListener() {
    251                         @Override
    252                         public void onClick(DialogInterface dialog,int id) {
    253                             dialog.dismiss();
    254 
    255                             // Close activity.
    256                             stopService(new Intent(DeviceOwnerProvisioningActivity.this,
    257                                             DeviceOwnerProvisioningService.class));
    258                             setResult(RESULT_CANCELED);
    259                             finish();
    260                         }
    261                     });
    262         }
    263         alertBuilder.show();
    264     }
    265 
    266     @Override
    267     protected void onSaveInstanceState(Bundle outState) {
    268         outState.putBoolean(KEY_CANCEL_DIALOG_SHOWN, mCancelDialogShown);
    269         outState.putParcelableArrayList(KEY_PENDING_INTENTS, mPendingProvisioningIntents);
    270     }
    271 
    272     @Override
    273     public void onDestroy() {
    274         if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONDESTROY");
    275         if (mServiceMessageReceiver != null) {
    276             LocalBroadcastManager.getInstance(this).unregisterReceiver(mServiceMessageReceiver);
    277             mServiceMessageReceiver = null;
    278         }
    279         super.onDestroy();
    280     }
    281 
    282     @Override
    283     protected void onRestart() {
    284         if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONRESTART");
    285         super.onRestart();
    286     }
    287 
    288     @Override
    289     protected void onResume() {
    290         if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONRESUME");
    291         super.onResume();
    292     }
    293 
    294     @Override
    295     protected void onPause() {
    296         if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONPAUSE");
    297         super.onPause();
    298     }
    299 
    300     @Override
    301     protected void onStop() {
    302         if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONSTOP");
    303         super.onStop();
    304     }
    305 }
    306