• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }