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.setting_up_workspace, true);
    104         setTitle(R.string.setup_profile_progress);
    105 
    106         if (mCancelStatus == STATUS_CANCEL_CONFIRMING) {
    107             showCancelProvisioningDialog();
    108         } else if (mCancelStatus == STATUS_CANCELLING) {
    109             showCancelProgressDialog();
    110         }
    111         mParams = (ProvisioningParams) getIntent().getParcelableExtra(
    112                 ProvisioningParams.EXTRA_PROVISIONING_PARAMS);
    113         if (mParams != null) {
    114             maybeSetLogoAndMainColor(mParams.mainColor);
    115         }
    116     }
    117 
    118     @Override
    119     protected void onResume() {
    120         super.onResume();
    121 
    122         // Setup broadcast receiver for feedback from service.
    123         mServiceMessageReceiver = new ServiceMessageReceiver();
    124         IntentFilter filter = new IntentFilter();
    125         filter.addAction(ProfileOwnerProvisioningService.ACTION_PROVISIONING_SUCCESS);
    126         filter.addAction(ProfileOwnerProvisioningService.ACTION_PROVISIONING_ERROR);
    127         filter.addAction(ProfileOwnerProvisioningService.ACTION_PROVISIONING_CANCELLED);
    128         LocalBroadcastManager.getInstance(this).registerReceiver(mServiceMessageReceiver, filter);
    129 
    130         // Start service async to make sure the UI is loaded first.
    131         final Handler handler = new Handler(getMainLooper());
    132         handler.post(new Runnable() {
    133             @Override
    134             public void run() {
    135                 Intent intent = new Intent(ProfileOwnerProvisioningActivity.this,
    136                         ProfileOwnerProvisioningService.class);
    137                 intent.putExtras(getIntent());
    138                 startService(intent);
    139             }
    140         });
    141     }
    142 
    143     class ServiceMessageReceiver extends BroadcastReceiver {
    144         @Override
    145         public void onReceive(Context context, Intent intent) {
    146             if (mCancelStatus == STATUS_CANCEL_CONFIRMING) {
    147                 // Store the incoming intent and only process it after the user has responded to
    148                 // the cancel dialog
    149                 mPendingProvisioningResult = intent;
    150                 return;
    151             }
    152             handleProvisioningResult(intent);
    153         }
    154     }
    155 
    156     private void handleProvisioningResult(Intent intent) {
    157         String action = intent.getAction();
    158         if (ProfileOwnerProvisioningService.ACTION_PROVISIONING_SUCCESS.equals(action)) {
    159             if (mCancelStatus == STATUS_CANCELLING) {
    160                 return;
    161             }
    162 
    163             ProvisionLogger.logd("Successfully provisioned."
    164                     + "Finishing ProfileOwnerProvisioningActivity");
    165 
    166             onProvisioningSuccess();
    167         } else if (ProfileOwnerProvisioningService.ACTION_PROVISIONING_ERROR.equals(action)) {
    168             if (mCancelStatus == STATUS_CANCELLING){
    169                 return;
    170             }
    171             String errorLogMessage = intent.getStringExtra(
    172                     ProfileOwnerProvisioningService.EXTRA_LOG_MESSAGE_KEY);
    173             ProvisionLogger.logd("Error reported: " + errorLogMessage);
    174             error(R.string.managed_provisioning_error_text, errorLogMessage);
    175             // Note that this will be reported as a canceled action
    176             mCancelStatus = STATUS_FINALIZING;
    177         } else if (ProfileOwnerProvisioningService.ACTION_PROVISIONING_CANCELLED.equals(action)) {
    178             if (mCancelStatus != STATUS_CANCELLING) {
    179                 return;
    180             }
    181             mCancelProgressDialog.dismiss();
    182             onProvisioningAborted();
    183         }
    184     }
    185 
    186     private void onProvisioningAborted() {
    187         stopService(new Intent(this, ProfileOwnerProvisioningService.class));
    188         setResult(Activity.RESULT_CANCELED);
    189         finish();
    190     }
    191 
    192     @Override
    193     public void onBackPressed() {
    194         if (mCancelStatus == STATUS_PROVISIONING) {
    195             mCancelStatus = STATUS_CANCEL_CONFIRMING;
    196             showCancelProvisioningDialog();
    197         } else {
    198             super.onBackPressed();
    199         }
    200     }
    201 
    202     private void showCancelProvisioningDialog() {
    203         AlertDialog alertDialog = new AlertDialog.Builder(this)
    204                 .setCancelable(false)
    205                 .setMessage(R.string.profile_owner_cancel_message)
    206                 .setNegativeButton(R.string.profile_owner_cancel_cancel,
    207                         new DialogInterface.OnClickListener() {
    208                             @Override
    209                             public void onClick(DialogInterface dialog,int id) {
    210                                 mCancelStatus = STATUS_PROVISIONING;
    211                                 if (mPendingProvisioningResult != null) {
    212                                     handleProvisioningResult(mPendingProvisioningResult);
    213                                 }
    214                             }
    215                         })
    216                 .setPositiveButton(R.string.profile_owner_cancel_ok,
    217                         new DialogInterface.OnClickListener() {
    218                             @Override
    219                             public void onClick(DialogInterface dialog,int id) {
    220                                 confirmCancel();
    221                             }
    222                         })
    223                 .create();
    224         alertDialog.show();
    225     }
    226 
    227     protected void showCancelProgressDialog() {
    228         mCancelProgressDialog = new ProgressDialog(this);
    229         mCancelProgressDialog.setMessage(getText(R.string.profile_owner_cancelling));
    230         mCancelProgressDialog.setCancelable(false);
    231         mCancelProgressDialog.setCanceledOnTouchOutside(false);
    232         mCancelProgressDialog.show();
    233     }
    234 
    235     public void error(int resourceId, String logText) {
    236         ProvisionLogger.loge(logText);
    237         new AlertDialog.Builder(this)
    238                 .setTitle(R.string.provisioning_error_title)
    239                 .setMessage(resourceId)
    240                 .setCancelable(false)
    241                 .setPositiveButton(R.string.device_owner_error_ok,
    242                         new DialogInterface.OnClickListener() {
    243                             @Override
    244                             public void onClick(DialogInterface dialog,int id) {
    245                                 onProvisioningAborted();
    246                             }
    247                         })
    248                 .show();
    249     }
    250 
    251     private void confirmCancel() {
    252         if (mCancelStatus != STATUS_CANCEL_CONFIRMING) {
    253             // Can only cancel if provisioning hasn't finished at this point.
    254             return;
    255         }
    256         mCancelStatus = STATUS_CANCELLING;
    257         Intent intent = new Intent(ProfileOwnerProvisioningActivity.this,
    258                 ProfileOwnerProvisioningService.class);
    259         intent.setAction(ACTION_CANCEL_PROVISIONING);
    260         startService(intent);
    261         showCancelProgressDialog();
    262     }
    263 
    264     /**
    265      * Finish activity and stop service.
    266      */
    267     private void onProvisioningSuccess() {
    268         mCancelStatus = STATUS_FINALIZING;
    269         stopService(new Intent(this, ProfileOwnerProvisioningService.class));
    270         setResult(Activity.RESULT_OK);
    271         finish();
    272     }
    273 
    274     @Override
    275     protected void onSaveInstanceState(Bundle outState) {
    276         outState.putInt(KEY_STATUS, mCancelStatus);
    277         outState.putParcelable(KEY_PENDING_INTENT, mPendingProvisioningResult);
    278     }
    279 
    280     @Override
    281     public void onPause() {
    282         LocalBroadcastManager.getInstance(this).unregisterReceiver(mServiceMessageReceiver);
    283         super.onPause();
    284     }
    285 }
    286