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         configureNavigationButtons(NEXT_BUTTON_EMPTY_LABEL, View.INVISIBLE, View.VISIBLE);
     92         setTitle(R.string.setup_device_progress);
     93 
     94         mProgressTextView = (TextView) findViewById(R.id.prog_text);
     95         if (mCancelDialogShown) showCancelResetDialog();
     96 
     97         // Setup broadcast receiver for feedback from service.
     98         mServiceMessageReceiver = new ServiceMessageReceiver();
     99         IntentFilter filter = new IntentFilter();
    100         filter.addAction(DeviceOwnerProvisioningService.ACTION_PROVISIONING_SUCCESS);
    101         filter.addAction(DeviceOwnerProvisioningService.ACTION_PROVISIONING_ERROR);
    102         filter.addAction(DeviceOwnerProvisioningService.ACTION_PROGRESS_UPDATE);
    103         LocalBroadcastManager.getInstance(this).registerReceiver(mServiceMessageReceiver, filter);
    104 
    105         // Load the ProvisioningParams (from message in Intent).
    106         mParams = (ProvisioningParams) getIntent().getParcelableExtra(
    107                 ProvisioningParams.EXTRA_PROVISIONING_PARAMS);
    108         if (mParams != null) {
    109             maybeSetLogoAndMainColor(mParams.mainColor);
    110         }
    111         startDeviceOwnerProvisioningService();
    112     }
    113 
    114     private void startDeviceOwnerProvisioningService() {
    115         Intent intent = new Intent(this, DeviceOwnerProvisioningService.class);
    116         intent.putExtras(getIntent());
    117         startService(intent);
    118     }
    119 
    120     class ServiceMessageReceiver extends BroadcastReceiver
    121     {
    122         @Override
    123         public void onReceive(Context context, Intent intent)
    124         {
    125             if (mCancelDialogShown) {
    126 
    127                 // Postpone handling the intent.
    128                 mPendingProvisioningIntents.add(intent);
    129                 return;
    130             }
    131             handleProvisioningIntent(intent);
    132         }
    133     }
    134 
    135     private void handleProvisioningIntent(Intent intent) {
    136         String action = intent.getAction();
    137         if (action.equals(DeviceOwnerProvisioningService.ACTION_PROVISIONING_SUCCESS)) {
    138             if (DEBUG) ProvisionLogger.logd("Successfully provisioned");
    139             onProvisioningSuccess();
    140         } else if (action.equals(DeviceOwnerProvisioningService.ACTION_PROVISIONING_ERROR)) {
    141             int errorMessageId = intent.getIntExtra(
    142                     DeviceOwnerProvisioningService.EXTRA_USER_VISIBLE_ERROR_ID_KEY,
    143                     R.string.device_owner_error_general);
    144             boolean factoryResetRequired = intent.getBooleanExtra(
    145                     DeviceOwnerProvisioningService.EXTRA_FACTORY_RESET_REQUIRED,
    146                     true);
    147 
    148             if (DEBUG) {
    149                 ProvisionLogger.logd("Error reported with code "
    150                         + getResources().getString(errorMessageId));
    151             }
    152             error(errorMessageId, factoryResetRequired);
    153         } else if (action.equals(DeviceOwnerProvisioningService.ACTION_PROGRESS_UPDATE)) {
    154             int progressMessage = intent.getIntExtra(
    155                     DeviceOwnerProvisioningService.EXTRA_PROGRESS_MESSAGE_ID_KEY, -1);
    156             if (DEBUG) {
    157                 ProvisionLogger.logd("Progress update reported with code "
    158                     + getResources().getString(progressMessage));
    159             }
    160             if (progressMessage >= 0) {
    161                 progressUpdate(progressMessage);
    162             }
    163         }
    164     }
    165 
    166 
    167     private void onProvisioningSuccess() {
    168         stopService(new Intent(this, DeviceOwnerProvisioningService.class));
    169         // Note: the DeviceOwnerProvisioningService will stop itself.
    170         setResult(Activity.RESULT_OK);
    171         finish();
    172     }
    173 
    174     @Override
    175     public void onBackPressed() {
    176         if (mCancelDialogShown) {
    177             return;
    178         }
    179 
    180         mCancelDialogShown = true;
    181         showCancelResetDialog();
    182     }
    183 
    184     private void showCancelResetDialog() {
    185         new AlertDialog.Builder(DeviceOwnerProvisioningActivity.this)
    186                 .setCancelable(false)
    187                 .setMessage(R.string.device_owner_cancel_message)
    188                 .setNegativeButton(R.string.device_owner_cancel_cancel,
    189                         new DialogInterface.OnClickListener() {
    190                             @Override
    191                             public void onClick(DialogInterface dialog, int id) {
    192                                 dialog.dismiss();
    193                                 handlePendingIntents();
    194                                 mCancelDialogShown = false;
    195                             }
    196                         })
    197                 .setPositiveButton(R.string.device_owner_error_reset,
    198                         new DialogInterface.OnClickListener() {
    199                             @Override
    200                             public void onClick(DialogInterface dialog, int id) {
    201                                 dialog.dismiss();
    202 
    203                                 // Factory reset the device.
    204                                 mUtils.sendFactoryResetBroadcast(
    205                                         DeviceOwnerProvisioningActivity.this,
    206                                         "DeviceOwnerProvisioningActivity.showCancelResetDialog()");
    207                             }
    208                         })
    209                 .show();
    210     }
    211 
    212     private void handlePendingIntents() {
    213         for (Intent intent : mPendingProvisioningIntents) {
    214             if (DEBUG) ProvisionLogger.logd("Handling pending intent " + intent.getAction());
    215             handleProvisioningIntent(intent);
    216         }
    217         mPendingProvisioningIntents.clear();
    218     }
    219 
    220     private void progressUpdate(int progressMessage) {
    221         mProgressTextView.setText(progressMessage);
    222         mProgressTextView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
    223     }
    224 
    225     private void error(int dialogMessage, boolean resetRequired) {
    226         AlertDialog.Builder alertBuilder = new AlertDialog.Builder(this)
    227                 .setTitle(R.string.provisioning_error_title)
    228                 .setMessage(dialogMessage)
    229                 .setCancelable(false);
    230         if (resetRequired) {
    231             alertBuilder.setPositiveButton(R.string.device_owner_error_reset,
    232                     new DialogInterface.OnClickListener() {
    233                         @Override
    234                         public void onClick(DialogInterface dialog,int id) {
    235                             dialog.dismiss();
    236 
    237                             // Factory reset the device.
    238                             Intent intent = new Intent(Intent.ACTION_MASTER_CLEAR);
    239                             intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
    240                             intent.putExtra(Intent.EXTRA_REASON,
    241                                     "DeviceOwnerProvisioningActivity.error()");
    242                             sendBroadcast(intent);
    243                             stopService(new Intent(DeviceOwnerProvisioningActivity.this,
    244                                             DeviceOwnerProvisioningService.class));
    245                             setResult(RESULT_CANCELED);
    246                             finish();
    247                         }
    248                     });
    249         } else {
    250             alertBuilder.setPositiveButton(R.string.device_owner_error_ok,
    251                     new DialogInterface.OnClickListener() {
    252                         @Override
    253                         public void onClick(DialogInterface dialog,int id) {
    254                             dialog.dismiss();
    255 
    256                             // Close activity.
    257                             stopService(new Intent(DeviceOwnerProvisioningActivity.this,
    258                                             DeviceOwnerProvisioningService.class));
    259                             setResult(RESULT_CANCELED);
    260                             finish();
    261                         }
    262                     });
    263         }
    264         alertBuilder.show();
    265     }
    266 
    267     @Override
    268     protected void onSaveInstanceState(Bundle outState) {
    269         outState.putBoolean(KEY_CANCEL_DIALOG_SHOWN, mCancelDialogShown);
    270         outState.putParcelableArrayList(KEY_PENDING_INTENTS, mPendingProvisioningIntents);
    271     }
    272 
    273     @Override
    274     public void onDestroy() {
    275         if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONDESTROY");
    276         if (mServiceMessageReceiver != null) {
    277             LocalBroadcastManager.getInstance(this).unregisterReceiver(mServiceMessageReceiver);
    278             mServiceMessageReceiver = null;
    279         }
    280         super.onDestroy();
    281     }
    282 
    283     @Override
    284     protected void onRestart() {
    285         if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONRESTART");
    286         super.onRestart();
    287     }
    288 
    289     @Override
    290     protected void onResume() {
    291         if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONRESUME");
    292         super.onResume();
    293     }
    294 
    295     @Override
    296     protected void onPause() {
    297         if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONPAUSE");
    298         super.onPause();
    299     }
    300 
    301     @Override
    302     protected void onStop() {
    303         if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONSTOP");
    304         super.onStop();
    305     }
    306 }
    307