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 onCreate(Bundle savedInstanceState)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 onResume()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 onReceive(Context context, Intent intent)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 handleProvisioningResult(Intent intent)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 onProvisioningAborted()190 private void onProvisioningAborted() { 191 stopService(new Intent(this, ProfileOwnerProvisioningService.class)); 192 setResult(Activity.RESULT_CANCELED); 193 finish(); 194 } 195 196 @Override onBackPressed()197 public void onBackPressed() { 198 if (mCancelStatus == STATUS_PROVISIONING) { 199 mCancelStatus = STATUS_CANCEL_CONFIRMING; 200 showCancelProvisioningDialog(); 201 } else { 202 super.onBackPressed(); 203 } 204 } 205 showCancelProvisioningDialog()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 showCancelProgressDialog()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 error(int resourceId, String logText)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 confirmCancel()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 */ onProvisioningSuccess()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 onSaveInstanceState(Bundle outState)281 protected void onSaveInstanceState(Bundle outState) { 282 outState.putInt(KEY_STATUS, mCancelStatus); 283 outState.putParcelable(KEY_PENDING_INTENT, mPendingProvisioningResult); 284 } 285 286 @Override onPause()287 public void onPause() { 288 LocalBroadcastManager.getInstance(this).unregisterReceiver(mServiceMessageReceiver); 289 super.onPause(); 290 } 291 } 292