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 static android.app.admin.DeviceAdminReceiver.ACTION_READY_FOR_USER_INITIALIZATION;
     20 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE;
     21 
     22 import android.app.Activity;
     23 import android.app.AlertDialog;
     24 import android.content.BroadcastReceiver;
     25 import android.content.Context;
     26 import android.content.DialogInterface;
     27 import android.content.Intent;
     28 import android.content.IntentFilter;
     29 import android.content.pm.ResolveInfo;
     30 import android.os.Bundle;
     31 import android.support.v4.content.LocalBroadcastManager;
     32 import android.view.accessibility.AccessibilityEvent;
     33 import android.view.View;
     34 import android.widget.TextView;
     35 
     36 import java.util.ArrayList;
     37 import java.util.List;
     38 
     39 /**
     40  * This activity starts device owner provisioning:
     41  * It downloads a mobile device management application(mdm) from a given url and installs it,
     42  * or a given mdm is already present on the device. The mdm is set as the owner of the device so
     43  * that it has full control over the device:
     44  * TODO: put link here with documentation on how a device owner has control over the device
     45  * The mdm can then execute further setup steps.
     46  *
     47  * <p>
     48  * An example use case might be when a company wants to set up a device for a single use case
     49  * (such as giving instructions).
     50  * </p>
     51  *
     52  * <p>
     53  * Provisioning is triggered by a programmer device that sends required provisioning parameters via
     54  * nfc. For an example of a programmer app see:
     55  * com.example.android.apis.app.DeviceProvisioningProgrammerSample.
     56  * </p>
     57  *
     58  * <p>
     59  * In the unlikely case that this activity is killed the whole provisioning process so far is
     60  * repeated. We made sure that all tasks can be done twice without causing any problems.
     61  * </p>
     62  */
     63 public class DeviceOwnerProvisioningActivity extends SetupLayoutActivity {
     64     private static final boolean DEBUG = false; // To control logging.
     65 
     66     private static final String KEY_CANCEL_DIALOG_SHOWN = "cancel_dialog_shown";
     67     private static final String KEY_PENDING_INTENTS = "pending_intents";
     68 
     69     private BroadcastReceiver mServiceMessageReceiver;
     70     private TextView mProgressTextView;
     71 
     72     private ProvisioningParams mParams;
     73 
     74     // Indicates that the cancel dialog is shown.
     75     private boolean mCancelDialogShown = false;
     76 
     77     // List of intents received while cancel dialog is shown.
     78     private ArrayList<Intent> mPendingProvisioningIntents = new ArrayList<Intent>();
     79 
     80     @Override
     81     public void onCreate(Bundle savedInstanceState) {
     82         super.onCreate(savedInstanceState);
     83         if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONCREATE");
     84 
     85         if (savedInstanceState != null) {
     86             mCancelDialogShown = savedInstanceState.getBoolean(KEY_CANCEL_DIALOG_SHOWN, false);
     87             mPendingProvisioningIntents = savedInstanceState
     88                     .getParcelableArrayList(KEY_PENDING_INTENTS);
     89         }
     90 
     91         // Setup the UI.
     92         initializeLayoutParams(R.layout.progress, R.string.setup_work_device, true);
     93         configureNavigationButtons(NEXT_BUTTON_EMPTY_LABEL, View.INVISIBLE, View.VISIBLE);
     94         setTitle(R.string.setup_device_progress);
     95 
     96         mProgressTextView = (TextView) findViewById(R.id.prog_text);
     97         if (mCancelDialogShown) showCancelResetDialog();
     98 
     99         // Setup broadcast receiver for feedback from service.
    100         mServiceMessageReceiver = new ServiceMessageReceiver();
    101         IntentFilter filter = new IntentFilter();
    102         filter.addAction(DeviceOwnerProvisioningService.ACTION_PROVISIONING_SUCCESS);
    103         filter.addAction(DeviceOwnerProvisioningService.ACTION_PROVISIONING_ERROR);
    104         filter.addAction(DeviceOwnerProvisioningService.ACTION_PROGRESS_UPDATE);
    105         LocalBroadcastManager.getInstance(this).registerReceiver(mServiceMessageReceiver, filter);
    106 
    107         // Load the ProvisioningParams (from message in Intent).
    108         mParams = (ProvisioningParams) getIntent().getParcelableExtra(
    109                 ProvisioningParams.EXTRA_PROVISIONING_PARAMS);
    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         if (mParams.deviceInitializerComponentName != null) {
    168             Intent result = new Intent(ACTION_READY_FOR_USER_INITIALIZATION);
    169             result.setComponent(mParams.deviceInitializerComponentName);
    170             result.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES |
    171                     Intent.FLAG_RECEIVER_FOREGROUND);
    172             if (mParams.adminExtrasBundle != null) {
    173                 result.putExtra(EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE,
    174                         mParams.adminExtrasBundle);
    175             }
    176             List<ResolveInfo> matchingReceivers =
    177                     getPackageManager().queryBroadcastReceivers(result, 0);
    178             if (matchingReceivers.size() > 0) {
    179                 // Notify the device initializer that it can now perform pre-user-setup tasks.
    180                 sendBroadcast(result);
    181             } else {
    182                 ProvisionLogger.logi("Initializer component doesn't have a receiver for "
    183                         + "android.app.action.READY_FOR_USER_INITIALIZATION. Skipping broadcast "
    184                         + "and finishing user initialization.");
    185                 Utils.markDeviceProvisioned(DeviceOwnerProvisioningActivity.this);
    186             }
    187         } else {
    188             // No initializer, set the device provisioned ourselves.
    189             Utils.markDeviceProvisioned(DeviceOwnerProvisioningActivity.this);
    190         }
    191         stopService(new Intent(this, DeviceOwnerProvisioningService.class));
    192         // Note: the DeviceOwnerProvisioningService will stop itself.
    193         setResult(Activity.RESULT_OK);
    194         finish();
    195     }
    196 
    197     @Override
    198     public void onBackPressed() {
    199         if (mCancelDialogShown) {
    200             return;
    201         }
    202 
    203         mCancelDialogShown = true;
    204         showCancelResetDialog();
    205     }
    206 
    207     private void showCancelResetDialog() {
    208         new AlertDialog.Builder(DeviceOwnerProvisioningActivity.this)
    209                 .setCancelable(false)
    210                 .setMessage(R.string.device_owner_cancel_message)
    211                 .setNegativeButton(R.string.device_owner_cancel_cancel,
    212                         new DialogInterface.OnClickListener() {
    213                             @Override
    214                             public void onClick(DialogInterface dialog,int id) {
    215                                 dialog.dismiss();
    216                                 handlePendingIntents();
    217                                 mCancelDialogShown = false;
    218                             }
    219                         })
    220                 .setPositiveButton(R.string.device_owner_error_reset,
    221                         new DialogInterface.OnClickListener() {
    222                             @Override
    223                             public void onClick(DialogInterface dialog,int id) {
    224                                 dialog.dismiss();
    225 
    226                                 // Factory reset the device.
    227                                 Intent intent = new Intent(Intent.ACTION_MASTER_CLEAR);
    228                                 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
    229                                 intent.putExtra(Intent.EXTRA_REASON,
    230                                         "DeviceOwnerProvisioningActivity.showCancelResetDialog()");
    231                                 sendBroadcast(intent);
    232                                 stopService(new Intent(DeviceOwnerProvisioningActivity.this,
    233                                         DeviceOwnerProvisioningService.class));
    234                                 setResult(RESULT_CANCELED);
    235                                 finish();
    236                             }
    237                         })
    238                 .show();
    239     }
    240 
    241     private void handlePendingIntents() {
    242         for (Intent intent : mPendingProvisioningIntents) {
    243             if (DEBUG) ProvisionLogger.logd("Handling pending intent " + intent.getAction());
    244             handleProvisioningIntent(intent);
    245         }
    246         mPendingProvisioningIntents.clear();
    247     }
    248 
    249     private void progressUpdate(int progressMessage) {
    250         mProgressTextView.setText(progressMessage);
    251         mProgressTextView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
    252     }
    253 
    254     private void error(int dialogMessage, boolean resetRequired) {
    255         AlertDialog.Builder alertBuilder = new AlertDialog.Builder(this)
    256                 .setTitle(R.string.provisioning_error_title)
    257                 .setMessage(dialogMessage)
    258                 .setCancelable(false);
    259         if (resetRequired) {
    260             alertBuilder.setPositiveButton(R.string.device_owner_error_reset,
    261                     new DialogInterface.OnClickListener() {
    262                         @Override
    263                         public void onClick(DialogInterface dialog,int id) {
    264                             dialog.dismiss();
    265 
    266                             // Factory reset the device.
    267                             Intent intent = new Intent(Intent.ACTION_MASTER_CLEAR);
    268                             intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
    269                             intent.putExtra(Intent.EXTRA_REASON,
    270                                     "DeviceOwnerProvisioningActivity.error()");
    271                             sendBroadcast(intent);
    272                             stopService(new Intent(DeviceOwnerProvisioningActivity.this,
    273                                             DeviceOwnerProvisioningService.class));
    274                             setResult(RESULT_CANCELED);
    275                             finish();
    276                         }
    277                     });
    278         } else {
    279             alertBuilder.setPositiveButton(R.string.device_owner_error_ok,
    280                     new DialogInterface.OnClickListener() {
    281                         @Override
    282                         public void onClick(DialogInterface dialog,int id) {
    283                             dialog.dismiss();
    284 
    285                             // Close activity.
    286                             stopService(new Intent(DeviceOwnerProvisioningActivity.this,
    287                                             DeviceOwnerProvisioningService.class));
    288                             setResult(RESULT_CANCELED);
    289                             finish();
    290                         }
    291                     });
    292         }
    293         alertBuilder.show();
    294     }
    295 
    296     @Override
    297     protected void onSaveInstanceState(Bundle outState) {
    298         outState.putBoolean(KEY_CANCEL_DIALOG_SHOWN, mCancelDialogShown);
    299         outState.putParcelableArrayList(KEY_PENDING_INTENTS, mPendingProvisioningIntents);
    300     }
    301 
    302     @Override
    303     public void onDestroy() {
    304         if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONDESTROY");
    305         if (mServiceMessageReceiver != null) {
    306             LocalBroadcastManager.getInstance(this).unregisterReceiver(mServiceMessageReceiver);
    307             mServiceMessageReceiver = null;
    308         }
    309         super.onDestroy();
    310     }
    311 
    312     @Override
    313     protected void onRestart() {
    314         if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONRESTART");
    315         super.onRestart();
    316     }
    317 
    318     @Override
    319     protected void onResume() {
    320         if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONRESUME");
    321         super.onResume();
    322     }
    323 
    324     @Override
    325     protected void onPause() {
    326         if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONPAUSE");
    327         super.onPause();
    328     }
    329 
    330     @Override
    331     protected void onStop() {
    332         if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONSTOP");
    333         super.onStop();
    334     }
    335 }
    336