blob: a785b0915bd815793a3a2c65d499463baf2853eb [file] [log] [blame]
Alexandra Gherghinae9965bb2014-02-25 17:09:40 +00001/*
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
Jessica Hummel8119e122014-01-10 12:13:49 +000017package com.android.managedprovisioning;
18
Alexandra Gherghina902c46f2014-11-20 16:29:29 +000019import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE;
20
21import android.accounts.Account;
22import android.accounts.AccountManager;
23import android.accounts.AccountManagerFuture;
24import android.accounts.AuthenticatorException;
25import android.accounts.OperationCanceledException;
Jessica Hummel8119e122014-01-10 12:13:49 +000026import android.app.Activity;
Sander Alewijnse606780d2014-10-29 10:51:39 +000027import android.app.ActivityManagerNative;
Jessica Hummel22265142014-07-15 16:10:12 +010028import android.app.AlertDialog;
Sander Alewijnse606780d2014-10-29 10:51:39 +000029import android.app.IActivityManager;
Rubin Xu88577472014-10-06 11:00:17 +010030import android.app.ProgressDialog;
Jessica Hummel14eeef92014-06-16 11:06:20 +010031import android.content.BroadcastReceiver;
Robin Lee4af4ed02014-08-04 12:29:31 +010032import android.content.ComponentName;
Jessica Hummelb5f001d2014-01-10 16:39:07 +000033import android.content.Context;
Jessica Hummel22265142014-07-15 16:10:12 +010034import android.content.DialogInterface;
Jessica Hummelb5f001d2014-01-10 16:39:07 +000035import android.content.Intent;
Nicolas Prevot3581ff92014-05-01 17:21:43 +010036import android.content.IntentFilter;
Alexandra Gherghina902c46f2014-11-20 16:29:29 +000037import android.os.AsyncTask;
Jessica Hummel8119e122014-01-10 12:13:49 +000038import android.os.Bundle;
Sander Alewijnse3dc234a2014-11-27 18:30:11 +000039import android.os.Handler;
Sander Alewijnse606780d2014-10-29 10:51:39 +000040import android.os.RemoteException;
Esteban Talavera928febf2014-08-01 14:37:08 +010041import android.os.UserHandle;
Jessica Hummelc4d5d2b2014-02-27 15:37:01 +000042import android.os.UserManager;
Sander Alewijnse606780d2014-10-29 10:51:39 +000043import android.provider.Settings;
Jessica Hummel81fe1042014-06-23 17:10:38 +010044import android.support.v4.content.LocalBroadcastManager;
Jessica Hummela08b23e2014-02-03 18:12:58 +000045import android.view.LayoutInflater;
46import android.view.View;
Jessica Hummel0ea830d2014-06-11 12:29:40 +010047import android.widget.Button;
Sander Alewijnse606780d2014-10-29 10:51:39 +000048import android.widget.TextView;
Jessica Hummel8119e122014-01-10 12:13:49 +000049
Alexandra Gherghina902c46f2014-11-20 16:29:29 +000050import java.io.IOException;
51
Jason Monkc6d71b82014-01-17 14:52:00 -050052/**
Sander Alewijnse30bc13c2014-11-03 12:21:30 +000053 * Profile owner provisioning sets up a separate profile on a device whose primary user is already
54 * set up.
55 *
56 * <p>
Jessica Hummel14eeef92014-06-16 11:06:20 +010057 * 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 *
Sander Alewijnse30bc13c2014-11-03 12:21:30 +000060 * <p>
Sander Alewijnse606780d2014-10-29 10:51:39 +000061 * The activity handles the UI for managed profile provisioning and starts the
Sander Alewijnse30bc13c2014-11-03 12:21:30 +000062 * {@link ProfileOwnerProvisioningService}, which runs through the setup steps in an
Jessica Hummel14eeef92014-06-16 11:06:20 +010063 * async task.
Jason Monkc6d71b82014-01-17 14:52:00 -050064 */
Sander Alewijnse30bc13c2014-11-03 12:21:30 +000065public class ProfileOwnerProvisioningActivity extends Activity {
Rubin Xu88577472014-10-06 11:00:17 +010066 protected static final String ACTION_CANCEL_PROVISIONING =
67 "com.android.managedprovisioning.CANCEL_PROVISIONING";
68
Jessica Hummel14eeef92014-06-16 11:06:20 +010069 private BroadcastReceiver mServiceMessageReceiver;
Jessica Hummelb5f001d2014-01-10 16:39:07 +000070
Rubin Xu88577472014-10-06 11:00:17 +010071 // Provisioning service started
Sander Alewijnse606780d2014-10-29 10:51:39 +000072 private static final int CANCELSTATUS_PROVISIONING = 1;
Rubin Xu88577472014-10-06 11:00:17 +010073 // Back button pressed during provisioning, confirm dialog showing.
Sander Alewijnse606780d2014-10-29 10:51:39 +000074 private static final int CANCELSTATUS_CONFIRMING = 2;
Rubin Xu88577472014-10-06 11:00:17 +010075 // Cancel confirmed, waiting for the provisioning service to complete.
Sander Alewijnse606780d2014-10-29 10:51:39 +000076 private static final int CANCELSTATUS_CANCELLING = 3;
77 // Cancelling not possible anymore, provisioning already finished succesfully.
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;
Rubin Xu88577472014-10-06 11:00:17 +010084 private Intent mPendingProvisioningResult = null;
85 private ProgressDialog mCancelProgressDialog = null;
Alexandra Gherghina902c46f2014-11-20 16:29:29 +000086 private AccountManager mAccountManager;
Rubin Xu88577472014-10-06 11:00:17 +010087
Jessica Hummel8119e122014-01-10 12:13:49 +000088 @Override
Jessica Hummelc4d5d2b2014-02-27 15:37:01 +000089 protected void onCreate(Bundle savedInstanceState) {
Jessica Hummel8119e122014-01-10 12:13:49 +000090 super.onCreate(savedInstanceState);
Sander Alewijnse30bc13c2014-11-03 12:21:30 +000091 ProvisionLogger.logd("Profile owner provisioning activity ONCREATE");
Alexandra Gherghina902c46f2014-11-20 16:29:29 +000092 mAccountManager = (AccountManager) getSystemService(Context.ACCOUNT_SERVICE);
Jessica Hummelb5f001d2014-01-10 16:39:07 +000093
Sander Alewijnse606780d2014-10-29 10:51:39 +000094 if (savedInstanceState != null) {
95 mCancelStatus = savedInstanceState.getInt(KEY_CANCELSTATUS, CANCELSTATUS_PROVISIONING);
96 mPendingProvisioningResult = savedInstanceState.getParcelable(KEY_PENDING_INTENT);
Adam Connors7f8c6e72014-04-01 12:14:54 +010097 }
Sander Alewijnse606780d2014-10-29 10:51:39 +000098
99 final LayoutInflater inflater = getLayoutInflater();
100 View contentView = inflater.inflate(R.layout.progress, null);
101 setContentView(contentView);
102 TextView textView = (TextView) findViewById(R.id.prog_text);
103 if (textView != null) textView.setText(getString(R.string.setting_up_workspace));
104
105
106 if (mCancelStatus == CANCELSTATUS_CONFIRMING) {
107 showCancelProvisioningDialog();
108 } else if (mCancelStatus == CANCELSTATUS_CANCELLING) {
109 showCancelProgressDialog();
Esteban Talavera928febf2014-08-01 14:37:08 +0100110 }
Sander Alewijnse3dc234a2014-11-27 18:30:11 +0000111 }
112
113
114 @Override
115 protected void onResume() {
116 super.onResume();
Adam Connors7f8c6e72014-04-01 12:14:54 +0100117
Jessica Hummel14eeef92014-06-16 11:06:20 +0100118 // Setup broadcast receiver for feedback from service.
119 mServiceMessageReceiver = new ServiceMessageReceiver();
120 IntentFilter filter = new IntentFilter();
Sander Alewijnse30bc13c2014-11-03 12:21:30 +0000121 filter.addAction(ProfileOwnerProvisioningService.ACTION_PROVISIONING_SUCCESS);
122 filter.addAction(ProfileOwnerProvisioningService.ACTION_PROVISIONING_ERROR);
123 filter.addAction(ProfileOwnerProvisioningService.ACTION_PROVISIONING_CANCELLED);
Jessica Hummel81fe1042014-06-23 17:10:38 +0100124 LocalBroadcastManager.getInstance(this).registerReceiver(mServiceMessageReceiver, filter);
Jessica Hummel14eeef92014-06-16 11:06:20 +0100125
Sander Alewijnse3dc234a2014-11-27 18:30:11 +0000126 // Start service async to make sure the UI is loaded first.
127 final Handler handler = new Handler(getMainLooper());
128 handler.post(new Runnable() {
129 @Override
130 public void run() {
131 Intent intent = new Intent(ProfileOwnerProvisioningActivity.this,
132 ProfileOwnerProvisioningService.class);
133 intent.putExtras(getIntent());
134 startService(intent);
135 }
136 });
Sander Alewijnse2d0a0932014-07-10 15:12:37 +0100137 }
138
Jessica Hummel14eeef92014-06-16 11:06:20 +0100139 class ServiceMessageReceiver extends BroadcastReceiver {
140 @Override
141 public void onReceive(Context context, Intent intent) {
Rubin Xu88577472014-10-06 11:00:17 +0100142 if (mCancelStatus == CANCELSTATUS_CONFIRMING) {
143 // Store the incoming intent and only process it after the user has responded to
144 // the cancel dialog
145 mPendingProvisioningResult = intent;
Jessica Hummel14eeef92014-06-16 11:06:20 +0100146 return;
147 }
Rubin Xu88577472014-10-06 11:00:17 +0100148 handleProvisioningResult(intent);
149 }
150 }
151
152 private void handleProvisioningResult(Intent intent) {
153 String action = intent.getAction();
Sander Alewijnse30bc13c2014-11-03 12:21:30 +0000154 if (ProfileOwnerProvisioningService.ACTION_PROVISIONING_SUCCESS.equals(action)) {
Rubin Xu88577472014-10-06 11:00:17 +0100155 if (mCancelStatus == CANCELSTATUS_CANCELLING) {
156 return;
157 }
Sander Alewijnse606780d2014-10-29 10:51:39 +0000158
Rubin Xu88577472014-10-06 11:00:17 +0100159 ProvisionLogger.logd("Successfully provisioned."
Sander Alewijnse30bc13c2014-11-03 12:21:30 +0000160 + "Finishing ProfileOwnerProvisioningActivity");
Sander Alewijnse606780d2014-10-29 10:51:39 +0000161
162 Intent pendingIntent = (Intent) intent.getParcelableExtra(
Sander Alewijnse30bc13c2014-11-03 12:21:30 +0000163 ProfileOwnerProvisioningService.EXTRA_PENDING_SUCCESS_INTENT);
Sander Alewijnse606780d2014-10-29 10:51:39 +0000164 int serialNumber = intent.getIntExtra(
Sander Alewijnse30bc13c2014-11-03 12:21:30 +0000165 ProfileOwnerProvisioningService.EXTRA_PROFILE_USER_SERIAL_NUMBER, -1);
Sander Alewijnse606780d2014-10-29 10:51:39 +0000166
Alexandra Gherghinad74aac82014-11-20 16:29:29 +0000167 int userId = intent.getIntExtra(ProfileOwnerProvisioningService.EXTRA_PROFILE_USER_ID,
168 -1);
Sander Alewijnse606780d2014-10-29 10:51:39 +0000169 onProvisioningSuccess(pendingIntent, userId, serialNumber);
Sander Alewijnse30bc13c2014-11-03 12:21:30 +0000170 } else if (ProfileOwnerProvisioningService.ACTION_PROVISIONING_ERROR.equals(action)) {
Rubin Xu88577472014-10-06 11:00:17 +0100171 if (mCancelStatus == CANCELSTATUS_CANCELLING){
172 return;
173 }
174 String errorLogMessage = intent.getStringExtra(
Sander Alewijnse30bc13c2014-11-03 12:21:30 +0000175 ProfileOwnerProvisioningService.EXTRA_LOG_MESSAGE_KEY);
Rubin Xu88577472014-10-06 11:00:17 +0100176 ProvisionLogger.logd("Error reported: " + errorLogMessage);
Sander Alewijnse606780d2014-10-29 10:51:39 +0000177 error(R.string.managed_provisioning_error_text, errorLogMessage);
Sander Alewijnse30bc13c2014-11-03 12:21:30 +0000178 } if (ProfileOwnerProvisioningService.ACTION_PROVISIONING_CANCELLED.equals(action)) {
Rubin Xu88577472014-10-06 11:00:17 +0100179 if (mCancelStatus != CANCELSTATUS_CANCELLING) {
180 return;
181 }
182 mCancelProgressDialog.dismiss();
Sander Alewijnse30bc13c2014-11-03 12:21:30 +0000183 ProfileOwnerProvisioningActivity.this.setResult(Activity.RESULT_CANCELED);
184 stopService(new Intent(ProfileOwnerProvisioningActivity.this,
185 ProfileOwnerProvisioningService.class));
186 ProfileOwnerProvisioningActivity.this.finish();
Jessica Hummel14eeef92014-06-16 11:06:20 +0100187 }
188 }
189
Jessica Hummelc4d5d2b2014-02-27 15:37:01 +0000190 @Override
191 public void onBackPressed() {
Sander Alewijnse606780d2014-10-29 10:51:39 +0000192 if (mCancelStatus == CANCELSTATUS_PROVISIONING) {
Rubin Xu88577472014-10-06 11:00:17 +0100193 showCancelProvisioningDialog();
194 }
195 }
196
197 private void showCancelProvisioningDialog() {
198 mCancelStatus = CANCELSTATUS_CONFIRMING;
199 AlertDialog alertDialog = new AlertDialog.Builder(this)
200 .setCancelable(false)
201 .setTitle(R.string.profile_owner_cancel_title)
202 .setMessage(R.string.profile_owner_cancel_message)
203 .setNegativeButton(R.string.profile_owner_cancel_cancel,
204 new DialogInterface.OnClickListener() {
205 @Override
206 public void onClick(DialogInterface dialog,int id) {
207 mCancelStatus = CANCELSTATUS_PROVISIONING;
208 if (mPendingProvisioningResult != null) {
209 handleProvisioningResult(mPendingProvisioningResult);
210 }
211 }
212 })
213 .setPositiveButton(R.string.profile_owner_cancel_ok,
214 new DialogInterface.OnClickListener() {
215 @Override
216 public void onClick(DialogInterface dialog,int id) {
Sander Alewijnse606780d2014-10-29 10:51:39 +0000217 confirmCancel();
Rubin Xu88577472014-10-06 11:00:17 +0100218 }
219 })
220 .create();
221 alertDialog.show();
222 }
223
224 protected void showCancelProgressDialog() {
225 mCancelProgressDialog = new ProgressDialog(this);
226 mCancelProgressDialog.setMessage(getText(R.string.profile_owner_cancelling));
227 mCancelProgressDialog.setCancelable(false);
228 mCancelProgressDialog.setCanceledOnTouchOutside(false);
229 mCancelProgressDialog.show();
Jessica Hummelb5f001d2014-01-10 16:39:07 +0000230 }
Jessica Hummel39259232014-02-03 18:12:58 +0000231
Sander Alewijnse606780d2014-10-29 10:51:39 +0000232 public void error(int resourceId, String logText) {
233 ProvisionLogger.loge(logText);
234 new AlertDialog.Builder(this)
235 .setTitle(R.string.provisioning_error_title)
236 .setMessage(getString(resourceId))
237 .setCancelable(false)
238 .setPositiveButton(R.string.device_owner_error_ok, new DialogInterface.OnClickListener() {
239 @Override
240 public void onClick(DialogInterface dialog,int id) {
241 confirmCancel();
242 }
243 }).show();
244 }
245
246 private void confirmCancel() {
247 mCancelStatus = CANCELSTATUS_CANCELLING;
Sander Alewijnse30bc13c2014-11-03 12:21:30 +0000248 Intent intent = new Intent(ProfileOwnerProvisioningActivity.this,
249 ProfileOwnerProvisioningService.class);
Sander Alewijnse606780d2014-10-29 10:51:39 +0000250 intent.setAction(ACTION_CANCEL_PROVISIONING);
251 startService(intent);
252 showCancelProgressDialog();
253 }
254
255 /**
Sander Alewijnse606780d2014-10-29 10:51:39 +0000256 * Notify the mdm that provisioning has completed. When the mdm has received the intent, stop
Sander Alewijnse30bc13c2014-11-03 12:21:30 +0000257 * the service and notify the {@link ProfileOwnerProvisioningActivity} so that it can finish itself.
Sander Alewijnse606780d2014-10-29 10:51:39 +0000258 */
259 private void onProvisioningSuccess(Intent pendingSuccessIntent, int userId, int serialNumber) {
260 mCancelStatus = CANCELSTATUS_FINALIZING;
261 Settings.Secure.putIntForUser(getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE,
262 1 /* true- > setup complete */, userId);
263
264 UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
265 UserHandle userHandle = userManager.getUserForSerialNumber(serialNumber);
266
267 // Use an ordered broadcast, so that we only finish when the mdm has received it.
268 // Avoids a lag in the transition between provisioning and the mdm.
269 BroadcastReceiver mdmReceivedSuccessReceiver = new BroadcastReceiver() {
270 @Override
271 public void onReceive(Context context, Intent intent) {
272 ProvisionLogger.logd("ACTION_PROFILE_PROVISIONING_COMPLETE broadcast received by"
273 + " mdm");
Sander Alewijnse30bc13c2014-11-03 12:21:30 +0000274 ProfileOwnerProvisioningActivity.this.setResult(Activity.RESULT_OK);
Alexandra Gherghina902c46f2014-11-20 16:29:29 +0000275
276 // Now cleanup the primary profile if necessary
277 if (getIntent().hasExtra(EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE)) {
278 ProvisionLogger.logd("Cleaning up account from the primary user.");
279 final Account account = (Account) getIntent().getParcelableExtra(
280 EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE);
281 new AsyncTask<Void, Void, Void>() {
282 @Override
283 protected Void doInBackground(Void... params) {
284 removeAccount(account);
285 return null;
286 }
287 }.execute();
288 }
289
Sander Alewijnse30bc13c2014-11-03 12:21:30 +0000290 ProfileOwnerProvisioningActivity.this.finish();
291 stopService(new Intent(ProfileOwnerProvisioningActivity.this,
292 ProfileOwnerProvisioningService.class));
Sander Alewijnse606780d2014-10-29 10:51:39 +0000293 }
294 };
295
296 sendOrderedBroadcastAsUser(pendingSuccessIntent, userHandle, null,
297 mdmReceivedSuccessReceiver, null, Activity.RESULT_OK, null, null);
298 ProvisionLogger.logd("Provisioning complete broadcast has been sent to user "
299 + userHandle.getIdentifier());
300 }
301
Alexandra Gherghina902c46f2014-11-20 16:29:29 +0000302 private void removeAccount(Account account) {
303 try {
Alexandra Gherghina88a1a702014-12-04 09:50:46 +0000304 AccountManagerFuture<Bundle> bundle = mAccountManager.removeAccount(account,
305 this, null /* callback */, null /* handler */);
306 if (bundle.getResult().getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
Alexandra Gherghina902c46f2014-11-20 16:29:29 +0000307 ProvisionLogger.logw("Account removed from the primary user.");
308 } else {
309 ProvisionLogger.logw("Could not remove account from the primary user.");
310 }
311 } catch (OperationCanceledException | AuthenticatorException | IOException e) {
312 ProvisionLogger.logw("Exception removing account from the primary user.", e);
313 }
314 }
Sander Alewijnse606780d2014-10-29 10:51:39 +0000315
316 @Override
317 protected void onSaveInstanceState(Bundle outState) {
318 outState.putInt(KEY_CANCELSTATUS, mCancelStatus);
319 outState.putParcelable(KEY_PENDING_INTENT, mPendingProvisioningResult);
320 }
321
Jessica Hummel14eeef92014-06-16 11:06:20 +0100322 @Override
Sander Alewijnse3dc234a2014-11-27 18:30:11 +0000323 public void onPause() {
Jessica Hummel81fe1042014-06-23 17:10:38 +0100324 LocalBroadcastManager.getInstance(this).unregisterReceiver(mServiceMessageReceiver);
Sander Alewijnse3dc234a2014-11-27 18:30:11 +0000325 super.onPause();
Jessica Hummel14eeef92014-06-16 11:06:20 +0100326 }
Jessica Hummel8119e122014-01-10 12:13:49 +0000327}
Jessica Hummelc4d5d2b2014-02-27 15:37:01 +0000328