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 android.app.Activity; 20 import android.app.AlertDialog; 21 import android.content.BroadcastReceiver; 22 import android.content.Context; 23 import android.content.DialogInterface; 24 import android.content.Intent; 25 import android.content.IntentFilter; 26 import android.os.Bundle; 27 import android.os.UserHandle; 28 import android.support.v4.content.LocalBroadcastManager; 29 import android.view.accessibility.AccessibilityEvent; 30 import android.view.View; 31 import android.widget.TextView; 32 33 import com.android.managedprovisioning.model.ProvisioningParams; 34 35 import java.util.ArrayList; 36 37 /** 38 * This activity starts device owner provisioning: 39 * It downloads a mobile device management application(mdm) from a given url and installs it, 40 * or a given mdm is already present on the device. The mdm is set as the owner of the device so 41 * that it has full control over the device: 42 * TODO: put link here with documentation on how a device owner has control over the device 43 * The mdm can then execute further setup steps. 44 * 45 * <p> 46 * An example use case might be when a company wants to set up a device for a single use case 47 * (such as giving instructions). 48 * </p> 49 * 50 * <p> 51 * Provisioning is triggered by a programmer device that sends required provisioning parameters via 52 * nfc. For an example of a programmer app see: 53 * com.example.android.apis.app.DeviceProvisioningProgrammerSample. 54 * </p> 55 * 56 * <p> 57 * In the unlikely case that this activity is killed the whole provisioning process so far is 58 * repeated. We made sure that all tasks can be done twice without causing any problems. 59 * </p> 60 */ 61 public class DeviceOwnerProvisioningActivity extends SetupLayoutActivity { 62 private static final boolean DEBUG = false; // To control logging. 63 64 private static final String KEY_CANCEL_DIALOG_SHOWN = "cancel_dialog_shown"; 65 private static final String KEY_PENDING_INTENTS = "pending_intents"; 66 67 private BroadcastReceiver mServiceMessageReceiver; 68 private TextView mProgressTextView; 69 70 private ProvisioningParams mParams; 71 72 // Indicates that the cancel dialog is shown. 73 private boolean mCancelDialogShown = false; 74 75 // List of intents received while cancel dialog is shown. 76 private ArrayList<Intent> mPendingProvisioningIntents = new ArrayList<Intent>(); 77 78 @Override onCreate(Bundle savedInstanceState)79 public void onCreate(Bundle savedInstanceState) { 80 super.onCreate(savedInstanceState); 81 if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONCREATE"); 82 83 if (savedInstanceState != null) { 84 mCancelDialogShown = savedInstanceState.getBoolean(KEY_CANCEL_DIALOG_SHOWN, false); 85 mPendingProvisioningIntents = savedInstanceState 86 .getParcelableArrayList(KEY_PENDING_INTENTS); 87 } 88 89 // Setup the UI. 90 initializeLayoutParams(R.layout.progress, R.string.setup_work_device, true); 91 configureNavigationButtons(NEXT_BUTTON_EMPTY_LABEL, View.INVISIBLE, View.VISIBLE); 92 setTitle(R.string.setup_device_progress); 93 94 mProgressTextView = (TextView) findViewById(R.id.prog_text); 95 if (mCancelDialogShown) showCancelResetDialog(); 96 97 // Setup broadcast receiver for feedback from service. 98 mServiceMessageReceiver = new ServiceMessageReceiver(); 99 IntentFilter filter = new IntentFilter(); 100 filter.addAction(DeviceOwnerProvisioningService.ACTION_PROVISIONING_SUCCESS); 101 filter.addAction(DeviceOwnerProvisioningService.ACTION_PROVISIONING_ERROR); 102 filter.addAction(DeviceOwnerProvisioningService.ACTION_PROGRESS_UPDATE); 103 LocalBroadcastManager.getInstance(this).registerReceiver(mServiceMessageReceiver, filter); 104 105 // Load the ProvisioningParams (from message in Intent). 106 mParams = (ProvisioningParams) getIntent().getParcelableExtra( 107 ProvisioningParams.EXTRA_PROVISIONING_PARAMS); 108 if (mParams != null) { 109 maybeSetLogoAndMainColor(mParams.mainColor); 110 } 111 startDeviceOwnerProvisioningService(); 112 } 113 startDeviceOwnerProvisioningService()114 private void startDeviceOwnerProvisioningService() { 115 Intent intent = new Intent(this, DeviceOwnerProvisioningService.class); 116 intent.putExtras(getIntent()); 117 startService(intent); 118 } 119 120 class ServiceMessageReceiver extends BroadcastReceiver 121 { 122 @Override onReceive(Context context, Intent intent)123 public void onReceive(Context context, Intent intent) 124 { 125 if (mCancelDialogShown) { 126 127 // Postpone handling the intent. 128 mPendingProvisioningIntents.add(intent); 129 return; 130 } 131 handleProvisioningIntent(intent); 132 } 133 } 134 handleProvisioningIntent(Intent intent)135 private void handleProvisioningIntent(Intent intent) { 136 String action = intent.getAction(); 137 if (action.equals(DeviceOwnerProvisioningService.ACTION_PROVISIONING_SUCCESS)) { 138 if (DEBUG) ProvisionLogger.logd("Successfully provisioned"); 139 onProvisioningSuccess(); 140 } else if (action.equals(DeviceOwnerProvisioningService.ACTION_PROVISIONING_ERROR)) { 141 int errorMessageId = intent.getIntExtra( 142 DeviceOwnerProvisioningService.EXTRA_USER_VISIBLE_ERROR_ID_KEY, 143 R.string.device_owner_error_general); 144 boolean factoryResetRequired = intent.getBooleanExtra( 145 DeviceOwnerProvisioningService.EXTRA_FACTORY_RESET_REQUIRED, 146 true); 147 148 if (DEBUG) { 149 ProvisionLogger.logd("Error reported with code " 150 + getResources().getString(errorMessageId)); 151 } 152 error(errorMessageId, factoryResetRequired); 153 } else if (action.equals(DeviceOwnerProvisioningService.ACTION_PROGRESS_UPDATE)) { 154 int progressMessage = intent.getIntExtra( 155 DeviceOwnerProvisioningService.EXTRA_PROGRESS_MESSAGE_ID_KEY, -1); 156 if (DEBUG) { 157 ProvisionLogger.logd("Progress update reported with code " 158 + getResources().getString(progressMessage)); 159 } 160 if (progressMessage >= 0) { 161 progressUpdate(progressMessage); 162 } 163 } 164 } 165 166 onProvisioningSuccess()167 private void onProvisioningSuccess() { 168 stopService(new Intent(this, DeviceOwnerProvisioningService.class)); 169 // Note: the DeviceOwnerProvisioningService will stop itself. 170 setResult(Activity.RESULT_OK); 171 finish(); 172 } 173 174 @Override onBackPressed()175 public void onBackPressed() { 176 if (mCancelDialogShown) { 177 return; 178 } 179 180 mCancelDialogShown = true; 181 showCancelResetDialog(); 182 } 183 showCancelResetDialog()184 private void showCancelResetDialog() { 185 new AlertDialog.Builder(DeviceOwnerProvisioningActivity.this) 186 .setCancelable(false) 187 .setMessage(R.string.device_owner_cancel_message) 188 .setNegativeButton(R.string.device_owner_cancel_cancel, 189 new DialogInterface.OnClickListener() { 190 @Override 191 public void onClick(DialogInterface dialog, int id) { 192 dialog.dismiss(); 193 handlePendingIntents(); 194 mCancelDialogShown = false; 195 } 196 }) 197 .setPositiveButton(R.string.device_owner_error_reset, 198 new DialogInterface.OnClickListener() { 199 @Override 200 public void onClick(DialogInterface dialog, int id) { 201 dialog.dismiss(); 202 203 // Factory reset the device. 204 mUtils.sendFactoryResetBroadcast( 205 DeviceOwnerProvisioningActivity.this, 206 "DeviceOwnerProvisioningActivity.showCancelResetDialog()"); 207 } 208 }) 209 .show(); 210 } 211 handlePendingIntents()212 private void handlePendingIntents() { 213 for (Intent intent : mPendingProvisioningIntents) { 214 if (DEBUG) ProvisionLogger.logd("Handling pending intent " + intent.getAction()); 215 handleProvisioningIntent(intent); 216 } 217 mPendingProvisioningIntents.clear(); 218 } 219 progressUpdate(int progressMessage)220 private void progressUpdate(int progressMessage) { 221 mProgressTextView.setText(progressMessage); 222 mProgressTextView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); 223 } 224 error(int dialogMessage, boolean resetRequired)225 private void error(int dialogMessage, boolean resetRequired) { 226 AlertDialog.Builder alertBuilder = new AlertDialog.Builder(this) 227 .setTitle(R.string.provisioning_error_title) 228 .setMessage(dialogMessage) 229 .setCancelable(false); 230 if (resetRequired) { 231 alertBuilder.setPositiveButton(R.string.device_owner_error_reset, 232 new DialogInterface.OnClickListener() { 233 @Override 234 public void onClick(DialogInterface dialog,int id) { 235 dialog.dismiss(); 236 237 // Factory reset the device. 238 Intent intent = new Intent(Intent.ACTION_MASTER_CLEAR); 239 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 240 intent.putExtra(Intent.EXTRA_REASON, 241 "DeviceOwnerProvisioningActivity.error()"); 242 sendBroadcast(intent); 243 stopService(new Intent(DeviceOwnerProvisioningActivity.this, 244 DeviceOwnerProvisioningService.class)); 245 setResult(RESULT_CANCELED); 246 finish(); 247 } 248 }); 249 } else { 250 alertBuilder.setPositiveButton(R.string.device_owner_error_ok, 251 new DialogInterface.OnClickListener() { 252 @Override 253 public void onClick(DialogInterface dialog,int id) { 254 dialog.dismiss(); 255 256 // Close activity. 257 stopService(new Intent(DeviceOwnerProvisioningActivity.this, 258 DeviceOwnerProvisioningService.class)); 259 setResult(RESULT_CANCELED); 260 finish(); 261 } 262 }); 263 } 264 alertBuilder.show(); 265 } 266 267 @Override onSaveInstanceState(Bundle outState)268 protected void onSaveInstanceState(Bundle outState) { 269 outState.putBoolean(KEY_CANCEL_DIALOG_SHOWN, mCancelDialogShown); 270 outState.putParcelableArrayList(KEY_PENDING_INTENTS, mPendingProvisioningIntents); 271 } 272 273 @Override onDestroy()274 public void onDestroy() { 275 if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONDESTROY"); 276 if (mServiceMessageReceiver != null) { 277 LocalBroadcastManager.getInstance(this).unregisterReceiver(mServiceMessageReceiver); 278 mServiceMessageReceiver = null; 279 } 280 super.onDestroy(); 281 } 282 283 @Override onRestart()284 protected void onRestart() { 285 if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONRESTART"); 286 super.onRestart(); 287 } 288 289 @Override onResume()290 protected void onResume() { 291 if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONRESUME"); 292 super.onResume(); 293 } 294 295 @Override onPause()296 protected void onPause() { 297 if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONPAUSE"); 298 super.onPause(); 299 } 300 301 @Override onStop()302 protected void onStop() { 303 if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONSTOP"); 304 super.onStop(); 305 } 306 } 307