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