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 17package com.android.managedprovisioning; 18 19import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE; 20import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME; 21 22import android.accounts.Account; 23import android.accounts.AccountManager; 24import android.accounts.AccountManagerFuture; 25import android.accounts.AuthenticatorException; 26import android.accounts.OperationCanceledException; 27import android.app.Activity; 28import android.app.AlertDialog; 29import android.app.ProgressDialog; 30import android.app.admin.DevicePolicyManager; 31import android.content.BroadcastReceiver; 32import android.content.Context; 33import android.content.DialogInterface; 34import android.content.Intent; 35import android.content.IntentFilter; 36import android.os.AsyncTask; 37import android.os.Bundle; 38import android.os.ConditionVariable; 39import android.os.Handler; 40import android.os.UserHandle; 41import android.os.UserManager; 42import android.provider.Settings; 43import android.support.v4.content.LocalBroadcastManager; 44import android.view.View; 45import android.widget.TextView; 46 47import com.android.managedprovisioning.model.ProvisioningParams; 48 49import java.io.IOException; 50import 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 */ 65public 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