• 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 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         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 
startDeviceOwnerProvisioningService()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
onReceive(Context context, Intent intent)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 
handleProvisioningIntent(Intent intent)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 
onProvisioningSuccess()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
onBackPressed()174     public void onBackPressed() {
175         if (mCancelDialogShown) {
176             return;
177         }
178 
179         mCancelDialogShown = true;
180         showCancelResetDialog();
181     }
182 
showCancelResetDialog()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 
handlePendingIntents()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 
progressUpdate(int progressMessage)219     private void progressUpdate(int progressMessage) {
220         mProgressTextView.setText(progressMessage);
221         mProgressTextView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
222     }
223 
error(int dialogMessage, boolean resetRequired)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
onSaveInstanceState(Bundle outState)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
onDestroy()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
onRestart()283     protected void onRestart() {
284         if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONRESTART");
285         super.onRestart();
286     }
287 
288     @Override
onResume()289     protected void onResume() {
290         if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONRESUME");
291         super.onResume();
292     }
293 
294     @Override
onPause()295     protected void onPause() {
296         if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONPAUSE");
297         super.onPause();
298     }
299 
300     @Override
onStop()301     protected void onStop() {
302         if (DEBUG) ProvisionLogger.logd("Device owner provisioning activity ONSTOP");
303         super.onStop();
304     }
305 }
306