blob: c0d91adc55317e24dd4a346a4196158bcdf6da9c [file] [log] [blame]
Sander Alewijnseed0883b2014-03-18 15:01:13 +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
17package com.android.managedprovisioning;
18
Sander Alewijnsed7043852014-06-17 15:50:48 +010019import static android.app.admin.DeviceAdminReceiver.ACTION_PROFILE_PROVISIONING_COMPLETE;
Sander Alewijnseae9b8212014-07-18 17:25:53 +010020import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_EMAIL_ADDRESS;
Sander Alewijnsed7043852014-06-17 15:50:48 +010021
Sander Alewijnseed0883b2014-03-18 15:01:13 +000022import android.app.Activity;
23import android.app.AlertDialog;
Sander Alewijnsed7043852014-06-17 15:50:48 +010024import android.app.Dialog;
Sander Alewijnseed0883b2014-03-18 15:01:13 +000025import android.app.admin.DevicePolicyManager;
Sander Alewijnse04ab6fe2014-04-29 10:53:54 +010026import android.content.BroadcastReceiver;
Sander Alewijnseed0883b2014-03-18 15:01:13 +000027import android.content.Context;
28import android.content.DialogInterface;
29import android.content.Intent;
Sander Alewijnse04ab6fe2014-04-29 10:53:54 +010030import android.content.IntentFilter;
Sander Alewijnseed0883b2014-03-18 15:01:13 +000031import android.os.Bundle;
32import android.provider.Settings.Global;
Sander Alewijnsed7043852014-06-17 15:50:48 +010033import android.provider.Settings.Secure;
Jessica Hummel81fe1042014-06-23 17:10:38 +010034import android.support.v4.content.LocalBroadcastManager;
Sander Alewijnse326bcfd2014-06-25 15:24:03 +010035import android.text.TextUtils;
Sander Alewijnseed0883b2014-03-18 15:01:13 +000036import android.view.LayoutInflater;
37import android.view.View;
Sander Alewijnse04ab6fe2014-04-29 10:53:54 +010038import android.widget.TextView;
Sander Alewijnsec7757382014-03-18 17:09:45 +000039
Sander Alewijnse326bcfd2014-06-25 15:24:03 +010040import com.android.managedprovisioning.task.AddWifiNetworkTask;
41
Sander Alewijnse56f71572014-06-23 16:21:33 +010042import java.util.Locale;
43
Sander Alewijnseed0883b2014-03-18 15:01:13 +000044/**
45 * This activity starts device owner provisioning:
46 * It downloads a mobile device management application(mdm) from a given url and installs it,
47 * or a given mdm is already present on the device. The mdm is set as the owner of the device so
48 * that it has full control over the device:
49 * TODO: put link here with documentation on how a device owner has control over the device
50 * The mdm can then execute further setup steps.
51 *
52 * <p>
53 * An example use case might be when a company wants to set up a device for a single use case
54 * (such as giving instructions).
55 * </p>
56 *
57 * <p>
58 * Provisioning is triggered by a programmer device that sends required provisioning parameters via
59 * nfc. For an example of a programmer app see:
60 * com.example.android.apis.app.DeviceProvisioningProgrammerSample.
61 * </p>
62 *
Sander Alewijnse4c4badf2014-03-20 14:12:49 +000063 * <p>
64 * In the unlikely case that this activity is killed the whole provisioning process so far is
65 * repeated. We made sure that all tasks can be done twice without causing any problems.
66 * </p>
Sander Alewijnseed0883b2014-03-18 15:01:13 +000067 */
68public class DeviceOwnerProvisioningActivity extends Activity {
Sander Alewijnse28bffd62014-06-05 10:54:26 +010069 private static final int ENCRYPT_DEVICE_REQUEST_CODE = 1;
Sander Alewijnse326bcfd2014-06-25 15:24:03 +010070 private static final int WIFI_REQUEST_CODE = 2;
Sander Alewijnse28bffd62014-06-05 10:54:26 +010071
Sander Alewijnse04ab6fe2014-04-29 10:53:54 +010072 private BroadcastReceiver mServiceMessageReceiver;
73 private TextView mProgressTextView;
Sander Alewijnsed7043852014-06-17 15:50:48 +010074 private Dialog mDialog; // The cancel or error dialog that is currently shown.
75 private boolean mDone; // Indicates whether the service has sent ACTION_PROVISIONING_SUCCESS.
Sander Alewijnse326bcfd2014-06-25 15:24:03 +010076 private ProvisioningParams mParams;
Sander Alewijnse04ab6fe2014-04-29 10:53:54 +010077
Sander Alewijnseed0883b2014-03-18 15:01:13 +000078 @Override
79 public void onCreate(Bundle savedInstanceState) {
80 super.onCreate(savedInstanceState);
81
82 ProvisionLogger.logd("Device owner provisioning activity ONCREATE");
83
84 // Check whether we can provision.
Sander Alewijnsed7043852014-06-17 15:50:48 +010085 if (Global.getInt(getContentResolver(), Global.DEVICE_PROVISIONED, 0 /* default */) != 0) {
Sander Alewijnseed0883b2014-03-18 15:01:13 +000086 ProvisionLogger.loge("Device already provisioned.");
Sander Alewijnsed7043852014-06-17 15:50:48 +010087 error(R.string.device_owner_error_already_provisioned, false /* no factory reset */);
Sander Alewijnseed0883b2014-03-18 15:01:13 +000088 return;
89 }
Sander Alewijnseed0883b2014-03-18 15:01:13 +000090 DevicePolicyManager dpm = (DevicePolicyManager)
91 getSystemService(Context.DEVICE_POLICY_SERVICE);
92 if (dpm.getDeviceOwner() != null) {
93 ProvisionLogger.loge("Device owner already present.");
Sander Alewijnsed7043852014-06-17 15:50:48 +010094 error(R.string.device_owner_error_already_owned, false /* no factory reset */);
Sander Alewijnseed0883b2014-03-18 15:01:13 +000095 return;
96 }
97
Sander Alewijnsed7043852014-06-17 15:50:48 +010098 // Setup the UI.
Sander Alewijnseed0883b2014-03-18 15:01:13 +000099 final LayoutInflater inflater = getLayoutInflater();
Sander Alewijnse8ce0c512014-06-03 17:49:02 +0100100 final View contentView = inflater.inflate(R.layout.progress, null);
Sander Alewijnseed0883b2014-03-18 15:01:13 +0000101 setContentView(contentView);
Sander Alewijnse8ce0c512014-06-03 17:49:02 +0100102 mProgressTextView = (TextView) findViewById(R.id.prog_text);
Sander Alewijnseb6578e72014-07-25 17:12:05 +0100103 TextView titleText = (TextView) findViewById(R.id.title);
104 if (titleText != null) titleText.setText(getString(R.string.setup_device));
Sander Alewijnseed0883b2014-03-18 15:01:13 +0000105
Sander Alewijnse04ab6fe2014-04-29 10:53:54 +0100106 // Setup broadcast receiver for feedback from service.
107 mServiceMessageReceiver = new ServiceMessageReceiver();
108 IntentFilter filter = new IntentFilter();
109 filter.addAction(DeviceOwnerProvisioningService.ACTION_PROVISIONING_SUCCESS);
110 filter.addAction(DeviceOwnerProvisioningService.ACTION_PROVISIONING_ERROR);
111 filter.addAction(DeviceOwnerProvisioningService.ACTION_PROGRESS_UPDATE);
Jessica Hummel81fe1042014-06-23 17:10:38 +0100112 LocalBroadcastManager.getInstance(this).registerReceiver(mServiceMessageReceiver, filter);
Sander Alewijnse04ab6fe2014-04-29 10:53:54 +0100113
Sander Alewijnsed7043852014-06-17 15:50:48 +0100114 // Parse the incoming intent.
115 MessageParser parser = new MessageParser();
Sander Alewijnsed7043852014-06-17 15:50:48 +0100116 try {
Sander Alewijnse326bcfd2014-06-25 15:24:03 +0100117 mParams = parser.parseIntent(getIntent());
Sander Alewijnsed7043852014-06-17 15:50:48 +0100118 } catch (MessageParser.ParseException e) {
119 ProvisionLogger.loge("Could not read data from intent", e);
120 error(e.getErrorMessageId(), false /* no factory reset */);
121 return;
122 }
Sander Alewijnsed7043852014-06-17 15:50:48 +0100123
124 // Ask to encrypt the device before proceeding
125 if (!EncryptDeviceActivity.isDeviceEncrypted()) {
Sander Alewijnse9aff1402014-07-18 17:15:32 +0100126 requestEncryption(parser);
Sander Alewijnsed7043852014-06-17 15:50:48 +0100127 finish();
128 return;
Sander Alewijnse326bcfd2014-06-25 15:24:03 +0100129 // System will reboot. Bootreminder will restart this activity.
Sander Alewijnsed7043852014-06-17 15:50:48 +0100130 }
131
Sander Alewijnse326bcfd2014-06-25 15:24:03 +0100132 // Have the user pick a wifi network if necessary.
133 if (!AddWifiNetworkTask.isConnectedToWifi(this) && TextUtils.isEmpty(mParams.mWifiSsid) &&
134 !TextUtils.isEmpty(mParams.mDeviceAdminPackageDownloadLocation)) {
135 requestWifiPick();
136 return;
137 // Wait for onActivityResult.
138 }
139
140 startDeviceOwnerProvisioningService();
141 }
142
143 private void startDeviceOwnerProvisioningService() {
Sander Alewijnse04ab6fe2014-04-29 10:53:54 +0100144 Intent intent = new Intent(this, DeviceOwnerProvisioningService.class);
Sander Alewijnse326bcfd2014-06-25 15:24:03 +0100145 intent.putExtra(DeviceOwnerProvisioningService.EXTRA_PROVISIONING_PARAMS, mParams);
Sander Alewijnse04ab6fe2014-04-29 10:53:54 +0100146 intent.putExtras(getIntent());
147 startService(intent);
Sander Alewijnseed0883b2014-03-18 15:01:13 +0000148 }
149
Jessica Hummel14eeef92014-06-16 11:06:20 +0100150 class ServiceMessageReceiver extends BroadcastReceiver
Sander Alewijnse04ab6fe2014-04-29 10:53:54 +0100151 {
152 @Override
153 public void onReceive(Context context, Intent intent)
154 {
155 String action = intent.getAction();
156 if (action.equals(DeviceOwnerProvisioningService.ACTION_PROVISIONING_SUCCESS)) {
157 ProvisionLogger.logd("Successfully provisioned");
Sander Alewijnsed7043852014-06-17 15:50:48 +0100158 synchronized(this) {
159 if (mDialog == null) {
160 onProvisioningSuccess();
161 } else {
162 // Postpone finishing this activity till the user has decided whether
163 // he/she wants to reset or not.
164 mDone = true;
165 }
166 }
Sander Alewijnse04ab6fe2014-04-29 10:53:54 +0100167 return;
168 } else if (action.equals(DeviceOwnerProvisioningService.ACTION_PROVISIONING_ERROR)) {
Sander Alewijnsed7043852014-06-17 15:50:48 +0100169 int errorMessageId = intent.getIntExtra(
170 DeviceOwnerProvisioningService.EXTRA_USER_VISIBLE_ERROR_ID_KEY,
171 R.string.device_owner_error_general);
172
Sander Alewijnse28bffd62014-06-05 10:54:26 +0100173 ProvisionLogger.logd("Error reported with code "
Sander Alewijnsed7043852014-06-17 15:50:48 +0100174 + getResources().getString(errorMessageId));
175 error(errorMessageId, true /* always factory reset */);
Sander Alewijnse04ab6fe2014-04-29 10:53:54 +0100176 } else if (action.equals(DeviceOwnerProvisioningService.ACTION_PROGRESS_UPDATE)) {
177 int progressMessage = intent.getIntExtra(
178 DeviceOwnerProvisioningService.EXTRA_PROGRESS_MESSAGE_ID_KEY, -1);
Sander Alewijnse28bffd62014-06-05 10:54:26 +0100179 ProvisionLogger.logd("Progress update reported with code "
180 + getResources().getString(progressMessage));
181 if (progressMessage >= 0) {
Sander Alewijnse04ab6fe2014-04-29 10:53:54 +0100182 progressUpdate(progressMessage);
Sander Alewijnseaf8413e2014-03-19 11:37:44 +0000183 }
Sander Alewijnsec7757382014-03-18 17:09:45 +0000184 }
Sander Alewijnse63254f42014-03-21 15:31:12 +0000185 }
186 }
187
Sander Alewijnsed7043852014-06-17 15:50:48 +0100188 private void onProvisioningSuccess() {
189 stopService(new Intent(DeviceOwnerProvisioningActivity.this,
190 DeviceOwnerProvisioningService.class));
191
192 // Skip the setup wizard.
193 Global.putInt(getContentResolver(), Global.DEVICE_PROVISIONED, 1);
194 Secure.putInt(getContentResolver(), Secure.USER_SETUP_COMPLETE, 1);
195
196 Intent completeIntent = new Intent(ACTION_PROFILE_PROVISIONING_COMPLETE);
Sander Alewijnse326bcfd2014-06-25 15:24:03 +0100197 completeIntent.setPackage(mParams.mDeviceAdminPackageName);
Sander Alewijnsed7043852014-06-17 15:50:48 +0100198 completeIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES |
199 Intent.FLAG_RECEIVER_FOREGROUND);
Sander Alewijnseae9b8212014-07-18 17:25:53 +0100200 if (mParams.mManagedDeviceEmailAddress != null) {
201 completeIntent.putExtra(EXTRA_PROVISIONING_EMAIL_ADDRESS,
202 mParams.mManagedDeviceEmailAddress);
203 }
204
Sander Alewijnsed7043852014-06-17 15:50:48 +0100205 sendBroadcast(completeIntent);
Sander Alewijnse1c8d9312014-08-18 20:00:49 +0100206
207 setResult(Activity.RESULT_OK);
Sander Alewijnsed7043852014-06-17 15:50:48 +0100208 finish();
209 }
210
Sander Alewijnse9aff1402014-07-18 17:15:32 +0100211 private void requestEncryption(MessageParser messageParser) {
Sander Alewijnse56f71572014-06-23 16:21:33 +0100212 Intent encryptIntent = new Intent(DeviceOwnerProvisioningActivity.this,
213 EncryptDeviceActivity.class);
214
215 Bundle resumeExtras = new Bundle();
216 resumeExtras.putString(EncryptDeviceActivity.EXTRA_RESUME_TARGET,
217 EncryptDeviceActivity.TARGET_DEVICE_OWNER);
Sander Alewijnse9aff1402014-07-18 17:15:32 +0100218 messageParser.addProvisioningParamsToBundle(resumeExtras, mParams);
Sander Alewijnse56f71572014-06-23 16:21:33 +0100219
220 encryptIntent.putExtra(EncryptDeviceActivity.EXTRA_RESUME, resumeExtras);
221
222 startActivityForResult(encryptIntent, ENCRYPT_DEVICE_REQUEST_CODE);
223 }
224
Sander Alewijnse326bcfd2014-06-25 15:24:03 +0100225 private void requestWifiPick() {
226 startActivityForResult(AddWifiNetworkTask.getWifiPickIntent(), WIFI_REQUEST_CODE);
227 }
228
Sander Alewijnse63254f42014-03-21 15:31:12 +0000229 @Override
230 public void onBackPressed() {
Sander Alewijnsed7043852014-06-17 15:50:48 +0100231 showCancelResetDialog();
232 }
233
234 private void showCancelResetDialog() {
235 AlertDialog.Builder alertBuilder =
236 new AlertDialog.Builder(DeviceOwnerProvisioningActivity.this)
237 .setTitle(R.string.device_owner_cancel_title)
238 .setMessage(R.string.device_owner_cancel_message)
239 .setNegativeButton(R.string.device_owner_cancel_cancel,
240 new DialogInterface.OnClickListener() {
241 @Override
242 public void onClick(DialogInterface dialog,int id) {
243 dialog.dismiss();
244 synchronized(this) {
245 mDialog = null;
246 if (mDone) {
247 onProvisioningSuccess();
248 }
249 }
250 }
251 })
252 .setPositiveButton(R.string.device_owner_error_reset,
253 new DialogInterface.OnClickListener() {
254 @Override
255 public void onClick(DialogInterface dialog,int id) {
256 // Factory reset the device.
257 sendBroadcast(
258 new Intent("android.intent.action.MASTER_CLEAR"));
259 stopService(new Intent(DeviceOwnerProvisioningActivity.this,
260 DeviceOwnerProvisioningService.class));
261 finish();
262 }
263 });
264
265 if (mDialog != null) {
266 mDialog.dismiss();
267 }
268 mDialog = alertBuilder.create();
269 mDialog.show();
Sander Alewijnse63254f42014-03-21 15:31:12 +0000270 }
271
Sander Alewijnse04ab6fe2014-04-29 10:53:54 +0100272 private void progressUpdate(int progressMessage) {
273 mProgressTextView.setText(progressMessage);
274 }
275
Sander Alewijnse28bffd62014-06-05 10:54:26 +0100276 @Override
277 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
278 if (requestCode == ENCRYPT_DEVICE_REQUEST_CODE) {
279 if (resultCode == RESULT_CANCELED) {
280 ProvisionLogger.loge("User canceled device encryption.");
281 finish();
282 }
Sander Alewijnse326bcfd2014-06-25 15:24:03 +0100283 } else if (requestCode == WIFI_REQUEST_CODE) {
284 if (resultCode == RESULT_CANCELED) {
285 ProvisionLogger.loge("User canceled wifi picking.");
286 stopService(new Intent(DeviceOwnerProvisioningActivity.this,
287 DeviceOwnerProvisioningService.class));
288 finish();
289 } else if (resultCode == RESULT_OK) {
290 ProvisionLogger.logd("Wifi request result is OK");
291 if (AddWifiNetworkTask.isConnectedToWifi(this)) {
292 startDeviceOwnerProvisioningService();
293 } else {
294 requestWifiPick();
295 }
296 }
Sander Alewijnse28bffd62014-06-05 10:54:26 +0100297 }
298 }
299
Sander Alewijnsed7043852014-06-17 15:50:48 +0100300 private void error(int dialogMessage, boolean resetRequired) {
301 AlertDialog.Builder alertBuilder = new AlertDialog.Builder(this)
Sander Alewijnseb6578e72014-07-25 17:12:05 +0100302 .setTitle(R.string.provisioning_error_title)
Sander Alewijnseed0883b2014-03-18 15:01:13 +0000303 .setMessage(dialogMessage)
Sander Alewijnsed7043852014-06-17 15:50:48 +0100304 .setCancelable(false);
305 if (resetRequired) {
306 alertBuilder.setPositiveButton(R.string.device_owner_error_reset,
307 new DialogInterface.OnClickListener() {
308 @Override
309 public void onClick(DialogInterface dialog,int id) {
310 // Factory reset the device.
311 sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR"));
312 stopService(new Intent(DeviceOwnerProvisioningActivity.this,
313 DeviceOwnerProvisioningService.class));
314 finish();
315 }
316 });
317 } else {
318 alertBuilder.setPositiveButton(R.string.device_owner_error_ok,
319 new DialogInterface.OnClickListener() {
320 @Override
321 public void onClick(DialogInterface dialog,int id) {
322 // Close activity.
323 stopService(new Intent(DeviceOwnerProvisioningActivity.this,
324 DeviceOwnerProvisioningService.class));
325 finish();
326 }
327 });
328 }
329 mDialog = alertBuilder.create();
330 mDialog.show();
Sander Alewijnseed0883b2014-03-18 15:01:13 +0000331 }
Sander Alewijnse28bffd62014-06-05 10:54:26 +0100332
Jessica Hummel14eeef92014-06-16 11:06:20 +0100333 @Override
Sander Alewijnse56f71572014-06-23 16:21:33 +0100334 public void onDestroy() {
335 ProvisionLogger.logd("Device owner provisioning activity ONDESTROY");
336 if (mServiceMessageReceiver != null) {
337 LocalBroadcastManager.getInstance(this).unregisterReceiver(mServiceMessageReceiver);
338 mServiceMessageReceiver = null;
339 }
340 if (mDialog != null) {
341 mDialog.dismiss();
342 mDialog = null;
343 }
344 super.onDestroy();
345 }
346
347 @Override
Sander Alewijnse28bffd62014-06-05 10:54:26 +0100348 protected void onRestart() {
349 ProvisionLogger.logd("Device owner provisioning activity ONRESTART");
350 super.onRestart();
351 }
352
Jessica Hummel14eeef92014-06-16 11:06:20 +0100353 @Override
Sander Alewijnse28bffd62014-06-05 10:54:26 +0100354 protected void onResume() {
355 ProvisionLogger.logd("Device owner provisioning activity ONRESUME");
356 super.onResume();
357 }
358
Jessica Hummel14eeef92014-06-16 11:06:20 +0100359 @Override
Sander Alewijnse28bffd62014-06-05 10:54:26 +0100360 protected void onPause() {
361 ProvisionLogger.logd("Device owner provisioning activity ONPAUSE");
362 super.onPause();
363 }
364
Jessica Hummel14eeef92014-06-16 11:06:20 +0100365 @Override
Sander Alewijnse28bffd62014-06-05 10:54:26 +0100366 protected void onStop() {
367 ProvisionLogger.logd("Device owner provisioning activity ONSTOP");
368 super.onStop();
369 }
Sander Alewijnseed0883b2014-03-18 15:01:13 +0000370}
371