Merge "Make Bluetooth checkin first packet." into mnc-dev
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index b13effb..0f06cc5 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -48,8 +48,7 @@
     <string name="managed_provisioning_error_text" msgid="7063621174570680890">"Couldn\'t set up your work profile. Contact your IT department or try again later."</string>
     <string name="managed_provisioning_not_supported" msgid="6582227325719911795">"Your device doesn\'t support work profiles"</string>
     <string name="user_is_not_owner" msgid="6193230832887977927">"Work profiles need to be set up by the primary user of the device"</string>
-    <!-- no translation found for device_owner_exists (8020080296133337023) -->
-    <skip />
+    <string name="device_owner_exists" msgid="8020080296133337023">"Work profiles can not be set up on a managed device"</string>
     <string name="maximum_user_limit_reached" msgid="7267689354022124652">"Work profile could not be created as the maximum number of users on your device has been reached. Please remove at least one user and try again."</string>
     <string name="managed_provisioning_not_supported_by_launcher" msgid="8710138269807942163">"Your work profile isn\'t supported by this launcher app. You\'ll need to switch to a launcher that\'s compatible."</string>
     <string name="cancel_provisioning" msgid="3408069559452653724">"Cancel"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index b13effb..0f06cc5 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -48,8 +48,7 @@
     <string name="managed_provisioning_error_text" msgid="7063621174570680890">"Couldn\'t set up your work profile. Contact your IT department or try again later."</string>
     <string name="managed_provisioning_not_supported" msgid="6582227325719911795">"Your device doesn\'t support work profiles"</string>
     <string name="user_is_not_owner" msgid="6193230832887977927">"Work profiles need to be set up by the primary user of the device"</string>
-    <!-- no translation found for device_owner_exists (8020080296133337023) -->
-    <skip />
+    <string name="device_owner_exists" msgid="8020080296133337023">"Work profiles can not be set up on a managed device"</string>
     <string name="maximum_user_limit_reached" msgid="7267689354022124652">"Work profile could not be created as the maximum number of users on your device has been reached. Please remove at least one user and try again."</string>
     <string name="managed_provisioning_not_supported_by_launcher" msgid="8710138269807942163">"Your work profile isn\'t supported by this launcher app. You\'ll need to switch to a launcher that\'s compatible."</string>
     <string name="cancel_provisioning" msgid="3408069559452653724">"Cancel"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index b13effb..0f06cc5 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -48,8 +48,7 @@
     <string name="managed_provisioning_error_text" msgid="7063621174570680890">"Couldn\'t set up your work profile. Contact your IT department or try again later."</string>
     <string name="managed_provisioning_not_supported" msgid="6582227325719911795">"Your device doesn\'t support work profiles"</string>
     <string name="user_is_not_owner" msgid="6193230832887977927">"Work profiles need to be set up by the primary user of the device"</string>
-    <!-- no translation found for device_owner_exists (8020080296133337023) -->
-    <skip />
+    <string name="device_owner_exists" msgid="8020080296133337023">"Work profiles can not be set up on a managed device"</string>
     <string name="maximum_user_limit_reached" msgid="7267689354022124652">"Work profile could not be created as the maximum number of users on your device has been reached. Please remove at least one user and try again."</string>
     <string name="managed_provisioning_not_supported_by_launcher" msgid="8710138269807942163">"Your work profile isn\'t supported by this launcher app. You\'ll need to switch to a launcher that\'s compatible."</string>
     <string name="cancel_provisioning" msgid="3408069559452653724">"Cancel"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 0a0e8f6..45d7d9e 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -48,8 +48,7 @@
     <string name="managed_provisioning_error_text" msgid="7063621174570680890">"نمایه کاریتان تنظیم نشد. با بخش فناوری اطلاعات مربوط به خودتان تماس بگیرید یا دوباره بعداً امتحان کنید."</string>
     <string name="managed_provisioning_not_supported" msgid="6582227325719911795">"دستگاهتان از نمایه‌های کاری پشتیبانی نمی‌کند"</string>
     <string name="user_is_not_owner" msgid="6193230832887977927">"کاربر اصلی دستگاه باید نمایه‌های کاری را تنظیم کند"</string>
-    <!-- no translation found for device_owner_exists (8020080296133337023) -->
-    <skip />
+    <string name="device_owner_exists" msgid="8020080296133337023">"نمی‌توان نمایه‌های کاری را در دستگاه مدیریت شده تنظیم کرد"</string>
     <string name="maximum_user_limit_reached" msgid="7267689354022124652">"چون تعداد کاربران در دستگاهتان به حداکثر رسیده است، نمی‌توان نمایه کاری ایجاد کرد. لطفاً حداقل یک کاربر را بردارید و دوباره امتحان کنید."</string>
     <string name="managed_provisioning_not_supported_by_launcher" msgid="8710138269807942163">"نمایه کاریتان از سوی این برنامه راه‌انداز پشتیبانی نمی‌شود. باید راه‌انداز خودتان را به راه‌اندازی تغییر دهید که سازگار است."</string>
     <string name="cancel_provisioning" msgid="3408069559452653724">"لغو"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 9496edd..dcb8d32 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -48,8 +48,7 @@
     <string name="managed_provisioning_error_text" msgid="7063621174570680890">"Tidak dapat menyiapkan profil kerja Anda. Hubungi departemen TI atau coba lagi nanti."</string>
     <string name="managed_provisioning_not_supported" msgid="6582227325719911795">"Perangkat Anda tidak mendukung profil kerja"</string>
     <string name="user_is_not_owner" msgid="6193230832887977927">"Profil kerja harus disiapkan oleh pengguna utama perangkat"</string>
-    <!-- no translation found for device_owner_exists (8020080296133337023) -->
-    <skip />
+    <string name="device_owner_exists" msgid="8020080296133337023">"Profil kerja tidak dapat disiapkan di perangkat yang dikelola"</string>
     <string name="maximum_user_limit_reached" msgid="7267689354022124652">"Profil kerja tidak dapat dibuat karena jumlah maksimum pengguna di perangkat telah tercapai. Hapus setidaknya satu pengguna dan coba lagi."</string>
     <string name="managed_provisioning_not_supported_by_launcher" msgid="8710138269807942163">"Profil kerja Anda tidak didukung oleh peluncur aplikasi ini. Anda perlu beralih ke peluncur yang kompatibel."</string>
     <string name="cancel_provisioning" msgid="3408069559452653724">"Batal"</string>
diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml
index 5cbcf20..eb83540 100644
--- a/res/values-mn-rMN/strings.xml
+++ b/res/values-mn-rMN/strings.xml
@@ -48,8 +48,7 @@
     <string name="managed_provisioning_error_text" msgid="7063621174570680890">"Таны ажлын профайлыг тохируулж чадсангүй. МТ хэлтэстэй холбогдох буюу дараа дахин оролдоно уу."</string>
     <string name="managed_provisioning_not_supported" msgid="6582227325719911795">"Таны төхөөрөмж ажлын профайлыг дэмждэггүй"</string>
     <string name="user_is_not_owner" msgid="6193230832887977927">"Ажлын танилцуулгыг төхөөрөмжийн үндсэн хэрэглэгч тохируулах шаардлагатай"</string>
-    <!-- no translation found for device_owner_exists (8020080296133337023) -->
-    <skip />
+    <string name="device_owner_exists" msgid="8020080296133337023">"Ажлын профайлыг хяналттай төхөөрөмж дээр суулгаж болохгүй"</string>
     <string name="maximum_user_limit_reached" msgid="7267689354022124652">"Ажлын профайл үүсгэх боломжгүй байна учир нь энэ төхөөрөмж дээрх хэрэглэгчдийн дээд хязгаарт хүрсэн байна. Хамгийн багадаа нэг хэрэглэгч усгасны дараагаар дахин хандана уу."</string>
     <string name="managed_provisioning_not_supported_by_launcher" msgid="8710138269807942163">"Таны ажлын профайл энэ эхлүүлэгч апп-аар дэмжигдээгүй байна. Таарах эхлүүлэгч апп-р солино уу."</string>
     <string name="cancel_provisioning" msgid="3408069559452653724">"Цуцлах"</string>
diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml
index 91eb392..26d854b 100644
--- a/res/values-my-rMM/strings.xml
+++ b/res/values-my-rMM/strings.xml
@@ -48,8 +48,7 @@
     <string name="managed_provisioning_error_text" msgid="7063621174570680890">"သင်၏ အလုပ် ပရိုဖိုင်ကို စဖွင့်စီစဉ် မရခဲ့ပါ။ သင်၏ IT ဌာနကို ဆက်သွယ်ပါ သို့မဟုတ် နောက်မှာ ထပ်စမ်းပါ။"</string>
     <string name="managed_provisioning_not_supported" msgid="6582227325719911795">"သင့်စက်က အလုပ်ပရိုဖိုင်များကို မပံ့ပိုးပါ။"</string>
     <string name="user_is_not_owner" msgid="6193230832887977927">"စက်ပစ္စည်း၏ ပင်မအသုံးပြုသူမှ အလုပ်ပရိုဖိုင်အား သက်မှတ်ရပါမည်။"</string>
-    <!-- no translation found for device_owner_exists (8020080296133337023) -->
-    <skip />
+    <string name="device_owner_exists" msgid="8020080296133337023">"စီမံထားသော စက်ကိရိယာတစ်ခုတွင် အလုပ်ပရိုဖိုင်များ ပြင်ဆင်သတ်မှတ်နိုင်ခြင်း မရှိပါ"</string>
     <string name="maximum_user_limit_reached" msgid="7267689354022124652">"သင့်စပစ္စည်းတွင် အသုံးပြုသူအရေအတွက် အများဆုံးသို့ရောက်နေပြီဖြစ်သောကြောင့် အလုပ်ပရိုဖိုင် ဖန်တီး၍မရနိုင်ပါ။ ကျေးဇူးပြု၍ အနည်းဆုံး အသုံးပြုသူ တစ်ယောက်ကို ဖယ်ရှားကာ ထပ်မံကြိုးစားပါ။"</string>
     <string name="managed_provisioning_not_supported_by_launcher" msgid="8710138269807942163">"သင့် အလုပ် ပရိုဖိုင်ကို ဒီဖွင့်တင်ရေး appမှ မပံ့ပိုးပါ။ သင်သည် လိုက်ဖက်သည့် ဖွင့်တင်ရေးစက်သို့ ပြောင်းရန် လိုသည်။"</string>
     <string name="cancel_provisioning" msgid="3408069559452653724">"ထားတော့"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index dc2e470..97a828e 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -48,8 +48,7 @@
     <string name="managed_provisioning_error_text" msgid="7063621174570680890">"Delovnega profila ni bilo mogoče nastaviti. Obrnite se na oddelek za IT ali poskusite znova pozneje."</string>
     <string name="managed_provisioning_not_supported" msgid="6582227325719911795">"Naprava ne podpira delovnih profilov"</string>
     <string name="user_is_not_owner" msgid="6193230832887977927">"Delovne profile mora nastaviti primarni uporabnik naprave"</string>
-    <!-- no translation found for device_owner_exists (8020080296133337023) -->
-    <skip />
+    <string name="device_owner_exists" msgid="8020080296133337023">"Delovnih profilov ni mogoče nastaviti v upravljani napravi"</string>
     <string name="maximum_user_limit_reached" msgid="7267689354022124652">"Delovnega profila zaradi doseženega največjega dovoljenega števila uporabnikov v napravi ni bilo mogoče ustvariti. Odstranite vsaj enega uporabnika in poskusite znova."</string>
     <string name="managed_provisioning_not_supported_by_launcher" msgid="8710138269807942163">"Ta aplikacija zaganjalnika ne podpira vašega delovnega profila. Uporabiti morate združljiv zaganjalnik."</string>
     <string name="cancel_provisioning" msgid="3408069559452653724">"Prekliči"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 27e770a..d0fac9f 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -48,8 +48,7 @@
     <string name="managed_provisioning_error_text" msgid="7063621174570680890">"Hindi ma-set up ang iyong profile sa trabaho. Makipag-ugnayan sa iyong IT department o subukang muli sa ibang pagkakataon."</string>
     <string name="managed_provisioning_not_supported" msgid="6582227325719911795">"Hindi sinusuportahan ng iyong device ang mga profile sa trabaho"</string>
     <string name="user_is_not_owner" msgid="6193230832887977927">"Kailangang i-set up ng pangunahing user ng device ang mga profile sa trabaho"</string>
-    <!-- no translation found for device_owner_exists (8020080296133337023) -->
-    <skip />
+    <string name="device_owner_exists" msgid="8020080296133337023">"Hindi maaaring mag-set up ng mga profile sa trabaho sa pinamamahalaang device"</string>
     <string name="maximum_user_limit_reached" msgid="7267689354022124652">"Hindi magawa ang profile sa trabaho dahil naabot na ang maximum na bilang ng mga user sa iyong device. Mangyaring mag-alis ng kahit isang user lang at subukang muli."</string>
     <string name="managed_provisioning_not_supported_by_launcher" msgid="8710138269807942163">"Ang iyong profile sa trabaho ay hindi sinusuportahan ng launcher app na ito. Kakailanganin mong lumipat sa isang launcher na tugma dito."</string>
     <string name="cancel_provisioning" msgid="3408069559452653724">"Kanselahin"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index b820995..61b11a2 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -48,8 +48,7 @@
     <string name="managed_provisioning_error_text" msgid="7063621174570680890">"Ayikwazanga ukusehta iphrofayela yakho yomsebenzi. Xhumana nomnyango wakho we-IT noma zama futhi emuva kwesikhathi."</string>
     <string name="managed_provisioning_not_supported" msgid="6582227325719911795">"Idivayisi yakho ayisekeli amaphrofayela womsebenzi"</string>
     <string name="user_is_not_owner" msgid="6193230832887977927">"Amaphrofayela womsebenzi adinga ukusethwa umsebenzisi oyinhloko wedivayisi"</string>
-    <!-- no translation found for device_owner_exists (8020080296133337023) -->
-    <skip />
+    <string name="device_owner_exists" msgid="8020080296133337023">"Amaphrofayela wokusebenza awakwazi ukusethwa kudivayisi ephethwe"</string>
     <string name="maximum_user_limit_reached" msgid="7267689354022124652">"Iphrofayela yomsebenzi ayikwazanga ukudalwa njengoba inombolo enkulu yabasebenzisi kudivayisi yakho ifinyelelwe. Sicela ukhiphe okungenani umsebenzisi oyedwa bese uyazama futhi."</string>
     <string name="managed_provisioning_not_supported_by_launcher" msgid="8710138269807942163">"Iphrofayela yakho yomsebenzi ayisekelwe yilesi siqalisi sohlelo lokusebenza. Kuzomele uguqulele kusiqalisi esihambisanayo."</string>
     <string name="cancel_provisioning" msgid="3408069559452653724">"Khansela"</string>
diff --git a/src/com/android/managedprovisioning/DeviceOwnerProvisioningActivity.java b/src/com/android/managedprovisioning/DeviceOwnerProvisioningActivity.java
index e74cb6c..eb7ef1f 100644
--- a/src/com/android/managedprovisioning/DeviceOwnerProvisioningActivity.java
+++ b/src/com/android/managedprovisioning/DeviceOwnerProvisioningActivity.java
@@ -162,7 +162,7 @@
         // Ask to encrypt the device before proceeding
         if (!(EncryptDeviceActivity.isDeviceEncrypted()
                         || SystemProperties.getBoolean("persist.sys.no_req_encrypt", false)
-                        || mParams.mSkipEncryption)) {
+                        || mParams.skipEncryption)) {
             requestEncryption(parser, mParams);
             finish();
             return;
@@ -170,8 +170,8 @@
         }
 
         // Have the user pick a wifi network if necessary.
-        if (!AddWifiNetworkTask.isConnectedToWifi(this) && TextUtils.isEmpty(mParams.mWifiSsid) &&
-                !mParams.mUseBluetoothProxy) {
+        if (!AddWifiNetworkTask.isConnectedToWifi(this) && TextUtils.isEmpty(mParams.wifiInfo.ssid)
+                && !mParams.bluetoothInfo.useProxy) {
             requestWifiPick();
             return;
             // Wait for onActivityResult.
@@ -181,7 +181,7 @@
     }
 
     private void showInterstitialAndProvision(final ProvisioningParams params) {
-        if (mUserConsented || params.mStartedByNfc || !Utils.isCurrentUserOwner()) {
+        if (mUserConsented || params.startedByNfc || !Utils.isCurrentUserOwner()) {
             startDeviceOwnerProvisioningService(params);
         } else {
             // Notify the user that the admin will have full control over the device,
@@ -254,14 +254,14 @@
 
 
     private void onProvisioningSuccess() {
-        if (mParams.mDeviceInitializerComponentName != null) {
+        if (mParams.deviceInitializerComponentName != null) {
             Intent result = new Intent(ACTION_READY_FOR_USER_INITIALIZATION);
-            result.setComponent(mParams.mDeviceInitializerComponentName);
+            result.setComponent(mParams.deviceInitializerComponentName);
             result.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES |
                     Intent.FLAG_RECEIVER_FOREGROUND);
-            if (mParams.mAdminExtrasBundle != null) {
+            if (mParams.adminExtrasBundle != null) {
                 result.putExtra(EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE,
-                        mParams.mAdminExtrasBundle);
+                        mParams.adminExtrasBundle);
             }
             List<ResolveInfo> matchingReceivers =
                     getPackageManager().queryBroadcastReceivers(result, 0);
diff --git a/src/com/android/managedprovisioning/DeviceOwnerProvisioningService.java b/src/com/android/managedprovisioning/DeviceOwnerProvisioningService.java
index d9ff8ee..a1611f2 100644
--- a/src/com/android/managedprovisioning/DeviceOwnerProvisioningService.java
+++ b/src/com/android/managedprovisioning/DeviceOwnerProvisioningService.java
@@ -28,8 +28,10 @@
 import android.os.IBinder;
 import android.os.UserHandle;
 import android.support.v4.content.LocalBroadcastManager;
+import android.text.TextUtils;
 
 import com.android.internal.app.LocalePicker;
+import com.android.managedprovisioning.ProvisioningParams.PackageDownloadInfo;
 import com.android.managedprovisioning.proxy.BluetoothConnectionService;
 import com.android.managedprovisioning.task.AddWifiNetworkTask;
 import com.android.managedprovisioning.task.BluetoothConnectTask;
@@ -55,6 +57,9 @@
 public class DeviceOwnerProvisioningService extends Service {
     private static final boolean DEBUG = false; // To control logging.
 
+    private static final String DEVICE_OWNER = "deviceOwner";
+    private static final String DEVICE_INITIALIZER = "deviceInitializer";
+
     /**
      * Intent action to activate the CDMA phone connection by OTASP.
      * This is not necessary for a GSM phone connection, which is activated automatically.
@@ -156,7 +161,8 @@
         if (DEBUG) ProvisionLogger.logd("Starting device owner provisioning");
 
         // Construct Tasks. Do not start them yet.
-        mBluetoothConnectTask = new BluetoothConnectTask(this, params,
+        mBluetoothConnectTask = new BluetoothConnectTask(this, params.bluetoothInfo,
+                !TextUtils.isEmpty(params.wifiInfo.ssid),
                 new BluetoothConnectTask.Callback() {
                         @Override
                         public void onSuccess() {
@@ -169,10 +175,8 @@
                         }
                 });
 
-        mAddWifiNetworkTask = new AddWifiNetworkTask(this, params.mWifiSsid,
-               params.mWifiHidden, params.mWifiSecurityType, params.mWifiPassword,
-               params.mWifiProxyHost, params.mWifiProxyPort, params.mWifiProxyBypassHosts,
-               params.mWifiPacUrl, new AddWifiNetworkTask.Callback() {
+        mAddWifiNetworkTask = new AddWifiNetworkTask(this, params.wifiInfo,
+                new AddWifiNetworkTask.Callback() {
                        @Override
                        public void onSuccess() {
                            progressUpdate(R.string.progress_wipe_frp);
@@ -186,7 +190,7 @@
                            }
                        });
 
-        mWipeResetProtectionTask = new WipeResetProtectionTask(this, params,
+        mWipeResetProtectionTask = new WipeResetProtectionTask(this, params.frpChallengeBundle,
                 new WipeResetProtectionTask.Callback() {
                     @Override
                     public void onSuccess() {
@@ -201,17 +205,20 @@
                     }
                 });
 
-
-        mDownloadPackageTask = new DownloadPackageTask(
-                this, params, new DownloadPackageTask.Callback() {
+        mDownloadPackageTask = new DownloadPackageTask(this,
+                new DownloadPackageTask.Callback() {
                         @Override
                         public void onSuccess() {
                             progressUpdate(R.string.progress_install);
-                            mInstallPackageTask.run(
+                            mInstallPackageTask.addInstallIfNecessary(
+                                    params.inferDeviceAdminPackageName(),
                                     mDownloadPackageTask.getDownloadedPackageLocation(
-                                            DownloadPackageTask.DEVICE_OWNER),
+                                            DEVICE_OWNER));
+                            mInstallPackageTask.addInstallIfNecessary(
+                                    params.getDeviceInitializerPackageName(),
                                     mDownloadPackageTask.getDownloadedPackageLocation(
-                                            DownloadPackageTask.INITIALIZER));
+                                            DEVICE_INITIALIZER));
+                            mInstallPackageTask.run();
                         }
 
                         @Override
@@ -231,8 +238,14 @@
                         }
                     });
 
-        mInstallPackageTask = new InstallPackageTask(
-                this, params, new InstallPackageTask.Callback() {
+        // Add packages to download to the DownloadPackageTask.
+        mDownloadPackageTask.addDownloadIfNecessary(params.inferDeviceAdminPackageName(),
+                params.deviceAdminDownloadInfo, DEVICE_OWNER);
+        mDownloadPackageTask.addDownloadIfNecessary(params.getDeviceInitializerPackageName(),
+                params.deviceInitializerDownloadInfo, DEVICE_INITIALIZER);
+
+        mInstallPackageTask = new InstallPackageTask(this,
+                new InstallPackageTask.Callback() {
                     @Override
                     public void onSuccess() {
                         progressUpdate(R.string.progress_set_owner);
@@ -268,9 +281,10 @@
                         statusUpdate(DeviceInitializerStatus.STATUS_ERROR_INSTALL_PACKAGE);
                     }
                 });
+
         mSetDevicePolicyTask = new SetDevicePolicyTask(this,
                 getResources().getString(R.string.default_owned_device_username),
-                params.mDeviceInitializerComponentName,
+                params.deviceInitializerComponentName,
                 getResources().getString(R.string.default_owned_device_username),
                 new SetDevicePolicyTask.Callback() {
                     @Override
@@ -298,7 +312,7 @@
         mDeleteNonRequiredAppsTask = new DeleteNonRequiredAppsTask(
                 this, params.inferDeviceAdminPackageName(), R.array.required_apps_managed_device,
                 R.array.vendor_required_apps_managed_device, true /* creating new profile */,
-                UserHandle.USER_OWNER, params.mLeaveAllSystemAppsEnabled,
+                UserHandle.USER_OWNER, params.leaveAllSystemAppsEnabled,
                 new DeleteNonRequiredAppsTask.Callback() {
                     @Override
                     public void onSuccess() {
@@ -382,8 +396,8 @@
     }
 
     private void initializeProvisioningEnvironment(ProvisioningParams params) {
-        setTimeAndTimezone(params.mTimeZone, params.mLocalTime);
-        setLocale(params.mLocale);
+        setTimeAndTimezone(params.timeZone, params.localTime);
+        setLocale(params.locale);
 
         // Start CDMA activation to enable phone calls.
         final Intent intent = new Intent(ACTION_PERFORM_CDMA_PROVISIONING);
diff --git a/src/com/android/managedprovisioning/HomeReceiverActivity.java b/src/com/android/managedprovisioning/HomeReceiverActivity.java
index 9ce5876..8c2e773 100644
--- a/src/com/android/managedprovisioning/HomeReceiverActivity.java
+++ b/src/com/android/managedprovisioning/HomeReceiverActivity.java
@@ -72,11 +72,11 @@
         }
 
         // Disable the Device Initializer component, if it exists, in case it did not do so itself.
-        if(mParams.mDeviceInitializerComponentName != null) {
+        if(mParams.deviceInitializerComponentName != null) {
             DevicePolicyManager devicePolicyManager =
                     (DevicePolicyManager) getSystemService(DEVICE_POLICY_SERVICE);
-            devicePolicyManager.removeActiveAdmin(mParams.mDeviceInitializerComponentName);
-            disableComponent(mParams.mDeviceInitializerComponentName);
+            devicePolicyManager.removeActiveAdmin(mParams.deviceInitializerComponentName);
+            disableComponent(mParams.deviceInitializerComponentName);
         }
 
         // Finalizing provisioning: send complete intent to mdm.
@@ -89,9 +89,9 @@
         }
         result.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES |
                 Intent.FLAG_RECEIVER_FOREGROUND);
-        if (mParams.mAdminExtrasBundle != null) {
+        if (mParams.adminExtrasBundle != null) {
             result.putExtra(EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE,
-                    mParams.mAdminExtrasBundle);
+                    mParams.adminExtrasBundle);
         }
         sendBroadcast(result);
 
diff --git a/src/com/android/managedprovisioning/MessageParser.java b/src/com/android/managedprovisioning/MessageParser.java
index af6d136..7d141f6 100644
--- a/src/com/android/managedprovisioning/MessageParser.java
+++ b/src/com/android/managedprovisioning/MessageParser.java
@@ -185,57 +185,54 @@
     };
 
     public void addProvisioningParamsToBundle(Bundle bundle, ProvisioningParams params) {
-        bundle.putString(EXTRA_PROVISIONING_TIME_ZONE, params.mTimeZone);
-        bundle.putString(EXTRA_PROVISIONING_LOCALE, params.getLocaleAsString());
-        bundle.putString(EXTRA_PROVISIONING_WIFI_SSID, params.mWifiSsid);
-        bundle.putString(EXTRA_PROVISIONING_WIFI_SECURITY_TYPE, params.mWifiSecurityType);
-        bundle.putString(EXTRA_PROVISIONING_WIFI_PASSWORD, params.mWifiPassword);
-        bundle.putString(EXTRA_PROVISIONING_WIFI_PROXY_HOST, params.mWifiProxyHost);
-        bundle.putString(EXTRA_PROVISIONING_WIFI_PROXY_BYPASS, params.mWifiProxyBypassHosts);
-        bundle.putString(EXTRA_PROVISIONING_WIFI_PAC_URL, params.mWifiPacUrl);
+        bundle.putString(EXTRA_PROVISIONING_TIME_ZONE, params.timeZone);
+        bundle.putString(EXTRA_PROVISIONING_LOCALE, localeToString(params.locale));
+        bundle.putString(EXTRA_PROVISIONING_WIFI_SSID, params.wifiInfo.ssid);
+        bundle.putString(EXTRA_PROVISIONING_WIFI_SECURITY_TYPE, params.wifiInfo.securityType);
+        bundle.putString(EXTRA_PROVISIONING_WIFI_PASSWORD, params.wifiInfo.password);
+        bundle.putString(EXTRA_PROVISIONING_WIFI_PROXY_HOST, params.wifiInfo.proxyHost);
+        bundle.putString(EXTRA_PROVISIONING_WIFI_PROXY_BYPASS, params.wifiInfo.proxyBypassHosts);
+        bundle.putString(EXTRA_PROVISIONING_WIFI_PAC_URL, params.wifiInfo.pacUrl);
+        bundle.putInt(EXTRA_PROVISIONING_WIFI_PROXY_PORT, params.wifiInfo.proxyPort);
+        bundle.putBoolean(EXTRA_PROVISIONING_WIFI_HIDDEN, params.wifiInfo.hidden);
         bundle.putString(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME,
-                params.mDeviceAdminPackageName);
+                params.deviceAdminPackageName);
         bundle.putParcelable(EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME,
-                params.mDeviceAdminComponentName);
+                params.deviceAdminComponentName);
         bundle.putInt(EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE,
-                params.mDeviceAdminMinVersion);
+                params.deviceAdminDownloadInfo.minVersion);
         bundle.putString(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION,
-                params.mDeviceAdminPackageDownloadLocation);
+                params.deviceAdminDownloadInfo.location);
         bundle.putString(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER,
-                params.mDeviceAdminPackageDownloadCookieHeader);
+                params.deviceAdminDownloadInfo.cookieHeader);
         bundle.putString(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM,
-                params.getDeviceAdminPackageChecksumAsString());
+                byteArrayToString(params.deviceAdminDownloadInfo.packageChecksum));
         bundle.putParcelable(EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME,
-                params.mDeviceInitializerComponentName);
+                params.deviceInitializerComponentName);
         bundle.putInt(EXTRA_PROVISIONING_DEVICE_INITIALIZER_MINIMUM_VERSION_CODE,
-                params.mDeviceInitializerMinVersion);
+                params.deviceInitializerDownloadInfo.minVersion);
         bundle.putString(EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION,
-                params.mDeviceInitializerPackageDownloadLocation);
+                params.deviceInitializerDownloadInfo.location);
         bundle.putString(EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER,
-                params.mDeviceInitializerPackageDownloadCookieHeader);
+                params.deviceInitializerDownloadInfo.cookieHeader);
         bundle.putString(EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM,
-                params.getDeviceInitializerPackageChecksumAsString());
-
-        bundle.putLong(EXTRA_PROVISIONING_LOCAL_TIME, params.mLocalTime);
-
-        bundle.putInt(EXTRA_PROVISIONING_WIFI_PROXY_PORT, params.mWifiProxyPort);
-
-        bundle.putBoolean(EXTRA_PROVISIONING_WIFI_HIDDEN, params.mWifiHidden);
-        bundle.putBoolean(EXTRA_PROVISIONING_STARTED_BY_NFC, params.mStartedByNfc);
+                byteArrayToString(params.deviceInitializerDownloadInfo.packageChecksum));
+        bundle.putLong(EXTRA_PROVISIONING_LOCAL_TIME, params.localTime);
+        bundle.putBoolean(EXTRA_PROVISIONING_STARTED_BY_NFC, params.startedByNfc);
         bundle.putBoolean(EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED,
-                params.mLeaveAllSystemAppsEnabled);
+                params.leaveAllSystemAppsEnabled);
 
-        bundle.putParcelable(EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE, params.mAdminExtrasBundle);
-        bundle.putBoolean(EXTRA_PROVISIONING_SKIP_ENCRYPTION, params.mSkipEncryption);
+        bundle.putParcelable(EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE, params.adminExtrasBundle);
+        bundle.putBoolean(EXTRA_PROVISIONING_SKIP_ENCRYPTION, params.skipEncryption);
 
         // Bluetooth parameters
-        bundle.putString(EXTRA_PROVISIONING_BT_MAC_ADDRESS, params.mBluetoothMac);
-        bundle.putString(EXTRA_PROVISIONING_BT_UUID, params.mBluetoothUuid);
-        bundle.putString(EXTRA_PROVISIONING_BT_DEVICE_ID, params.mBluetoothDeviceIdentifier);
-        bundle.putBoolean(EXTRA_PROVISIONING_BT_USE_PROXY, params.mUseBluetoothProxy);
+        bundle.putString(EXTRA_PROVISIONING_BT_MAC_ADDRESS, params.bluetoothInfo.mac);
+        bundle.putString(EXTRA_PROVISIONING_BT_UUID, params.bluetoothInfo.uuid);
+        bundle.putString(EXTRA_PROVISIONING_BT_DEVICE_ID, params.bluetoothInfo.deviceIdentifier);
+        bundle.putBoolean(EXTRA_PROVISIONING_BT_USE_PROXY, params.bluetoothInfo.useProxy);
 
         bundle.putParcelable(EXTRA_PROVISIONING_RESET_PROTECTION_PARAMETERS,
-                params.mFrpChallengeBundle);
+                params.frpChallengeBundle);
     }
 
     public ProvisioningParams parseIntent(Intent intent)
@@ -264,7 +261,7 @@
                     MIME_TYPE_PROVISIONING_NFC_V2.equals(mimeType)) {
                 ProvisioningParams params = parseProperties(new String(firstRecord.getPayload()
                                 , UTF_8));
-                params.mStartedByNfc = true;
+                params.startedByNfc = true;
                 return params;
             }
         }
@@ -283,88 +280,91 @@
             props.load(new StringReader(data));
 
             String s; // Used for parsing non-Strings.
-            params.mTimeZone
+            params.timeZone
                     = props.getProperty(EXTRA_PROVISIONING_TIME_ZONE);
             if ((s = props.getProperty(EXTRA_PROVISIONING_LOCALE)) != null) {
-                params.mLocale = stringToLocale(s);
+                params.locale = stringToLocale(s);
             }
-            params.mWifiSsid = props.getProperty(EXTRA_PROVISIONING_WIFI_SSID);
-            params.mWifiSecurityType = props.getProperty(EXTRA_PROVISIONING_WIFI_SECURITY_TYPE);
-            params.mWifiPassword = props.getProperty(EXTRA_PROVISIONING_WIFI_PASSWORD);
-            params.mWifiProxyHost = props.getProperty(EXTRA_PROVISIONING_WIFI_PROXY_HOST);
-            params.mWifiProxyBypassHosts = props.getProperty(EXTRA_PROVISIONING_WIFI_PROXY_BYPASS);
-            params.mWifiPacUrl = props.getProperty(EXTRA_PROVISIONING_WIFI_PAC_URL);
-            params.mDeviceAdminPackageName
+            params.wifiInfo.ssid = props.getProperty(EXTRA_PROVISIONING_WIFI_SSID);
+            params.wifiInfo.securityType = props.getProperty(EXTRA_PROVISIONING_WIFI_SECURITY_TYPE);
+            params.wifiInfo.password = props.getProperty(EXTRA_PROVISIONING_WIFI_PASSWORD);
+            params.wifiInfo.proxyHost = props.getProperty(EXTRA_PROVISIONING_WIFI_PROXY_HOST);
+            params.wifiInfo.proxyBypassHosts =
+                    props.getProperty(EXTRA_PROVISIONING_WIFI_PROXY_BYPASS);
+            params.wifiInfo.pacUrl = props.getProperty(EXTRA_PROVISIONING_WIFI_PAC_URL);
+            if ((s = props.getProperty(EXTRA_PROVISIONING_WIFI_PROXY_PORT)) != null) {
+                params.wifiInfo.proxyPort = Integer.parseInt(s);
+            }
+            if ((s = props.getProperty(EXTRA_PROVISIONING_WIFI_HIDDEN)) != null) {
+                params.wifiInfo.hidden = Boolean.parseBoolean(s);
+            }
+
+            params.deviceAdminPackageName
                     = props.getProperty(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME);
             String componentNameString = props.getProperty(
                     EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME);
             if (componentNameString != null) {
-                params.mDeviceAdminComponentName = ComponentName.unflattenFromString(
+                params.deviceAdminComponentName = ComponentName.unflattenFromString(
                         componentNameString);
             }
             if ((s = props.getProperty(
                     EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE)) != null) {
-                params.mDeviceAdminMinVersion = Integer.parseInt(s);
+                params.deviceAdminDownloadInfo.minVersion = Integer.parseInt(s);
             } else {
-                params.mDeviceAdminMinVersion = ProvisioningParams.DEFAULT_MINIMUM_VERSION;
+                params.deviceAdminDownloadInfo.minVersion =
+                        ProvisioningParams.DEFAULT_MINIMUM_VERSION;
             }
-            params.mDeviceAdminPackageDownloadLocation
+            params.deviceAdminDownloadInfo.location
                     = props.getProperty(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION);
-            params.mDeviceAdminPackageDownloadCookieHeader = props.getProperty(
+            params.deviceAdminDownloadInfo.cookieHeader = props.getProperty(
                     EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER);
             if ((s = props.getProperty(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM)) != null) {
-                params.mDeviceAdminPackageChecksum = stringToByteArray(s);
+                params.deviceAdminDownloadInfo.packageChecksum = stringToByteArray(s);
             }
             String name = props.getProperty(
                     EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME);
             if (name != null) {
-                params.mDeviceInitializerComponentName = ComponentName.unflattenFromString(name);
+                params.deviceInitializerComponentName = ComponentName.unflattenFromString(name);
             }
             if ((s = props.getProperty(
                     EXTRA_PROVISIONING_DEVICE_INITIALIZER_MINIMUM_VERSION_CODE)) != null) {
-                params.mDeviceInitializerMinVersion = Integer.parseInt(s);
+                params.deviceInitializerDownloadInfo.minVersion = Integer.parseInt(s);
             } else {
-                params.mDeviceInitializerMinVersion = ProvisioningParams.DEFAULT_MINIMUM_VERSION;
+                params.deviceInitializerDownloadInfo.minVersion =
+                        ProvisioningParams.DEFAULT_MINIMUM_VERSION;
             }
-            params.mDeviceInitializerPackageDownloadLocation = props.getProperty(
+            params.deviceInitializerDownloadInfo.location = props.getProperty(
                     EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION);
-            params.mDeviceInitializerPackageDownloadCookieHeader = props.getProperty(
+            params.deviceInitializerDownloadInfo.cookieHeader = props.getProperty(
                     EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER);
             if ((s = props.getProperty(
                     EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM)) != null) {
-                params.mDeviceInitializerPackageChecksum = stringToByteArray(s);
+                params.deviceInitializerDownloadInfo.packageChecksum = stringToByteArray(s);
             }
 
             if ((s = props.getProperty(EXTRA_PROVISIONING_LOCAL_TIME)) != null) {
-                params.mLocalTime = Long.parseLong(s);
-            }
-
-            if ((s = props.getProperty(EXTRA_PROVISIONING_WIFI_PROXY_PORT)) != null) {
-                params.mWifiProxyPort = Integer.parseInt(s);
-            }
-
-            if ((s = props.getProperty(EXTRA_PROVISIONING_WIFI_HIDDEN)) != null) {
-                params.mWifiHidden = Boolean.parseBoolean(s);
+                params.localTime = Long.parseLong(s);
             }
 
             if ((s = props.getProperty(EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED)) != null) {
-                params.mLeaveAllSystemAppsEnabled = Boolean.parseBoolean(s);
+                params.leaveAllSystemAppsEnabled = Boolean.parseBoolean(s);
             }
             if ((s = props.getProperty(EXTRA_PROVISIONING_SKIP_ENCRYPTION)) != null) {
-                params.mSkipEncryption = Boolean.parseBoolean(s);
+                params.skipEncryption = Boolean.parseBoolean(s);
             }
 
-            params.mBluetoothMac = props.getProperty(EXTRA_PROVISIONING_BT_MAC_ADDRESS);
-            params.mBluetoothUuid = props.getProperty(EXTRA_PROVISIONING_BT_UUID);
-            params.mBluetoothDeviceIdentifier = props.getProperty(EXTRA_PROVISIONING_BT_DEVICE_ID);
+            params.bluetoothInfo.mac = props.getProperty(EXTRA_PROVISIONING_BT_MAC_ADDRESS);
+            params.bluetoothInfo.uuid = props.getProperty(EXTRA_PROVISIONING_BT_UUID);
+            params.bluetoothInfo.deviceIdentifier =
+                    props.getProperty(EXTRA_PROVISIONING_BT_DEVICE_ID);
             if ((s = props.getProperty(EXTRA_PROVISIONING_BT_USE_PROXY)) != null) {
-                params.mUseBluetoothProxy = Boolean.parseBoolean(s);
+                params.bluetoothInfo.useProxy = Boolean.parseBoolean(s);
             }
 
-            params.mAdminExtrasBundle = deserializeExtrasBundle(props,
+            params.adminExtrasBundle = deserializeExtrasBundle(props,
                     EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE);
 
-            params.mFrpChallengeBundle = deserializeExtrasBundle(props,
+            params.frpChallengeBundle = deserializeExtrasBundle(props,
                     EXTRA_PROVISIONING_RESET_PROTECTION_PARAMETERS);
 
             checkValidityOfProvisioningParams(params);
@@ -405,70 +405,72 @@
         ProvisionLogger.logi("Processing intent.");
         ProvisioningParams params = new ProvisioningParams();
 
-        params.mTimeZone = intent.getStringExtra(EXTRA_PROVISIONING_TIME_ZONE);
+        params.timeZone = intent.getStringExtra(EXTRA_PROVISIONING_TIME_ZONE);
         String localeString = intent.getStringExtra(EXTRA_PROVISIONING_LOCALE);
         if (localeString != null) {
-            params.mLocale = stringToLocale(localeString);
+            params.locale = stringToLocale(localeString);
         }
-        params.mWifiSsid = intent.getStringExtra(EXTRA_PROVISIONING_WIFI_SSID);
-        params.mWifiSecurityType = intent.getStringExtra(EXTRA_PROVISIONING_WIFI_SECURITY_TYPE);
-        params.mWifiPassword = intent.getStringExtra(EXTRA_PROVISIONING_WIFI_PASSWORD);
-        params.mWifiProxyHost = intent.getStringExtra(EXTRA_PROVISIONING_WIFI_PROXY_HOST);
-        params.mWifiProxyBypassHosts = intent.getStringExtra(EXTRA_PROVISIONING_WIFI_PROXY_BYPASS);
-        params.mWifiPacUrl = intent.getStringExtra(EXTRA_PROVISIONING_WIFI_PAC_URL);
-        params.mDeviceAdminComponentName = (ComponentName) intent.getParcelableExtra(
+        params.wifiInfo.ssid = intent.getStringExtra(EXTRA_PROVISIONING_WIFI_SSID);
+        params.wifiInfo.securityType = intent.getStringExtra(EXTRA_PROVISIONING_WIFI_SECURITY_TYPE);
+        params.wifiInfo.password = intent.getStringExtra(EXTRA_PROVISIONING_WIFI_PASSWORD);
+        params.wifiInfo.proxyHost = intent.getStringExtra(EXTRA_PROVISIONING_WIFI_PROXY_HOST);
+        params.wifiInfo.proxyBypassHosts =
+                intent.getStringExtra(EXTRA_PROVISIONING_WIFI_PROXY_BYPASS);
+        params.wifiInfo.pacUrl = intent.getStringExtra(EXTRA_PROVISIONING_WIFI_PAC_URL);
+        params.wifiInfo.proxyPort = intent.getIntExtra(EXTRA_PROVISIONING_WIFI_PROXY_PORT,
+                ProvisioningParams.DEFAULT_WIFI_PROXY_PORT);
+        params.wifiInfo.hidden = intent.getBooleanExtra(EXTRA_PROVISIONING_WIFI_HIDDEN,
+                ProvisioningParams.DEFAULT_WIFI_HIDDEN);
+
+        params.deviceAdminComponentName = (ComponentName) intent.getParcelableExtra(
                 EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME);
-        params.mDeviceAdminPackageName
+        params.deviceAdminPackageName
                 = intent.getStringExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME);
-        params.mDeviceAdminMinVersion = intent.getIntExtra(
+        params.deviceAdminDownloadInfo.minVersion = intent.getIntExtra(
                 EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE,
                 ProvisioningParams.DEFAULT_MINIMUM_VERSION);
-        params.mDeviceAdminPackageDownloadLocation
+        params.deviceAdminDownloadInfo.location
                 = intent.getStringExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION);
-        params.mDeviceAdminPackageDownloadCookieHeader = intent.getStringExtra(
+        params.deviceAdminDownloadInfo.cookieHeader = intent.getStringExtra(
                 EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER);
         String hashString = intent.getStringExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM);
         if (hashString != null) {
-            params.mDeviceAdminPackageChecksum = stringToByteArray(hashString);
+            params.deviceAdminDownloadInfo.packageChecksum = stringToByteArray(hashString);
         }
-        params.mDeviceInitializerComponentName = (ComponentName) intent.getParcelableExtra(
+        params.deviceInitializerComponentName = (ComponentName) intent.getParcelableExtra(
                 EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME);
-        params.mDeviceInitializerMinVersion = intent.getIntExtra(
+        params.deviceInitializerDownloadInfo.minVersion = intent.getIntExtra(
                 EXTRA_PROVISIONING_DEVICE_INITIALIZER_MINIMUM_VERSION_CODE,
                 ProvisioningParams.DEFAULT_MINIMUM_VERSION);
-        params.mDeviceInitializerPackageDownloadLocation = intent.getStringExtra(
+        params.deviceInitializerDownloadInfo.location = intent.getStringExtra(
                 EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION);
-        params.mDeviceInitializerPackageDownloadCookieHeader = intent.getStringExtra(
+        params.deviceInitializerDownloadInfo.cookieHeader = intent.getStringExtra(
                 EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER);
         hashString = intent.getStringExtra(EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM);
         if (hashString != null) {
-            params.mDeviceInitializerPackageChecksum = stringToByteArray(hashString);
+            params.deviceInitializerDownloadInfo.packageChecksum = stringToByteArray(hashString);
         }
 
-        params.mLocalTime = intent.getLongExtra(EXTRA_PROVISIONING_LOCAL_TIME,
+        params.localTime = intent.getLongExtra(EXTRA_PROVISIONING_LOCAL_TIME,
                 ProvisioningParams.DEFAULT_LOCAL_TIME);
-
-        params.mWifiProxyPort = intent.getIntExtra(EXTRA_PROVISIONING_WIFI_PROXY_PORT,
-                ProvisioningParams.DEFAULT_WIFI_PROXY_PORT);
-
-        params.mWifiHidden = intent.getBooleanExtra(EXTRA_PROVISIONING_WIFI_HIDDEN,
-                ProvisioningParams.DEFAULT_WIFI_HIDDEN);
-        params.mStartedByNfc = intent.getBooleanExtra(EXTRA_PROVISIONING_STARTED_BY_NFC,
+        params.startedByNfc = intent.getBooleanExtra(EXTRA_PROVISIONING_STARTED_BY_NFC,
                 false);
-        params.mLeaveAllSystemAppsEnabled = intent.getBooleanExtra(
+        params.leaveAllSystemAppsEnabled = intent.getBooleanExtra(
                 EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED,
                 ProvisioningParams.DEFAULT_LEAVE_ALL_SYSTEM_APPS_ENABLED);
-        params.mSkipEncryption = intent.getBooleanExtra(
+        params.skipEncryption = intent.getBooleanExtra(
                 EXTRA_PROVISIONING_SKIP_ENCRYPTION,
                 ProvisioningParams.DEFAULT_EXTRA_PROVISIONING_SKIP_ENCRYPTION);
 
-        params.mBluetoothMac = intent.getStringExtra(EXTRA_PROVISIONING_BT_MAC_ADDRESS);
-        params.mBluetoothUuid = intent.getStringExtra(EXTRA_PROVISIONING_BT_UUID);
-        params.mBluetoothDeviceIdentifier = intent.getStringExtra(EXTRA_PROVISIONING_BT_DEVICE_ID);
-        params.mUseBluetoothProxy = intent.getBooleanExtra(EXTRA_PROVISIONING_BT_USE_PROXY, false);
+        params.bluetoothInfo.mac = intent.getStringExtra(EXTRA_PROVISIONING_BT_MAC_ADDRESS);
+        params.bluetoothInfo.uuid = intent.getStringExtra(EXTRA_PROVISIONING_BT_UUID);
+        params.bluetoothInfo.deviceIdentifier =
+                intent.getStringExtra(EXTRA_PROVISIONING_BT_DEVICE_ID);
+        params.bluetoothInfo.useProxy =
+                intent.getBooleanExtra(EXTRA_PROVISIONING_BT_USE_PROXY, false);
 
         try {
-            params.mAdminExtrasBundle = (PersistableBundle) intent.getParcelableExtra(
+            params.adminExtrasBundle = (PersistableBundle) intent.getParcelableExtra(
                     EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE);
         } catch (ClassCastException e) {
             throw new IllegalProvisioningArgumentException("Extra "
@@ -477,7 +479,7 @@
         }
 
         try {
-            params.mFrpChallengeBundle = intent.getParcelableExtra(
+            params.frpChallengeBundle = intent.getParcelableExtra(
                     EXTRA_PROVISIONING_RESET_PROTECTION_PARAMETERS);
         } catch (ClassCastException e) {
             throw new IllegalProvisioningArgumentException("Extra "
@@ -494,14 +496,14 @@
      */
     private void checkValidityOfProvisioningParams(ProvisioningParams params)
             throws IllegalProvisioningArgumentException  {
-        if (TextUtils.isEmpty(params.mDeviceAdminPackageName)
-                && params.mDeviceAdminComponentName == null) {
+        if (TextUtils.isEmpty(params.deviceAdminPackageName)
+                && params.deviceAdminComponentName == null) {
             throw new IllegalProvisioningArgumentException("Must provide the name of the device"
                     + " admin package or component name");
         }
-        if (!TextUtils.isEmpty(params.mDeviceAdminPackageDownloadLocation)) {
-            if (params.mDeviceAdminPackageChecksum == null ||
-                    params.mDeviceAdminPackageChecksum.length == 0) {
+        if (!TextUtils.isEmpty(params.deviceAdminDownloadInfo.location)) {
+            if (params.deviceAdminDownloadInfo.packageChecksum == null ||
+                    params.deviceAdminDownloadInfo.packageChecksum.length == 0) {
                 throw new IllegalProvisioningArgumentException("Checksum of installer file is"
                         + " required for downloading device admin file, but not provided.");
             }
@@ -517,8 +519,20 @@
         }
     }
 
-    public static Locale stringToLocale(String s)
+    public static String byteArrayToString(byte[] bytes) {
+        return Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP);
+    }
+
+    public static Locale stringToLocale(String string)
         throws IllformedLocaleException {
-        return new Locale.Builder().setLanguageTag(s.replace("_", "-")).build();
+        return new Locale.Builder().setLanguageTag(string.replace("_", "-")).build();
+    }
+
+    public static String localeToString(Locale locale) {
+        if (locale != null) {
+            return locale.getLanguage() + "_" + locale.getCountry();
+        } else {
+            return null;
+        }
     }
 }
diff --git a/src/com/android/managedprovisioning/ProvisioningParams.java b/src/com/android/managedprovisioning/ProvisioningParams.java
index 0c248a3..543f9a1 100644
--- a/src/com/android/managedprovisioning/ProvisioningParams.java
+++ b/src/com/android/managedprovisioning/ProvisioningParams.java
@@ -21,8 +21,11 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.PersistableBundle;
+import android.text.TextUtils;
 import android.util.Base64;
+
 import java.util.Locale;
+
 /**
  * Provisioning Parameters for DeviceOwner Provisioning
  */
@@ -35,84 +38,129 @@
     // Always download packages if no minimum version given.
     public static final int DEFAULT_MINIMUM_VERSION = Integer.MAX_VALUE;
 
-    public String mTimeZone;
-    public long mLocalTime = DEFAULT_LOCAL_TIME;
-    public Locale mLocale;
+    public String timeZone;
+    public long localTime = DEFAULT_LOCAL_TIME;
+    public Locale locale;
 
-    public String mWifiSsid;
-    public boolean mWifiHidden = DEFAULT_WIFI_HIDDEN;
-    public String mWifiSecurityType;
-    public String mWifiPassword;
-    public String mWifiProxyHost;
-    public int mWifiProxyPort = DEFAULT_WIFI_PROXY_PORT;
-    public String mWifiProxyBypassHosts;
-    public String mWifiPacUrl;
+    public static class WifiInfo {
+        public String ssid;
+        public boolean hidden = DEFAULT_WIFI_HIDDEN;
+        public String securityType;
+        public String password;
+        public String proxyHost;
+        public int proxyPort = DEFAULT_WIFI_PROXY_PORT;
+        public String proxyBypassHosts;
+        public String pacUrl;
 
-    // At least one one of mDeviceAdminPackageName and mDeviceAdminComponentName should be non-null
-    public String mDeviceAdminPackageName; // Package name of the device admin package.
-    public ComponentName mDeviceAdminComponentName;
-    public ComponentName mDeviceInitializerComponentName;
+        public void writeToParcel(Parcel out) {
+            out.writeString(ssid);
+            out.writeInt(hidden ? 1 : 0);
+            out.writeString(securityType);
+            out.writeString(password);
+            out.writeString(proxyHost);
+            out.writeInt(proxyPort);
+            out.writeString(proxyBypassHosts);
+            out.writeString(pacUrl);
+        }
 
-    private ComponentName mInferedDeviceAdminComponentName;
+        public void readFromParcel(Parcel in) {
+            ssid = in.readString();
+            hidden = in.readInt() == 1;
+            securityType = in.readString();
+            password = in.readString();
+            proxyHost = in.readString();
+            proxyPort = in.readInt();
+            proxyBypassHosts = in.readString();
+            pacUrl = in.readString();
+        }
+    };
+    public WifiInfo wifiInfo = new WifiInfo();
 
-    public String mDeviceAdminPackageDownloadLocation; // Url of the device admin .apk
-    public String mDeviceAdminPackageDownloadCookieHeader; // Cookie header for http request
-    public byte[] mDeviceAdminPackageChecksum = new byte[0]; // SHA-1 sum of the .apk file.
-    public int mDeviceAdminMinVersion;
+    // At least one one of deviceAdminPackageName and deviceAdminComponentName should be non-null
+    public String deviceAdminPackageName; // Package name of the device admin package.
+    public ComponentName deviceAdminComponentName;
+    public ComponentName deviceInitializerComponentName;
 
-    public String mDeviceInitializerPackageDownloadLocation; // Url of the device initializer .apk.
-    // Cookie header for initializer http request.
-    public String mDeviceInitializerPackageDownloadCookieHeader;
-    // SHA-1 sum of the initializer .apk file.
-    public byte[] mDeviceInitializerPackageChecksum = new byte[0];
-    public int mDeviceInitializerMinVersion;
+    private ComponentName inferedDeviceAdminComponentName;
 
-    public PersistableBundle mAdminExtrasBundle;
-    public PersistableBundle mFrpChallengeBundle;
+    public static class PackageDownloadInfo {
+        // Url where the package (.apk) can be downloaded from
+        public String location;
+        // Cookie header for http request
+        public String cookieHeader;
+        // SHA-1 sum of the .apk file
+        public byte[] packageChecksum = new byte[0];
+        public int minVersion;
 
-    public boolean mStartedByNfc; // True iff provisioning flow was started by Nfc bump.
+        public void writeToParcel(Parcel out) {
+            out.writeInt(minVersion);
+            out.writeString(location);
+            out.writeString(cookieHeader);
+            out.writeByteArray(packageChecksum);
+        }
 
-    public boolean mLeaveAllSystemAppsEnabled;
-    public boolean mSkipEncryption;
+        public void readFromParcel(Parcel in) {
+            minVersion = in.readInt();
+            location = in.readString();
+            cookieHeader = in.readString();
+            packageChecksum = in.createByteArray();
+        }
+    }
+    public PackageDownloadInfo deviceAdminDownloadInfo = new PackageDownloadInfo();
+    public PackageDownloadInfo deviceInitializerDownloadInfo  = new PackageDownloadInfo();
 
-    public String mBluetoothMac;
-    public String mBluetoothUuid;
-    public String mBluetoothDeviceIdentifier;
-    public boolean mUseBluetoothProxy;
+    public PersistableBundle adminExtrasBundle;
+    public PersistableBundle frpChallengeBundle;
+
+    public boolean startedByNfc; // True iff provisioning flow was started by Nfc bump.
+
+    public boolean leaveAllSystemAppsEnabled;
+    public boolean skipEncryption;
+
+    public static class BluetoothInfo {
+        public String mac;
+        public String uuid;
+        public String deviceIdentifier;
+        public boolean useProxy;
+
+        public void writeToParcel(Parcel out) {
+            out.writeString(mac);
+            out.writeString(uuid);
+            out.writeString(deviceIdentifier);
+            out.writeInt(useProxy ? 1 : 0);
+        }
+
+        public void readFromParcel(Parcel in) {
+            mac = in.readString();
+            uuid = in.readString();
+            deviceIdentifier = in.readString();
+            useProxy = in.readInt() == 1;
+        }
+    };
+    public BluetoothInfo bluetoothInfo = new BluetoothInfo();
 
     public String inferDeviceAdminPackageName() {
-        if (mDeviceAdminComponentName != null) {
-            return mDeviceAdminComponentName.getPackageName();
+        if (deviceAdminComponentName != null) {
+            return deviceAdminComponentName.getPackageName();
         }
-        return mDeviceAdminPackageName;
+        return deviceAdminPackageName;
+    }
+
+    public String getDeviceInitializerPackageName() {
+        if (deviceInitializerComponentName != null) {
+            return deviceInitializerComponentName.getPackageName();
+        }
+        return null;
     }
 
     // This should not be called if the app has not been installed yet.
     ComponentName inferDeviceAdminComponentName(Context c)
             throws Utils.IllegalProvisioningArgumentException {
-        if (mInferedDeviceAdminComponentName == null) {
-            mInferedDeviceAdminComponentName = Utils.findDeviceAdmin(
-                    mDeviceAdminPackageName, mDeviceAdminComponentName, c);
+        if (inferedDeviceAdminComponentName == null) {
+            inferedDeviceAdminComponentName = Utils.findDeviceAdmin(
+                    deviceAdminPackageName, deviceAdminComponentName, c);
         }
-        return mInferedDeviceAdminComponentName;
-    }
-
-    public String getLocaleAsString() {
-        if (mLocale != null) {
-            return mLocale.getLanguage() + "_" + mLocale.getCountry();
-        } else {
-            return null;
-        }
-    }
-
-    public String getDeviceAdminPackageChecksumAsString() {
-        return Base64.encodeToString(mDeviceAdminPackageChecksum,
-                Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP);
-    }
-
-    public String getDeviceInitializerPackageChecksumAsString() {
-        return Base64.encodeToString(mDeviceInitializerPackageChecksum,
-                Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP);
+        return inferedDeviceAdminComponentName;
     }
 
     @Override
@@ -122,39 +170,21 @@
 
     @Override
     public void writeToParcel(Parcel out, int flags) {
-        out.writeString(mTimeZone);
-        out.writeLong(mLocalTime);
-        out.writeSerializable(mLocale);
-        out.writeString(mWifiSsid);
-        out.writeInt(mWifiHidden ? 1 : 0);
-        out.writeString(mWifiSecurityType);
-        out.writeString(mWifiPassword);
-        out.writeString(mWifiProxyHost);
-        out.writeInt(mWifiProxyPort);
-        out.writeString(mWifiProxyBypassHosts);
-        out.writeString(mWifiPacUrl);
-        out.writeString(mDeviceAdminPackageName);
-        out.writeParcelable(mDeviceAdminComponentName, 0 /* default */);
-        out.writeInt(mDeviceAdminMinVersion);
-        out.writeString(mDeviceAdminPackageDownloadLocation);
-        out.writeString(mDeviceAdminPackageDownloadCookieHeader);
-        out.writeInt(mDeviceAdminPackageChecksum.length);
-        out.writeByteArray(mDeviceAdminPackageChecksum);
-        out.writeParcelable(mDeviceInitializerComponentName, 0 /* default */);
-        out.writeInt(mDeviceInitializerMinVersion);
-        out.writeString(mDeviceInitializerPackageDownloadLocation);
-        out.writeString(mDeviceInitializerPackageDownloadCookieHeader);
-        out.writeInt(mDeviceInitializerPackageChecksum.length);
-        out.writeByteArray(mDeviceInitializerPackageChecksum);
-        out.writeParcelable(mAdminExtrasBundle, 0 /* default */);
-        out.writeInt(mStartedByNfc ? 1 : 0);
-        out.writeInt(mLeaveAllSystemAppsEnabled ? 1 : 0);
-        out.writeInt(mSkipEncryption ? 1 : 0);
-        out.writeString(mBluetoothMac);
-        out.writeString(mBluetoothUuid);
-        out.writeString(mBluetoothDeviceIdentifier);
-        out.writeInt(mUseBluetoothProxy ? 1 : 0);
-        out.writeParcelable(mFrpChallengeBundle, 0 /* default */);
+        out.writeString(timeZone);
+        out.writeLong(localTime);
+        out.writeSerializable(locale);
+        wifiInfo.writeToParcel(out);
+        out.writeString(deviceAdminPackageName);
+        out.writeParcelable(deviceAdminComponentName, 0 /* default */);
+        deviceAdminDownloadInfo.writeToParcel(out);
+        out.writeParcelable(deviceInitializerComponentName, 0 /* default */);
+        deviceInitializerDownloadInfo.writeToParcel(out);
+        out.writeParcelable(adminExtrasBundle, 0 /* default */);
+        out.writeInt(startedByNfc ? 1 : 0);
+        out.writeInt(leaveAllSystemAppsEnabled ? 1 : 0);
+        out.writeInt(skipEncryption ? 1 : 0);
+        bluetoothInfo.writeToParcel(out);
+        out.writeParcelable(frpChallengeBundle, 0 /* default */);
     }
 
     public static final Parcelable.Creator<ProvisioningParams> CREATOR
@@ -162,43 +192,23 @@
         @Override
         public ProvisioningParams createFromParcel(Parcel in) {
             ProvisioningParams params = new ProvisioningParams();
-            params.mTimeZone = in.readString();
-            params.mLocalTime = in.readLong();
-            params.mLocale = (Locale) in.readSerializable();
-            params.mWifiSsid = in.readString();
-            params.mWifiHidden = in.readInt() == 1;
-            params.mWifiSecurityType = in.readString();
-            params.mWifiPassword = in.readString();
-            params.mWifiProxyHost = in.readString();
-            params.mWifiProxyPort = in.readInt();
-            params.mWifiProxyBypassHosts = in.readString();
-            params.mWifiPacUrl = in.readString();
-            params.mDeviceAdminPackageName = in.readString();
-            params.mDeviceAdminComponentName = (ComponentName)
+            params.timeZone = in.readString();
+            params.localTime = in.readLong();
+            params.locale = (Locale) in.readSerializable();
+            params.wifiInfo.readFromParcel(in);
+            params.deviceAdminPackageName = in.readString();
+            params.deviceAdminComponentName = (ComponentName)
                     in.readParcelable(null /* use default classloader */);
-            params.mDeviceAdminMinVersion = in.readInt();
-            params.mDeviceAdminPackageDownloadLocation = in.readString();
-            params.mDeviceAdminPackageDownloadCookieHeader = in.readString();
-            int checksumLength = in.readInt();
-            params.mDeviceAdminPackageChecksum = new byte[checksumLength];
-            in.readByteArray(params.mDeviceAdminPackageChecksum);
-            params.mDeviceInitializerComponentName = (ComponentName)
+            params.deviceAdminDownloadInfo.readFromParcel(in);
+            params.deviceInitializerComponentName = (ComponentName)
                     in.readParcelable(null /* use default classloader */);
-            params.mDeviceInitializerMinVersion = in.readInt();
-            params.mDeviceInitializerPackageDownloadLocation = in.readString();
-            params.mDeviceInitializerPackageDownloadCookieHeader = in.readString();
-            checksumLength = in.readInt();
-            params.mDeviceInitializerPackageChecksum = new byte[checksumLength];
-            in.readByteArray(params.mDeviceInitializerPackageChecksum);
-            params.mAdminExtrasBundle = in.readParcelable(null /* use default classloader */);
-            params.mStartedByNfc = in.readInt() == 1;
-            params.mLeaveAllSystemAppsEnabled = in.readInt() == 1;
-            params.mSkipEncryption = in.readInt() == 1;
-            params.mBluetoothMac = in.readString();
-            params.mBluetoothUuid = in.readString();
-            params.mBluetoothDeviceIdentifier = in.readString();
-            params.mUseBluetoothProxy = in.readInt() == 1;
-            params.mFrpChallengeBundle = in.readParcelable(null /* use default classloader */);
+            params.deviceInitializerDownloadInfo.readFromParcel(in);
+            params.adminExtrasBundle = in.readParcelable(null /* use default classloader */);
+            params.startedByNfc = in.readInt() == 1;
+            params.leaveAllSystemAppsEnabled = in.readInt() == 1;
+            params.skipEncryption = in.readInt() == 1;
+            params.bluetoothInfo.readFromParcel(in);
+            params.frpChallengeBundle = in.readParcelable(null /* use default classloader */);
             return params;
         }
 
diff --git a/src/com/android/managedprovisioning/Utils.java b/src/com/android/managedprovisioning/Utils.java
index 8ed866a..318aee5 100644
--- a/src/com/android/managedprovisioning/Utils.java
+++ b/src/com/android/managedprovisioning/Utils.java
@@ -237,4 +237,22 @@
         return user != null ? user.isManagedProfile() : false;
     }
 
+    /**
+     * Returns true if the given package does not exist on the device or if its version code is less
+     * than the given version, and false otherwise.
+     */
+    public static boolean packageRequiresUpdate(String packageName, int minSupportedVersion,
+            Context context) {
+        try {
+            PackageInfo packageInfo = context.getPackageManager().getPackageInfo(packageName, 0);
+            if (packageInfo.versionCode >= minSupportedVersion) {
+                return false;
+            }
+        } catch (NameNotFoundException e) {
+            // Package not on device.
+        }
+
+        return true;
+    }
+
 }
diff --git a/src/com/android/managedprovisioning/task/AddWifiNetworkTask.java b/src/com/android/managedprovisioning/task/AddWifiNetworkTask.java
index f5337f9..e12aaba 100644
--- a/src/com/android/managedprovisioning/task/AddWifiNetworkTask.java
+++ b/src/com/android/managedprovisioning/task/AddWifiNetworkTask.java
@@ -29,9 +29,11 @@
 import java.lang.Thread;
 
 import com.android.managedprovisioning.NetworkMonitor;
+import com.android.managedprovisioning.ProvisioningParams.WifiInfo;
 import com.android.managedprovisioning.ProvisionLogger;
 import com.android.managedprovisioning.WifiConfig;
 
+
 /**
  * Adds a wifi network to system.
  */
@@ -42,14 +44,7 @@
     private static final int RECONNECT_TIMEOUT_MS = 30000;
 
     private final Context mContext;
-    private final String mSsid;
-    private final boolean mHidden;
-    private final String mSecurityType;
-    private final String mPassword;
-    private final String mProxyHost;
-    private final int mProxyPort;
-    private final String mProxyBypassHosts;
-    private final String mPacUrl;
+    private final WifiInfo mWifiInfo;
     private final Callback mCallback;
 
     private WifiManager mWifiManager;
@@ -65,19 +60,10 @@
     /**
      * @throws IllegalArgumentException if the {@code ssid} parameter is empty.
      */
-    public AddWifiNetworkTask(Context context, String ssid, boolean hidden, String securityType,
-            String password, String proxyHost, int proxyPort, String proxyBypassHosts,
-            String pacUrl, Callback callback) {
+    public AddWifiNetworkTask(Context context, WifiInfo wifiInfo, Callback callback) {
         mCallback = callback;
         mContext = context;
-        mSsid = ssid;
-        mHidden = hidden;
-        mSecurityType = securityType;
-        mPassword = password;
-        mProxyHost = proxyHost;
-        mProxyPort = proxyPort;
-        mProxyBypassHosts = proxyBypassHosts;
-        mPacUrl = pacUrl;
+        mWifiInfo = wifiInfo;
         mWifiManager  = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
         mWifiConfig = new WifiConfig(mWifiManager);
 
@@ -89,7 +75,7 @@
     }
 
     public void run() {
-        if (TextUtils.isEmpty(mSsid)) {
+        if (TextUtils.isEmpty(mWifiInfo.ssid)) {
             mCallback.onSuccess();
             return;
         }
@@ -109,8 +95,9 @@
     }
 
     private void connectToProvidedNetwork() {
-        int netId = mWifiConfig.addNetwork(mSsid, mHidden, mSecurityType, mPassword, mProxyHost,
-                mProxyPort, mProxyBypassHosts, mPacUrl);
+        int netId = mWifiConfig.addNetwork(mWifiInfo.ssid, mWifiInfo.hidden, mWifiInfo.securityType,
+                mWifiInfo.password, mWifiInfo.proxyHost, mWifiInfo.proxyPort,
+                mWifiInfo.proxyBypassHosts, mWifiInfo.pacUrl);
 
         if (netId == -1) {
             ProvisionLogger.loge("Failed to save network.");
@@ -194,7 +181,7 @@
     private boolean isConnectedToSpecifiedWifi() {
         return NetworkMonitor.isConnectedToWifi(mContext)
                 && mWifiManager.getConnectionInfo() != null
-                && mSsid.equals(mWifiManager.getConnectionInfo().getSSID());
+                && mWifiInfo.ssid.equals(mWifiManager.getConnectionInfo().getSSID());
     }
 
     public static boolean isConnectedToWifi(Context context) {
diff --git a/src/com/android/managedprovisioning/task/BluetoothConnectTask.java b/src/com/android/managedprovisioning/task/BluetoothConnectTask.java
index d57498b..1436d55 100644
--- a/src/com/android/managedprovisioning/task/BluetoothConnectTask.java
+++ b/src/com/android/managedprovisioning/task/BluetoothConnectTask.java
@@ -11,9 +11,10 @@
 import android.support.v4.content.LocalBroadcastManager;
 import android.text.TextUtils;
 
+import com.android.managedprovisioning.comm.ProvisionCommLogger;
 import com.android.managedprovisioning.ProvisionLogger;
 import com.android.managedprovisioning.ProvisioningParams;
-import com.android.managedprovisioning.comm.ProvisionCommLogger;
+import com.android.managedprovisioning.ProvisioningParams.BluetoothInfo;
 import com.android.managedprovisioning.proxy.BluetoothConnectionService;
 
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -38,11 +39,7 @@
      */
     private final Handler mHandler;
     private final AtomicBoolean mTaskDone;
-
-    private final boolean mUseBluetoothProxy;
-    private final String mBluetoothMac;
-    private final String mBluetoothUuid;
-    private final String mBluetoothDeviceId;
+    private final BluetoothInfo mBluetoothInfo;
     private final boolean mHasWifiSsid;
     private final BluetoothAdapter mBtAdapter;
 
@@ -60,17 +57,15 @@
      */
     private BroadcastReceiver mBtNetworkProxyReceiver;
 
-    public BluetoothConnectTask(Context context, ProvisioningParams params, Callback callback) {
+    public BluetoothConnectTask(Context context, BluetoothInfo bluetoothInfo, boolean hasWifiSsid,
+            Callback callback) {
         mCallback = callback;
         mContext = context;
         // Use the default Bluetooth adapter
         mBtAdapter = BluetoothAdapter.getDefaultAdapter();
         // Read provisioning parameters
-        mBluetoothMac = params.mBluetoothMac;
-        mBluetoothUuid = params.mBluetoothUuid;
-        mBluetoothDeviceId = params.mBluetoothDeviceIdentifier;
-        mUseBluetoothProxy = params.mUseBluetoothProxy;
-        mHasWifiSsid = !TextUtils.isEmpty(params.mWifiSsid);
+        mBluetoothInfo = bluetoothInfo;
+        mHasWifiSsid = hasWifiSsid;
         // Handler used to execute onBluetoothEnabled
         HandlerThread thread = new HandlerThread("Timeout thread",
                 android.os.Process.THREAD_PRIORITY_BACKGROUND);
@@ -87,8 +82,8 @@
             return;
         }
         // If any of the required fields are invalid, do not connect over Bluetooth.
-        if (TextUtils.isEmpty(mBluetoothMac) || TextUtils.isEmpty(mBluetoothUuid) ||
-                TextUtils.isEmpty(mBluetoothDeviceId)) {
+        if (TextUtils.isEmpty(mBluetoothInfo.mac) || TextUtils.isEmpty(mBluetoothInfo.uuid) ||
+                TextUtils.isEmpty(mBluetoothInfo.deviceIdentifier)) {
             mCallback.onSuccess();
             return;
         }
@@ -165,10 +160,10 @@
         // Don't start the Bluetooth proxy if a Wi-Fi network is available. If an SSID is available,
         // assume that a Wi-Fi network will be added and do not start the network proxy. If a
         // Wi-Fi connection is already available, do not use the proxy.
-        ProvisionLogger.logd("useProxy: mUseBluetoothProxy=" + mUseBluetoothProxy +
+        ProvisionLogger.logd("useProxy: mBluetoothInfo.useProxy=" + mBluetoothInfo.useProxy +
                 ", mHasWifiSsid=" + mHasWifiSsid +
                 ", isConnectedToWifi=" + AddWifiNetworkTask.isConnectedToWifi(mContext));
-        boolean useProxy = mUseBluetoothProxy && !mHasWifiSsid &&
+        boolean useProxy = mBluetoothInfo.useProxy && !mHasWifiSsid &&
                 !AddWifiNetworkTask.isConnectedToWifi(mContext);
         // Start Bluetooth Service
         if (useProxy) {
@@ -187,9 +182,10 @@
                     new IntentFilter(BluetoothConnectionService.ACTION_LOCAL_BLUETOOTH_SETUP));
         }
         Intent intent = new Intent(mContext, BluetoothConnectionService.class);
-        intent.putExtra(BluetoothConnectionService.EXTRA_BLUETOOTH_MAC, mBluetoothMac);
-        intent.putExtra(BluetoothConnectionService.EXTRA_BLUETOOTH_UUID, mBluetoothUuid);
-        intent.putExtra(BluetoothConnectionService.EXTRA_BLUETOOTH_DEVICE_ID, mBluetoothDeviceId);
+        intent.putExtra(BluetoothConnectionService.EXTRA_BLUETOOTH_MAC, mBluetoothInfo.mac);
+        intent.putExtra(BluetoothConnectionService.EXTRA_BLUETOOTH_UUID, mBluetoothInfo.uuid);
+        intent.putExtra(BluetoothConnectionService.EXTRA_BLUETOOTH_DEVICE_ID,
+                mBluetoothInfo.deviceIdentifier);
         intent.putExtra(BluetoothConnectionService.EXTRA_BLUETOOTH_USE_PROXY, useProxy);
         mContext.startService(intent);
         if (!useProxy) {
diff --git a/src/com/android/managedprovisioning/task/DownloadPackageTask.java b/src/com/android/managedprovisioning/task/DownloadPackageTask.java
index 58e3c24..33fe108 100644
--- a/src/com/android/managedprovisioning/task/DownloadPackageTask.java
+++ b/src/com/android/managedprovisioning/task/DownloadPackageTask.java
@@ -32,7 +32,8 @@
 import android.util.Base64;
 
 import com.android.managedprovisioning.ProvisionLogger;
-import com.android.managedprovisioning.ProvisioningParams;
+import com.android.managedprovisioning.ProvisioningParams.PackageDownloadInfo;
+import com.android.managedprovisioning.Utils;
 
 import java.io.InputStream;
 import java.io.IOException;
@@ -44,8 +45,7 @@
 import java.util.Set;
 
 /**
- * Downloads the device admin and the device initializer if download locations were provided for
- * them in the provisioning parameters. Also checks that each file's hash matches a given hash to
+ * Downloads all packages that were added. Also checks that each file's hash matches a given hash to
  * verify that the downloaded files are the ones that are expected.
  */
 public class DownloadPackageTask {
@@ -53,9 +53,6 @@
     public static final int ERROR_DOWNLOAD_FAILED = 1;
     public static final int ERROR_OTHER = 2;
 
-    public static final String DEVICE_OWNER = "deviceOwner";
-    public static final String INITIALIZER = "initializer";
-
     private static final String HASH_TYPE = "SHA-1";
 
     private final Context mContext;
@@ -64,36 +61,22 @@
     private final DownloadManager mDlm;
     private final PackageManager mPm;
 
-    private Set<DownloadInfo> mDownloads;
+    private Set<DownloadStatusInfo> mDownloads;
 
-    public DownloadPackageTask (Context context, ProvisioningParams params, Callback callback) {
+    public DownloadPackageTask (Context context, Callback callback) {
         mCallback = callback;
         mContext = context;
         mDlm = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
         mPm = context.getPackageManager();
 
-        mDownloads = new HashSet<DownloadInfo>();
-        if (!TextUtils.isEmpty(params.mDeviceAdminPackageDownloadLocation)
-                && packageRequiresUpdate(params.inferDeviceAdminPackageName(),
-                        params.mDeviceAdminMinVersion)) {
-            mDownloads.add(new DownloadInfo(
-                    params.mDeviceAdminPackageDownloadLocation,
-                    params.mDeviceAdminPackageChecksum,
-                    params.mDeviceAdminPackageDownloadCookieHeader,
-                    DEVICE_OWNER));
-        }
-        if (params.mDeviceInitializerComponentName != null) {
-            String deviceInitializerPackageName =
-                    params.mDeviceInitializerComponentName.getPackageName();
-            if (!TextUtils.isEmpty(params.mDeviceInitializerPackageDownloadLocation)
-                    && packageRequiresUpdate(deviceInitializerPackageName,
-                            params.mDeviceInitializerMinVersion)) {
-                mDownloads.add(new DownloadInfo(
-                        params.mDeviceInitializerPackageDownloadLocation,
-                        params.mDeviceInitializerPackageChecksum,
-                        params.mDeviceInitializerPackageDownloadCookieHeader,
-                        INITIALIZER));
-            }
+        mDownloads = new HashSet<DownloadStatusInfo>();
+    }
+
+    public void addDownloadIfNecessary(String packageName, PackageDownloadInfo downloadInfo,
+            String label) {
+        if (!TextUtils.isEmpty(downloadInfo.location) && Utils.packageRequiresUpdate(packageName,
+               downloadInfo.minVersion, mContext)) {
+            mDownloads.add(new DownloadStatusInfo(downloadInfo, label));
         }
     }
 
@@ -125,16 +108,17 @@
 
         DownloadManager dm = (DownloadManager) mContext
                 .getSystemService(Context.DOWNLOAD_SERVICE);
-        for (DownloadInfo downloadInfo : mDownloads) {
-            ProvisionLogger.logd("Starting download from " + downloadInfo.mDownloadLocationFrom);
+        for (DownloadStatusInfo info : mDownloads) {
+            ProvisionLogger.logd("Starting download from " + info.mPackageDownloadInfo.location);
 
-            Request request = new Request(Uri.parse(downloadInfo.mDownloadLocationFrom));
-            if (downloadInfo.mHttpCookieHeader != null) {
-                request.addRequestHeader("Cookie", downloadInfo.mHttpCookieHeader);
+            Request request = new Request(Uri.parse(info.mPackageDownloadInfo.location));
+            if (info.mPackageDownloadInfo.cookieHeader != null) {
+                request.addRequestHeader("Cookie", info.mPackageDownloadInfo.cookieHeader);
                 ProvisionLogger.logd(
-                        "Downloading with http cookie header: " + downloadInfo.mHttpCookieHeader);
+                        "Downloading with http cookie header: "
+                        + info.mPackageDownloadInfo.cookieHeader);
             }
-            downloadInfo.mDownloadId = dm.enqueue(request);
+            info.mDownloadId = dm.enqueue(request);
         }
     }
 
@@ -142,14 +126,14 @@
         return new BroadcastReceiver() {
             /**
              * Whenever the download manager finishes a download, record the successful download for
-             * the corresponding DownloadInfo.
+             * the corresponding DownloadStatusInfo.
              */
             @Override
             public void onReceive(Context context, Intent intent) {
                 if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(intent.getAction())) {
                     Query q = new Query();
-                    for (DownloadInfo downloadInfo : mDownloads) {
-                        q.setFilterById(downloadInfo.mDownloadId);
+                    for (DownloadStatusInfo info : mDownloads) {
+                        q.setFilterById(info.mDownloadId);
                         Cursor c = mDlm.query(q);
                         if (c.moveToFirst()) {
                             long downloadId =
@@ -181,17 +165,17 @@
      * @param location the file location of the downloaded file.
      */
     private void onDownloadSuccess(long downloadId, String location) {
-        DownloadInfo downloadInfo = null;
-        for (DownloadInfo info : mDownloads) {
-            if (downloadId == info.mDownloadId) {
-                downloadInfo = info;
+        DownloadStatusInfo info = null;
+        for (DownloadStatusInfo infoToMatch : mDownloads) {
+            if (downloadId == infoToMatch.mDownloadId) {
+                info = infoToMatch;
             }
         }
-        if (downloadInfo == null || downloadInfo.mDoneDownloading) {
+        if (info == null || info.mDoneDownloading) {
             // DownloadManager can send success more than once. Only act first time.
             return;
         } else {
-            downloadInfo.mDoneDownloading = true;
+            info.mDoneDownloading = true;
         }
         ProvisionLogger.logd("Downloaded succesfully to: " + location);
 
@@ -202,25 +186,25 @@
             return;
         }
 
-        if (Arrays.equals(downloadInfo.mHash, hash)) {
+        if (Arrays.equals(info.mPackageDownloadInfo.packageChecksum, hash)) {
             ProvisionLogger.logd(HASH_TYPE + "-hashes matched, both are "
                     + byteArrayToString(hash));
-            downloadInfo.mLocation = location;
-            downloadInfo.mSuccess = true;
+            info.mLocation = location;
+            info.mSuccess = true;
             checkSuccess();
         } else {
             ProvisionLogger.loge(HASH_TYPE + "-hash of downloaded file does not match given hash.");
             ProvisionLogger.loge(HASH_TYPE + "-hash of downloaded file: "
                     + byteArrayToString(hash));
             ProvisionLogger.loge(HASH_TYPE + "-hash provided by programmer: "
-                    + byteArrayToString(downloadInfo.mHash));
+                    + byteArrayToString(info.mPackageDownloadInfo.packageChecksum));
 
             mCallback.onError(ERROR_HASH_MISMATCH);
         }
     }
 
     private void checkSuccess() {
-        for (DownloadInfo info : mDownloads) {
+        for (DownloadStatusInfo info : mDownloads) {
             if (!info.mSuccess) {
                 return;
             }
@@ -274,9 +258,9 @@
         return hash;
     }
 
-    public String getDownloadedPackageLocation(String packageType) {
-        for (DownloadInfo info : mDownloads) {
-            if (packageType.equals(info.mPackageType)) {
+    public String getDownloadedPackageLocation(String label) {
+        for (DownloadStatusInfo info : mDownloads) {
+            if (info.mLabel.equals(label)) {
                 return info.mLocation;
             }
         }
@@ -293,7 +277,7 @@
         //Remove download.
         DownloadManager dm = (DownloadManager) mContext
                 .getSystemService(Context.DOWNLOAD_SERVICE);
-        for (DownloadInfo info : mDownloads) {
+        for (DownloadStatusInfo info : mDownloads) {
             boolean removeSuccess = dm.remove(info.mDownloadId) == 1;
             if (removeSuccess) {
                 ProvisionLogger.logd("Successfully removed installer file.");
@@ -314,22 +298,17 @@
         public abstract void onError(int errorCode);
     }
 
-    private static class DownloadInfo {
-        public final String mDownloadLocationFrom;
-        public final byte[] mHash;
-        public final String mHttpCookieHeader;
-        public final String mPackageType;
+    private static class DownloadStatusInfo {
+        public final PackageDownloadInfo mPackageDownloadInfo;
+        public final String mLabel;
         public long mDownloadId;
-        public String mLocation;
+        public String mLocation; // Location where the package is downloaded to.
         public boolean mDoneDownloading;
         public boolean mSuccess;
 
-        public DownloadInfo(String downloadLocation, byte[] hash, String httpCookieHeader,
-                String packageType) {
-            mDownloadLocationFrom = downloadLocation;
-            mHash = hash;
-            mHttpCookieHeader = httpCookieHeader;
-            mPackageType = packageType;
+        public DownloadStatusInfo(PackageDownloadInfo packageDownloadInfo,String label) {
+            mPackageDownloadInfo = packageDownloadInfo;
+            mLabel = label;
             mDoneDownloading = false;
         }
     }
diff --git a/src/com/android/managedprovisioning/task/InstallPackageTask.java b/src/com/android/managedprovisioning/task/InstallPackageTask.java
index fb375eb..b1054e1 100644
--- a/src/com/android/managedprovisioning/task/InstallPackageTask.java
+++ b/src/com/android/managedprovisioning/task/InstallPackageTask.java
@@ -35,8 +35,8 @@
 import java.util.Set;
 
 /**
- * Optionally installs device owner and device initializer packages. Can install a downloaded apk,
- * or install an existing package which is already installed for a different user.
+ * Installs all packages that were added. Can install a downloaded apk, or install an existing
+ * package which is already installed for a different user.
  * <p>
  * Before installing from a downloaded file, each file is checked to ensure it contains the correct
  * package and admin receiver.
@@ -49,8 +49,6 @@
 
     private final Context mContext;
     private final Callback mCallback;
-    private final String mDeviceAdminPackageName;
-    private final String mDeviceInitializerPackageName;
 
     private PackageManager mPm;
     private int mPackageVerifierEnable;
@@ -58,51 +56,36 @@
 
     /**
      * Create an InstallPackageTask. When run, this will attempt to install the device initializer
-     * and device admin packages if they are specified in {@code params}.
+     * and device admin packages if they are non-null.
      *
      * {@see #run(String, String)} for more detail on package installation.
      */
-    public InstallPackageTask (Context context, ProvisioningParams params, Callback callback) {
+    public InstallPackageTask (Context context, Callback callback) {
         mCallback = callback;
         mContext = context;
-        mDeviceAdminPackageName = params.inferDeviceAdminPackageName();
-
-        if (params.mDeviceInitializerComponentName != null) {
-            mDeviceInitializerPackageName = params.mDeviceInitializerComponentName.getPackageName();
-        } else {
-            mDeviceInitializerPackageName = null;
-        }
-
         mPackagesToInstall = new HashSet<InstallInfo>();
         mPm = mContext.getPackageManager();
     }
 
     /**
-     * Install the device admin and device initializer packages. Each package will be installed from
-     * the given location if one is provided. If a null or empty location is provided, and the
+     * Should be called before {@link #run}.
+     */
+    public void addInstallIfNecessary(String packageName, String packageLocation) {
+        if (!TextUtils.isEmpty(packageName)) {
+            mPackagesToInstall.add(new InstallInfo(packageName, packageLocation));
+        }
+    }
+
+    /**
+     * Install all packages given by {@link #addPackageToInstall}. Each package will be installed
+     * from the given location if one is provided. If a null or empty location is provided, and the
      * package is installed for a different user, it will be enabled for the calling user. If the
      * package location is not provided and the package is not installed for any other users, this
      * task will produce an error.
      *
      * Errors will be indicated if a downloaded package is invalid, or installation fails.
-     *
-     * @param deviceAdminPackageLocation The file system location of a downloaded device admin
-     *                                   package. If null, the package will be installed from
-     *                                   another user if possible.
-     * @param deviceInitializerPackageLocation The file system location of a downloaded device
-     *                                         initializer package. If null, the package will be
-     *                                         installed from another user if possible.
      */
-    public void run(String deviceAdminPackageLocation, String deviceInitializerPackageLocation) {
-        if (!TextUtils.isEmpty(mDeviceAdminPackageName)) {
-            mPackagesToInstall.add(new InstallInfo(
-                    mDeviceAdminPackageName, deviceAdminPackageLocation));
-        }
-        if (!TextUtils.isEmpty(mDeviceInitializerPackageName)) {
-            mPackagesToInstall.add(new InstallInfo(
-                    mDeviceInitializerPackageName, deviceInitializerPackageLocation));
-        }
-
+    public void run() {
         if (mPackagesToInstall.size() == 0) {
             ProvisionLogger.loge("No downloaded packages to install");
             mCallback.onSuccess();
@@ -245,4 +228,4 @@
             mLocation = location;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/managedprovisioning/task/WipeResetProtectionTask.java b/src/com/android/managedprovisioning/task/WipeResetProtectionTask.java
index 75aa17e..78f620d 100644
--- a/src/com/android/managedprovisioning/task/WipeResetProtectionTask.java
+++ b/src/com/android/managedprovisioning/task/WipeResetProtectionTask.java
@@ -64,15 +64,16 @@
 
     /**
      * @param context used to register receivers and get system services
-     * @param params holds FRP unlock data
+     * @param frpChallengeBundle holds FRP unlock data
      * @param callback called when this task finishes
      */
-    public WipeResetProtectionTask(Context context, ProvisioningParams params, Callback callback) {
+    public WipeResetProtectionTask(Context context, PersistableBundle frpChallengeBundle,
+            Callback callback) {
         mContext = context;
-        if (params.mFrpChallengeBundle == null) {
+        if (frpChallengeBundle == null) {
             mChallengeData = new Bundle();
         } else {
-            mChallengeData = new Bundle(params.mFrpChallengeBundle);
+            mChallengeData = new Bundle(frpChallengeBundle);
         }
         mDataBlockManager = (PersistentDataBlockManager) mContext.getSystemService(
                 Context.PERSISTENT_DATA_BLOCK_SERVICE);