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 onCreate(Bundle savedInstanceState)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 onResume()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 onReceive(Context context, Intent intent)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 handleProvisioningResult(Intent intent)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 onProvisioningAborted()181 private void onProvisioningAborted() { 182 stopService(new Intent(this, ProfileOwnerProvisioningService.class)); 183 setResult(Activity.RESULT_CANCELED); 184 finish(); 185 } 186 187 @Override onBackPressed()188 public void onBackPressed() { 189 if (mCancelStatus != CANCELSTATUS_PROVISIONING) { 190 mCancelStatus = CANCELSTATUS_CONFIRMING; 191 showCancelProvisioningDialog(); 192 } 193 } 194 showCancelProvisioningDialog()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 showCancelProgressDialog()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 error(int resourceId, String logText)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 confirmCancel()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 */ onProvisioningSuccess()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 onSaveInstanceState(Bundle outState)276 protected void onSaveInstanceState(Bundle outState) { 277 outState.putInt(KEY_CANCELSTATUS, mCancelStatus); 278 outState.putParcelable(KEY_PENDING_INTENT, mPendingProvisioningResult); 279 } 280 281 @Override onPause()282 public void onPause() { 283 LocalBroadcastManager.getInstance(this).unregisterReceiver(mServiceMessageReceiver); 284 super.onPause(); 285 } 286 }