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 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 setTitle(R.string.setup_device_progress); 92 93 mProgressTextView = (TextView) findViewById(R.id.prog_text); 94 if (mCancelDialogShown) showCancelResetDialog(); 95 96 // Setup broadcast receiver for feedback from service. 97 mServiceMessageReceiver = new ServiceMessageReceiver(); 98 IntentFilter filter = new IntentFilter(); 99 filter.addAction(DeviceOwnerProvisioningService.ACTION_PROVISIONING_SUCCESS); 100 filter.addAction(DeviceOwnerProvisioningService.ACTION_PROVISIONING_ERROR); 101 filter.addAction(DeviceOwnerProvisioningService.ACTION_PROGRESS_UPDATE); 102 LocalBroadcastManager.getInstance(this).registerReceiver(mServiceMessageReceiver, filter); 103 104 // Load the ProvisioningParams (from message in Intent). 105 mParams = (ProvisioningParams) getIntent().getParcelableExtra( 106 ProvisioningParams.EXTRA_PROVISIONING_PARAMS); 107 if (mParams != null) { 108 maybeSetLogoAndMainColor(mParams.mainColor); 109 } 110 startDeviceOwnerProvisioningService(); 111 } 112 113 private void startDeviceOwnerProvisioningService() { 114 Intent intent = new Intent(this, DeviceOwnerProvisioningService.class); 115 intent.putExtras(getIntent()); 116 startService(intent); 117 } 118 119 class ServiceMessageReceiver extends BroadcastReceiver 120 { 121 @Override 122 public void onReceive(Context context, Intent intent) 123 { 124 if (mCancelDialogShown) { 125 126 // Postpone handling the intent. 127 mPendingProvisioningIntents.add(intent); 128 return; 129 } 130 handleProvisioningIntent(intent); 131 } 132 } 133 134 private void handleProvisioningIntent(Intent intent) { 135 String action = intent.getAction(); 136 if (action.equals(DeviceOwnerProvisioningService.ACTION_PROVISIONING_SUCCESS)) { 137 if (DEBUG) ProvisionLogger.logd("Successfully provisioned"); 138 onProvisioningSuccess(); 139 } else if (action.equals(DeviceOwnerProvisioningService.ACTION_PROVISIONING_ERROR)) { 140 int errorMessageId = intent.getIntExtra( 141 DeviceOwnerProvisioningService.EXTRA_USER_VISIBLE_ERROR_ID_KEY, 142 R.string.device_owner_error_general); 143 boolean factoryResetRequired = intent.getBooleanExtra( 144 DeviceOwnerProvisioningService.EXTRA_FACTORY_RESET_REQUIRED, 145 true); 146 147 if (DEBUG) { 148 ProvisionLogger.logd("Error reported with code " 149 + getResources().getString(errorMessageId)); 150 } 151 error(errorMessageId, factoryResetRequired); 152 } else if (action.equals(DeviceOwnerProvisioningService.ACTION_PROGRESS_UPDATE)) { 153 int progressMessage = intent.getIntExtra( 154 DeviceOwnerProvisioningService.EXTRA_PROGRESS_MESSAGE_ID_KEY, -1); 155 if (DEBUG) { 156 ProvisionLogger.logd("Progress update reported with code " 157 + getResources().getString(progressMessage)); 158 } 159 if (progressMessage >= 0) { 160 progressUpdate(progressMessage); 161 } 162 } 163 } 164 165 166 private void onProvisioningSuccess() { 167 stopService(new Intent(this, DeviceOwnerProvisioningService.class)); 168 // Note: the DeviceOwnerProvisioningService will stop itself. 169 setResult(Activity.RESULT_OK); 170 finish(); 171 } 172 173 @Override 174 public void onBackPressed() { 175 if (mCancelDialogShown) { 176 return; 177 } 178 179 mCancelDialogShown = true; 180 showCancelResetDialog(); 181 } 182 183 private void showCancelResetDialog() { 184 new AlertDialog.Builder(DeviceOwnerProvisioningActivity.this) 185 .setCancelable(false) 186 .setMessage(R.string.device_owner_cancel_message) 187 .setNegativeButton(R.string.device_owner_cancel_cancel, 188 new DialogInterface.OnClickListener() { 189 @Override 190 public void onClick(DialogInterface dialog, int id) { 191 dialog.dismiss(); 192 handlePendingIntents(); 193 mCancelDialogShown = false; 194 } 195 }) 196 .setPositiveButton(R.string.device_owner_error_reset, 197 new DialogInterface.OnClickListener() { 198 @Override 199 public void onClick(DialogInterface dialog, int id) { 200 dialog.dismiss(); 201 202 // Factory reset the device. 203 mUtils.sendFactoryResetBroadcast( 204 DeviceOwnerProvisioningActivity.this, 205 "DeviceOwnerProvisioningActivity.showCancelResetDialog()"); 206 } 207 }) 208 .show(); 209 } 210 211 private void handlePendingIntents() { 212 for (Intent intent : mPendingProvisioningIntents) { 213 if (DEBUG) ProvisionLogger.logd("Handling pending intent " + intent.getAction()); 214 handleProvisioningIntent(intent); 215 } 216 mPendingProvisioningIntents.clear(); 217 } 218 219 private void progressUpdate(int progressMessage) { 220 mProgressTextView.setText(progressMessage); 221 mProgressTextView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); 222 } 223 224 private void error(int dialogMessage, boolean resetRequired) { 225 AlertDialog.Builder alertBuilder = new AlertDialog.Builder(this) 226 .setTitle(R.string.provisioning_error_title) 227 .setMessage(dialogMessage) 228 .setCancelable(false); 229 if (resetRequired) { 230 alertBuilder.setPositiveButton(R.string.device_owner_error_reset, 231 new DialogInterface.OnClickListener() { 232 @Override 233 public void onClick(DialogInterface dialog,int id) { 234 dialog.dismiss(); 235 236 // Factory reset the device. 237 Intent intent = new Intent(Intent.ACTION_MASTER_CLEAR); 238 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 239 intent.putExtra(Intent.EXTRA_REASON, 240 "DeviceOwnerProvisioningActivity.error()"); 241 sendBroadcast(intent); 242 stopService(new Intent(DeviceOwnerProvisioningActivity.this, 243 DeviceOwnerProvisioningService.class)); 244 setResult(RESULT_CANCELED); 245 finish(); 246 } 247 }); 248 } else { 249 alertBuilder.setPositiveButton(R.string.device_owner_error_ok, 250 new DialogInterface.OnClickListener() { 251 @Override 252 public void onClick(DialogInterface dialog,int id) { 253 dialog.dismiss(); 254 255 // Close activity. 256 stopService(new Intent(DeviceOwnerProvisioningActivity.this, 257 DeviceOwnerProvisioningService.class)); 258 setResult(RESULT_CANCELED); 259 finish(); 260 } 261 }); 262 } 263 alertBuilder.show(); 264 } 265 266 @Override 267 protected void onSaveInstanceState(Bundle outState) { 268 outState.putBoolean(KEY_CANCEL_DIALOG_SHOWN, mCancelDialogShown); 269 outState.putParcelableArrayList(KEY_PENDING_INTENTS, mPendingProvisioningIntents); 270 } 271 272 @Override 273 public void onDestroy() { 274 if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONDESTROY"); 275 if (mServiceMessageReceiver != null) { 276 LocalBroadcastManager.getInstance(this).unregisterReceiver(mServiceMessageReceiver); 277 mServiceMessageReceiver = null; 278 } 279 super.onDestroy(); 280 } 281 282 @Override 283 protected void onRestart() { 284 if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONRESTART"); 285 super.onRestart(); 286 } 287 288 @Override 289 protected void onResume() { 290 if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONRESUME"); 291 super.onResume(); 292 } 293 294 @Override 295 protected void onPause() { 296 if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONPAUSE"); 297 super.onPause(); 298 } 299 300 @Override 301 protected void onStop() { 302 if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONSTOP"); 303 super.onStop(); 304 } 305 } 306