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.DevicePolicyManager.EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE;
     20 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME;
     21 
     22 import android.accounts.Account;
     23 import android.accounts.AccountManager;
     24 import android.accounts.AccountManagerFuture;
     25 import android.accounts.AuthenticatorException;
     26 import android.accounts.OperationCanceledException;
     27 import android.app.Activity;
     28 import android.app.AlertDialog;
     29 import android.app.ProgressDialog;
     30 import android.app.admin.DevicePolicyManager;
     31 import android.content.BroadcastReceiver;
     32 import android.content.Context;
     33 import android.content.DialogInterface;
     34 import android.content.Intent;
     35 import android.content.IntentFilter;
     36 import android.os.AsyncTask;
     37 import android.os.Bundle;
     38 import android.os.ConditionVariable;
     39 import android.os.Handler;
     40 import android.os.UserHandle;
     41 import android.os.UserManager;
     42 import android.provider.Settings;
     43 import android.support.v4.content.LocalBroadcastManager;
     44 import android.view.View;
     45 import android.widget.TextView;
     46 
     47 import java.io.IOException;
     48 import java.util.concurrent.ExecutionException;
     49 
     50 /**
     51  * Profile owner provisioning sets up a separate profile on a device whose primary user is already
     52  * set up or being set up.
     53  *
     54  * <p>
     55  * The typical example is setting up a corporate profile that is controlled by their employer on a
     56  * users personal device to keep personal and work data separate.
     57  *
     58  * <p>
     59  * The activity handles the UI for managed profile provisioning and starts the
     60  * {@link ProfileOwnerProvisioningService}, which runs through the setup steps in an
     61  * async task.
     62  */
     63 public class ProfileOwnerProvisioningActivity extends SetupLayoutActivity {
     64     protected static final String ACTION_CANCEL_PROVISIONING =
     65             "com.android.managedprovisioning.CANCEL_PROVISIONING";
     66 
     67     private BroadcastReceiver mServiceMessageReceiver;
     68 
     69     private static final int BROADCAST_TIMEOUT = 2 * 60 * 1000;
     70 
     71     // Provisioning service started
     72     private static final int CANCELSTATUS_PROVISIONING = 1;
     73     // Back button pressed during provisioning, confirm dialog showing.
     74     private static final int CANCELSTATUS_CONFIRMING = 2;
     75     // Cancel confirmed, waiting for the provisioning service to complete.
     76     private static final int CANCELSTATUS_CANCELLING = 3;
     77     // Cancelling not possible anymore, provisioning already finished successfully.
     78     private static final int CANCELSTATUS_FINALIZING = 4;
     79 
     80     private static final String KEY_CANCELSTATUS= "cancelstatus";
     81     private static final String KEY_PENDING_INTENT = "pending_intent";
     82 
     83     private int mCancelStatus = CANCELSTATUS_PROVISIONING;
     84     private Intent mPendingProvisioningResult = null;
     85     private ProgressDialog mCancelProgressDialog = null;
     86     private AccountManager mAccountManager;
     87 
     88     @Override
     89     protected void onCreate(Bundle savedInstanceState) {
     90         super.onCreate(savedInstanceState);
     91         ProvisionLogger.logd("Profile owner provisioning activity ONCREATE");
     92         mAccountManager = (AccountManager) getSystemService(Context.ACCOUNT_SERVICE);
     93 
     94         if (savedInstanceState != null) {
     95             mCancelStatus = savedInstanceState.getInt(KEY_CANCELSTATUS, CANCELSTATUS_PROVISIONING);
     96             mPendingProvisioningResult = savedInstanceState.getParcelable(KEY_PENDING_INTENT);
     97         }
     98 
     99         initializeLayoutParams(R.layout.progress, R.string.setup_work_profile, true);
    100         configureNavigationButtons(NEXT_BUTTON_EMPTY_LABEL, View.INVISIBLE, View.VISIBLE);
    101         setTitle(R.string.setup_profile_progress);
    102 
    103         TextView textView = (TextView) findViewById(R.id.prog_text);
    104         if (textView != null) textView.setText(R.string.setting_up_workspace);
    105 
    106         if (mCancelStatus == CANCELSTATUS_CONFIRMING) {
    107             showCancelProvisioningDialog();
    108         } else if (mCancelStatus == CANCELSTATUS_CANCELLING) {
    109             showCancelProgressDialog();
    110         }
    111     }
    112 
    113     @Override
    114     protected void onResume() {
    115         super.onResume();
    116 
    117         // Setup broadcast receiver for feedback from service.
    118         mServiceMessageReceiver = new ServiceMessageReceiver();
    119         IntentFilter filter = new IntentFilter();
    120         filter.addAction(ProfileOwnerProvisioningService.ACTION_PROVISIONING_SUCCESS);
    121         filter.addAction(ProfileOwnerProvisioningService.ACTION_PROVISIONING_ERROR);
    122         filter.addAction(ProfileOwnerProvisioningService.ACTION_PROVISIONING_CANCELLED);
    123         LocalBroadcastManager.getInstance(this).registerReceiver(mServiceMessageReceiver, filter);
    124 
    125         // Start service async to make sure the UI is loaded first.
    126         final Handler handler = new Handler(getMainLooper());
    127         handler.post(new Runnable() {
    128             @Override
    129             public void run() {
    130                 Intent intent = new Intent(ProfileOwnerProvisioningActivity.this,
    131                         ProfileOwnerProvisioningService.class);
    132                 intent.putExtras(getIntent());
    133                 startService(intent);
    134             }
    135         });
    136     }
    137 
    138     class ServiceMessageReceiver extends BroadcastReceiver {
    139         @Override
    140         public void onReceive(Context context, Intent intent) {
    141             if (mCancelStatus == CANCELSTATUS_CONFIRMING) {
    142                 // Store the incoming intent and only process it after the user has responded to
    143                 // the cancel dialog
    144                 mPendingProvisioningResult = intent;
    145                 return;
    146             }
    147             handleProvisioningResult(intent);
    148         }
    149     }
    150 
    151     private void handleProvisioningResult(Intent intent) {
    152         String action = intent.getAction();
    153         if (ProfileOwnerProvisioningService.ACTION_PROVISIONING_SUCCESS.equals(action)) {
    154             if (mCancelStatus == CANCELSTATUS_CANCELLING) {
    155                 return;
    156             }
    157 
    158             ProvisionLogger.logd("Successfully provisioned."
    159                     + "Finishing ProfileOwnerProvisioningActivity");
    160 
    161             onProvisioningSuccess();
    162         } else if (ProfileOwnerProvisioningService.ACTION_PROVISIONING_ERROR.equals(action)) {
    163             if (mCancelStatus == CANCELSTATUS_CANCELLING){
    164                 return;
    165             }
    166             String errorLogMessage = intent.getStringExtra(
    167                     ProfileOwnerProvisioningService.EXTRA_LOG_MESSAGE_KEY);
    168             ProvisionLogger.logd("Error reported: " + errorLogMessage);
    169             error(R.string.managed_provisioning_error_text, errorLogMessage);
    170             // Note that this will be reported as a canceled action
    171             mCancelStatus = CANCELSTATUS_FINALIZING;
    172         } else if (ProfileOwnerProvisioningService.ACTION_PROVISIONING_CANCELLED.equals(action)) {
    173             if (mCancelStatus != CANCELSTATUS_CANCELLING) {
    174                 return;
    175             }
    176             mCancelProgressDialog.dismiss();
    177             onProvisioningAborted();
    178         }
    179     }
    180 
    181     private void onProvisioningAborted() {
    182         stopService(new Intent(this, ProfileOwnerProvisioningService.class));
    183         setResult(Activity.RESULT_CANCELED);
    184         finish();
    185     }
    186 
    187     @Override
    188     public void onBackPressed() {
    189         if (mCancelStatus != CANCELSTATUS_PROVISIONING) {
    190             mCancelStatus = CANCELSTATUS_CONFIRMING;
    191             showCancelProvisioningDialog();
    192         }
    193     }
    194 
    195     private void showCancelProvisioningDialog() {
    196         AlertDialog alertDialog = new AlertDialog.Builder(this)
    197                 .setCancelable(false)
    198                 .setMessage(R.string.profile_owner_cancel_message)
    199                 .setNegativeButton(R.string.profile_owner_cancel_cancel,
    200                         new DialogInterface.OnClickListener() {
    201                             @Override
    202                             public void onClick(DialogInterface dialog,int id) {
    203                                 mCancelStatus = CANCELSTATUS_PROVISIONING;
    204                                 if (mPendingProvisioningResult != null) {
    205                                     handleProvisioningResult(mPendingProvisioningResult);
    206                                 }
    207                             }
    208                         })
    209                 .setPositiveButton(R.string.profile_owner_cancel_ok,
    210                         new DialogInterface.OnClickListener() {
    211                             @Override
    212                             public void onClick(DialogInterface dialog,int id) {
    213                                 confirmCancel();
    214                             }
    215                         })
    216                 .create();
    217         alertDialog.show();
    218     }
    219 
    220     protected void showCancelProgressDialog() {
    221         mCancelProgressDialog = new ProgressDialog(this);
    222         mCancelProgressDialog.setMessage(getText(R.string.profile_owner_cancelling));
    223         mCancelProgressDialog.setCancelable(false);
    224         mCancelProgressDialog.setCanceledOnTouchOutside(false);
    225         mCancelProgressDialog.show();
    226     }
    227 
    228     public void error(int resourceId, String logText) {
    229         ProvisionLogger.loge(logText);
    230         new AlertDialog.Builder(this)
    231                 .setTitle(R.string.provisioning_error_title)
    232                 .setMessage(resourceId)
    233                 .setCancelable(false)
    234                 .setPositiveButton(R.string.device_owner_error_ok,
    235                         new DialogInterface.OnClickListener() {
    236                             @Override
    237                             public void onClick(DialogInterface dialog,int id) {
    238                                 onProvisioningAborted();
    239                             }
    240                         })
    241                 .show();
    242     }
    243 
    244     private void confirmCancel() {
    245         if (mCancelStatus != CANCELSTATUS_CONFIRMING) {
    246             // Can only cancel if provisioning hasn't finished at this point.
    247             return;
    248         }
    249         mCancelStatus = CANCELSTATUS_CANCELLING;
    250         Intent intent = new Intent(ProfileOwnerProvisioningActivity.this,
    251                 ProfileOwnerProvisioningService.class);
    252         intent.setAction(ACTION_CANCEL_PROVISIONING);
    253         startService(intent);
    254         showCancelProgressDialog();
    255     }
    256 
    257     /**
    258      * Finish activity and stop service.
    259      */
    260     private void onProvisioningSuccess() {
    261         mBackButton.setVisibility(View.INVISIBLE);
    262 
    263         if (!Utils.isUserSetupCompleted(this)) {
    264             // Since provisioning could have started from Setup wizard, we should set
    265             // USER_SETUP_COMPLETE to true in order to shut down the Setup wizard.
    266             Utils.markDeviceProvisioned(ProfileOwnerProvisioningActivity.this);
    267         }
    268 
    269         mCancelStatus = CANCELSTATUS_FINALIZING;
    270         stopService(new Intent(this, ProfileOwnerProvisioningService.class));
    271         setResult(Activity.RESULT_OK);
    272         finish();
    273     }
    274 
    275     @Override
    276     protected void onSaveInstanceState(Bundle outState) {
    277         outState.putInt(KEY_CANCELSTATUS, mCancelStatus);
    278         outState.putParcelable(KEY_PENDING_INTENT, mPendingProvisioningResult);
    279     }
    280 
    281     @Override
    282     public void onPause() {
    283         LocalBroadcastManager.getInstance(this).unregisterReceiver(mServiceMessageReceiver);
    284         super.onPause();
    285     }
    286 }