am d3e66f64: am f88f709f: Pass through the provisioning extra admin extras bundle.

* commit 'd3e66f649e30ce482b6b55785c1ba21fb7e2e07f':
  Pass through the provisioning extra admin extras bundle.
diff --git a/src/com/android/managedprovisioning/BootReminder.java b/src/com/android/managedprovisioning/BootReminder.java
index 91f26b2..c0b1e26 100644
--- a/src/com/android/managedprovisioning/BootReminder.java
+++ b/src/com/android/managedprovisioning/BootReminder.java
@@ -15,6 +15,7 @@
  */
 package com.android.managedprovisioning;
 
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE;
 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEFAULT_MANAGED_PROFILE_NAME;
 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME;
 
@@ -49,6 +50,11 @@
         EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME
     };
 
+    private static final String[] PROFILE_OWNER_PERSISTABLE_BUNDLE_EXTRAS = {
+        // Key for the admin extras bundle
+        EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE
+    };
+
     private static final ComponentName PROFILE_OWNER_INTENT_TARGET =
             ManagedProvisioningActivity.ALIAS_NO_CHECK_CALLER;
 
@@ -134,23 +140,18 @@
     }
 
     private static IntentStore getProfileOwnerIntentStore(Context context) {
-        return new IntentStore(context,
-                PROFILE_OWNER_STRING_EXTRAS,
-                new String[0],
-                new String[0],
-                new String[0],
-                PROFILE_OWNER_INTENT_TARGET,
-                PROFILE_OWNER_PREFERENCES_NAME);
+        return new IntentStore(context,PROFILE_OWNER_INTENT_TARGET, PROFILE_OWNER_PREFERENCES_NAME)
+                .setStringKeys(PROFILE_OWNER_STRING_EXTRAS)
+                .setPersistableBundleKeys(PROFILE_OWNER_PERSISTABLE_BUNDLE_EXTRAS);
     }
 
     private static IntentStore getDeviceOwnerIntentStore(Context context) {
-        return new IntentStore(context,
-                MessageParser.DEVICE_OWNER_STRING_EXTRAS,
-                MessageParser.DEVICE_OWNER_LONG_EXTRAS,
-                MessageParser.DEVICE_OWNER_INT_EXTRAS,
-                MessageParser.DEVICE_OWNER_BOOLEAN_EXTRAS,
-                DEVICE_OWNER_INTENT_TARGET,
-                DEVICE_OWNER_PREFERENCES_NAME);
+        return new IntentStore(context, DEVICE_OWNER_INTENT_TARGET, DEVICE_OWNER_PREFERENCES_NAME)
+                .setStringKeys(MessageParser.DEVICE_OWNER_STRING_EXTRAS)
+                .setLongKeys(MessageParser.DEVICE_OWNER_LONG_EXTRAS)
+                .setIntKeys(MessageParser.DEVICE_OWNER_INT_EXTRAS)
+                .setBooleanKeys(MessageParser.DEVICE_OWNER_BOOLEAN_EXTRAS)
+                .setPersistableBundleKeys(MessageParser.DEVICE_OWNER_PERSISTABLE_BUNDLE_EXTRAS);
     }
 
     /** Create and show the provisioning reminder notification. */
diff --git a/src/com/android/managedprovisioning/DeviceOwnerProvisioningActivity.java b/src/com/android/managedprovisioning/DeviceOwnerProvisioningActivity.java
index 93be0c2..c34a5f2 100644
--- a/src/com/android/managedprovisioning/DeviceOwnerProvisioningActivity.java
+++ b/src/com/android/managedprovisioning/DeviceOwnerProvisioningActivity.java
@@ -17,6 +17,7 @@
 package com.android.managedprovisioning;
 
 import static android.app.admin.DeviceAdminReceiver.ACTION_PROFILE_PROVISIONING_COMPLETE;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE;
 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_EMAIL_ADDRESS;
 
 import android.app.Activity;
@@ -74,7 +75,10 @@
     private TextView mProgressTextView;
     private Dialog mDialog; // The cancel or error dialog that is currently shown.
     private boolean mDone; // Indicates whether the service has sent ACTION_PROVISIONING_SUCCESS.
-    private ProvisioningParams mParams;
+
+    private Runnable mOnWifiConnectedRunnable;
+
+    private Intent mProvisioningCompleteIntent;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -119,9 +123,10 @@
         LocalBroadcastManager.getInstance(this).registerReceiver(mServiceMessageReceiver, filter);
 
         // Parse the incoming intent.
+        final ProvisioningParams params;
         MessageParser parser = new MessageParser();
         try {
-            mParams = parser.parseIntent(getIntent());
+            params = parser.parseIntent(getIntent());
         } catch (MessageParser.ParseException e) {
             ProvisionLogger.loge("Could not read data from intent", e);
             error(e.getErrorMessageId(), false /* no factory reset */);
@@ -130,26 +135,33 @@
 
         // Ask to encrypt the device before proceeding
         if (!EncryptDeviceActivity.isDeviceEncrypted()) {
-            requestEncryption(parser);
+            requestEncryption(parser, params);
             finish();
             return;
             // System will reboot. Bootreminder will restart this activity.
         }
 
         // Have the user pick a wifi network if necessary.
-        if (!AddWifiNetworkTask.isConnectedToWifi(this) && TextUtils.isEmpty(mParams.mWifiSsid) &&
-                !TextUtils.isEmpty(mParams.mDeviceAdminPackageDownloadLocation)) {
+        if (!AddWifiNetworkTask.isConnectedToWifi(this) && TextUtils.isEmpty(params.mWifiSsid) &&
+                !TextUtils.isEmpty(params.mDeviceAdminPackageDownloadLocation)) {
+
+            mOnWifiConnectedRunnable = new Runnable() {
+                public void run() {
+                    startDeviceOwnerProvisioningService(params);
+                }
+            };
+
             requestWifiPick();
             return;
             // Wait for onActivityResult.
         }
 
-        startDeviceOwnerProvisioningService();
+        startDeviceOwnerProvisioningService(params);
     }
 
-    private void startDeviceOwnerProvisioningService() {
+    private void startDeviceOwnerProvisioningService(ProvisioningParams params) {
         Intent intent = new Intent(this, DeviceOwnerProvisioningService.class);
-        intent.putExtra(DeviceOwnerProvisioningService.EXTRA_PROVISIONING_PARAMS, mParams);
+        intent.putExtra(DeviceOwnerProvisioningService.EXTRA_PROVISIONING_PARAMS, params);
         intent.putExtras(getIntent());
         startService(intent);
     }
@@ -162,6 +174,9 @@
             String action = intent.getAction();
             if (action.equals(DeviceOwnerProvisioningService.ACTION_PROVISIONING_SUCCESS)) {
                 ProvisionLogger.logd("Successfully provisioned");
+
+                mProvisioningCompleteIntent = getProvisioningCompleteIntent(intent);
+
                 synchronized(this) {
                     if (mDialog == null) {
                         onProvisioningSuccess();
@@ -192,6 +207,25 @@
         }
     }
 
+    private Intent getProvisioningCompleteIntent(Intent serviceIntent) {
+        ProvisioningParams paramsFromService = (ProvisioningParams) serviceIntent.getExtra(
+                DeviceOwnerProvisioningService.EXTRA_PROVISIONING_PARAMS);
+
+        Intent result = new Intent(ACTION_PROFILE_PROVISIONING_COMPLETE);
+        result.setPackage(paramsFromService.mDeviceAdminPackageName);
+        result.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES |
+                Intent.FLAG_RECEIVER_FOREGROUND);
+        if (paramsFromService.mManagedDeviceEmailAddress != null) {
+            result.putExtra(EXTRA_PROVISIONING_EMAIL_ADDRESS,
+                    paramsFromService.mManagedDeviceEmailAddress);
+        }
+        if (paramsFromService.mAdminExtrasBundle != null) {
+            result.putExtra(EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE,
+                    paramsFromService.mAdminExtrasBundle);
+        }
+        return result;
+    }
+
     private void onProvisioningSuccess() {
         stopService(new Intent(DeviceOwnerProvisioningActivity.this,
                         DeviceOwnerProvisioningService.class));
@@ -200,29 +234,20 @@
         Global.putInt(getContentResolver(), Global.DEVICE_PROVISIONED, 1);
         Secure.putInt(getContentResolver(), Secure.USER_SETUP_COMPLETE, 1);
 
-        Intent completeIntent = new Intent(ACTION_PROFILE_PROVISIONING_COMPLETE);
-        completeIntent.setPackage(mParams.mDeviceAdminPackageName);
-        completeIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES |
-                Intent.FLAG_RECEIVER_FOREGROUND);
-        if (mParams.mManagedDeviceEmailAddress != null) {
-            completeIntent.putExtra(EXTRA_PROVISIONING_EMAIL_ADDRESS,
-                    mParams.mManagedDeviceEmailAddress);
-        }
-
-        sendBroadcast(completeIntent);
+        sendBroadcast(mProvisioningCompleteIntent);
 
         setResult(Activity.RESULT_OK);
         finish();
     }
 
-    private void requestEncryption(MessageParser messageParser) {
+    private void requestEncryption(MessageParser messageParser, ProvisioningParams params) {
         Intent encryptIntent = new Intent(DeviceOwnerProvisioningActivity.this,
                 EncryptDeviceActivity.class);
 
         Bundle resumeExtras = new Bundle();
         resumeExtras.putString(EncryptDeviceActivity.EXTRA_RESUME_TARGET,
                 EncryptDeviceActivity.TARGET_DEVICE_OWNER);
-        messageParser.addProvisioningParamsToBundle(resumeExtras, mParams);
+        messageParser.addProvisioningParamsToBundle(resumeExtras, params);
 
         encryptIntent.putExtra(EncryptDeviceActivity.EXTRA_RESUME, resumeExtras);
 
@@ -297,7 +322,7 @@
             } else if (resultCode == RESULT_OK) {
                 ProvisionLogger.logd("Wifi request result is OK");
                 if (AddWifiNetworkTask.isConnectedToWifi(this)) {
-                    startDeviceOwnerProvisioningService();
+                    mOnWifiConnectedRunnable.run();
                 } else {
                     requestWifiPick();
                 }
diff --git a/src/com/android/managedprovisioning/DeviceOwnerProvisioningService.java b/src/com/android/managedprovisioning/DeviceOwnerProvisioningService.java
index 2181e35..77d7358 100644
--- a/src/com/android/managedprovisioning/DeviceOwnerProvisioningService.java
+++ b/src/com/android/managedprovisioning/DeviceOwnerProvisioningService.java
@@ -294,6 +294,7 @@
         ProvisionLogger.logv("Reporting success.");
         mDone = true;
         Intent successIntent = new Intent(ACTION_PROVISIONING_SUCCESS);
+        successIntent.putExtra(EXTRA_PROVISIONING_PARAMS, mParams);
         successIntent.setClass(this, DeviceOwnerProvisioningActivity.ServiceMessageReceiver.class);
         LocalBroadcastManager.getInstance(this).sendBroadcast(successIntent);
         // Wait for stopService() call from the activity.
diff --git a/src/com/android/managedprovisioning/IntentStore.java b/src/com/android/managedprovisioning/IntentStore.java
index 58b1f24..994672c 100644
--- a/src/com/android/managedprovisioning/IntentStore.java
+++ b/src/com/android/managedprovisioning/IntentStore.java
@@ -21,6 +21,17 @@
 import android.content.SharedPreferences;
 
 import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.util.Xml;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+import org.xmlpull.v1.XmlSerializer;
 
 /**
  * Helper class to load/save resume information from Intents into a SharedPreferences.
@@ -29,26 +40,51 @@
     private SharedPreferences mPrefs;
     private String mPrefsName; // Name of the file where mPrefs is stored.
     private Context mContext;
-    private String[] mStringKeys;
-    private String[] mLongKeys;
-    private String[] mIntKeys;
-    private String[] mBooleanKeys;
     private ComponentName mIntentTarget;
 
+    // Key arrays should never be null.
+    private String[] mStringKeys = new String[0];
+    private String[] mLongKeys = new String[0];
+    private String[] mIntKeys = new String[0];
+    private String[] mBooleanKeys = new String[0];
+    private String[] mPersistableBundleKeys = new String[0];
+
+    private static final String TAG_PERSISTABLEBUNDLE = "persistable_bundle";
+
     private static final String IS_SET = "isSet";
 
-    public IntentStore(Context context, String[] stringKeys, String[] longKeys, String[] intKeys,
-            String[] booleanKeys, ComponentName intentTarget, String preferencesName) {
+    public IntentStore(Context context, ComponentName intentTarget, String preferencesName) {
         mContext = context;
         mPrefsName = preferencesName;
         mPrefs = context.getSharedPreferences(preferencesName, Context.MODE_PRIVATE);
-        mStringKeys = stringKeys;
-        mLongKeys = longKeys;
-        mIntKeys = intKeys;
-        mBooleanKeys = booleanKeys;
         mIntentTarget = intentTarget;
     }
 
+    public IntentStore setStringKeys(String[] keys) {
+        mStringKeys = (keys == null) ? new String[0] : keys;
+        return this;
+    }
+
+    public IntentStore setLongKeys(String[] keys) {
+        mLongKeys = (keys == null) ? new String[0] : keys;
+        return this;
+    }
+
+    public IntentStore setIntKeys(String[] keys) {
+        mIntKeys = (keys == null) ? new String[0] : keys;
+        return this;
+    }
+
+    public IntentStore setBooleanKeys(String[] keys) {
+        mBooleanKeys = (keys == null) ? new String[0] : keys;
+        return this;
+    }
+
+    public IntentStore setPersistableBundleKeys(String[] keys) {
+        mPersistableBundleKeys = (keys == null) ? new String[0] : keys;
+        return this;
+    }
+
     public void clear() {
         mPrefs.edit().clear().commit();
     }
@@ -69,6 +105,12 @@
         for (String key : mBooleanKeys) {
             editor.putBoolean(key, data.getBoolean(key));
         }
+        for (String key : mPersistableBundleKeys) {
+
+            // Cast should be guaranteed to succeed by check in the provisioning activities.
+            editor.putString(key, persistableBundleToString((PersistableBundle) data
+                            .getParcelable(key)));
+        }
         editor.putBoolean(IS_SET, true);
         editor.commit();
     }
@@ -102,7 +144,55 @@
                 result.putExtra(key, mPrefs.getBoolean(key, false));
             }
         }
+        for (String key : mPersistableBundleKeys) {
+            if (mPrefs.contains(key)) {
+                PersistableBundle bundle = stringToPersistableBundle(mPrefs.getString(key, null));
+                if (bundle != null) {
+                    result.putExtra(key, bundle);
+                }
+            }
+        }
 
         return result;
     }
+
+    private String persistableBundleToString(PersistableBundle bundle) {
+        StringWriter writer = new StringWriter();
+        XmlSerializer serializer = Xml.newSerializer();
+        try {
+            serializer.setOutput(writer);
+            serializer.startDocument(null, true);
+            serializer.startTag(null, TAG_PERSISTABLEBUNDLE);
+            bundle.saveToXml(serializer);
+            serializer.endTag(null, TAG_PERSISTABLEBUNDLE);
+            serializer.endDocument();
+        } catch (IOException|XmlPullParserException e) {
+            ProvisionLogger.loge("Persistable bundle could not be stored as string.", e);
+            return null;
+        }
+
+        return writer.toString();
+    }
+
+    private PersistableBundle stringToPersistableBundle(String string) {
+        XmlPullParserFactory factory;
+        XmlPullParser parser;
+        try {
+            factory = XmlPullParserFactory.newInstance();
+
+            parser = factory.newPullParser();
+            parser.setInput(new StringReader(string));
+
+            if (parser.next() == XmlPullParser.START_TAG) {
+                if (TAG_PERSISTABLEBUNDLE.equals(parser.getName())) {
+                    return PersistableBundle.restoreFromXml(parser);
+                }
+            }
+        } catch (IOException|XmlPullParserException e) {
+            ProvisionLogger.loge(e);
+            // Fall through.
+        }
+        ProvisionLogger.loge("Persistable bundle could not be restored from string " + string);
+        return null;
+    }
 }
diff --git a/src/com/android/managedprovisioning/ManagedProvisioningActivity.java b/src/com/android/managedprovisioning/ManagedProvisioningActivity.java
index 7bbb55c..4d140ff 100644
--- a/src/com/android/managedprovisioning/ManagedProvisioningActivity.java
+++ b/src/com/android/managedprovisioning/ManagedProvisioningActivity.java
@@ -16,6 +16,7 @@
 
 package com.android.managedprovisioning;
 
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE;
 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME;
 import static com.android.managedprovisioning.EncryptDeviceActivity.EXTRA_RESUME;
 import static com.android.managedprovisioning.EncryptDeviceActivity.EXTRA_RESUME_TARGET;
@@ -37,6 +38,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -257,10 +259,20 @@
     /**
      * Checks if all required provisioning parameters are provided.
      * Does not check for extras that are optional such as the email address.
+     * Also checks whether type of admin extras bundle (if present) is PersistableBundle.
      *
      * @param intent The intent that started provisioning
      */
     private void initialize(Intent intent) throws ManagedProvisioningFailedException {
+        // Check if the admin extras bundle is of the right type.
+        try {
+            PersistableBundle bundle = (PersistableBundle) getIntent().getParcelableExtra(
+                    EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE);
+        } catch (ClassCastException e) {
+            throw new ManagedProvisioningFailedException("Extra "
+                    + EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE
+                    + " must be of type PersistableBundle.", e);
+        }
 
         // Validate package name and check if the package is installed
         mMdmPackageName = getMdmPackageName(intent);
@@ -272,7 +284,7 @@
                 this.getPackageManager().getPackageInfo(mMdmPackageName, 0);
             } catch (NameNotFoundException e) {
                 throw new ManagedProvisioningFailedException("Mdm "+ mMdmPackageName
-                        + " is not installed. " + e);
+                        + " is not installed. ", e);
             }
         }
     }
@@ -481,9 +493,13 @@
      * significant.
      */
     private class ManagedProvisioningFailedException extends Exception {
-      public ManagedProvisioningFailedException(String message) {
-          super(message);
-      }
+        public ManagedProvisioningFailedException(String message) {
+            super(message);
+        }
+
+        public ManagedProvisioningFailedException(String message, Throwable t) {
+            super(message, t);
+        }
     }
 }
 
diff --git a/src/com/android/managedprovisioning/ManagedProvisioningService.java b/src/com/android/managedprovisioning/ManagedProvisioningService.java
index 4089468..77cf42e 100644
--- a/src/com/android/managedprovisioning/ManagedProvisioningService.java
+++ b/src/com/android/managedprovisioning/ManagedProvisioningService.java
@@ -17,6 +17,7 @@
 package com.android.managedprovisioning;
 
 import static android.app.admin.DeviceAdminReceiver.ACTION_PROFILE_PROVISIONING_COMPLETE;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE;
 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEFAULT_MANAGED_PROFILE_NAME;
 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME;
 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_EMAIL_ADDRESS;
@@ -38,6 +39,8 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.UserInfo;
 import android.os.AsyncTask;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
 import android.os.IBinder;
 import android.os.Process;
 import android.os.RemoteException;
@@ -72,6 +75,10 @@
     private String mDefaultManagedProfileName;
     private String mManagedProfileEmailAddress;
 
+    // PersistableBundle extra received in starting intent.
+    // Should be passed through to device management application when provisioning is complete.
+    private PersistableBundle mAdminExtrasBundle;
+
     private IPackageManager mIpm;
     private UserInfo mManagedProfileUserInfo;
     private UserManager mUserManager;
@@ -118,6 +125,10 @@
         mManagedProfileEmailAddress =
                 intent.getStringExtra(EXTRA_PROVISIONING_EMAIL_ADDRESS);
 
+        // Cast is guaranteed by check in Activity.
+        mAdminExtrasBundle  = (PersistableBundle) intent.getParcelableExtra(
+                EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE);
+
         mActiveAdminComponentName = getAdminReceiverComponent(mMdmPackageName);
         mDefaultManagedProfileName = getDefaultManagedProfileName(intent);
     }
@@ -307,6 +318,9 @@
         if (mManagedProfileEmailAddress != null) {
             completeIntent.putExtra(EXTRA_PROVISIONING_EMAIL_ADDRESS, mManagedProfileEmailAddress);
         }
+        if (mAdminExtrasBundle != null) {
+            completeIntent.putExtra(EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE, mAdminExtrasBundle);
+        }
         context.sendBroadcastAsUser(completeIntent, userHandle);
 
         ProvisionLogger.logd("Provisioning complete broadcast has been sent to user "
diff --git a/src/com/android/managedprovisioning/MessageParser.java b/src/com/android/managedprovisioning/MessageParser.java
index 101909d..0452166 100644
--- a/src/com/android/managedprovisioning/MessageParser.java
+++ b/src/com/android/managedprovisioning/MessageParser.java
@@ -27,6 +27,7 @@
 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_WIFI_PROXY_PORT;
 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_WIFI_PROXY_BYPASS;
 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_WIFI_PAC_URL;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE;
 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME;
 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION;
 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER;
@@ -41,6 +42,7 @@
 import android.nfc.NfcAdapter;
 import android.os.Bundle;
 import android.os.Parcelable;
+import android.os.PersistableBundle;
 import android.text.TextUtils;
 import android.util.Base64;
 
@@ -74,6 +76,8 @@
  * together with an optional http cookie header
  * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER} accompanied by the SHA-1
  * sum of the target file {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM}.
+ * Additional information to send through to the device admin may be specified in
+ * {@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}.
  * Furthermore a wifi network may be specified in {@link #EXTRA_PROVISIONING_WIFI_SSID}, and if
  * applicable {@link #EXTRA_PROVISIONING_WIFI_HIDDEN},
  * {@link #EXTRA_PROVISIONING_WIFI_SECURITY_TYPE}, {@link #EXTRA_PROVISIONING_WIFI_PASSWORD},
@@ -115,6 +119,10 @@
         EXTRA_PROVISIONING_WIFI_HIDDEN
     };
 
+    protected static final String[] DEVICE_OWNER_PERSISTABLE_BUNDLE_EXTRAS = {
+        EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE
+    };
+
     public void addProvisioningParamsToBundle(Bundle bundle, ProvisioningParams params) {
         bundle.putString(EXTRA_PROVISIONING_TIME_ZONE, params.mTimeZone);
         bundle.putString(EXTRA_PROVISIONING_LOCALE, params.getLocaleAsString());
@@ -139,6 +147,8 @@
         bundle.putInt(EXTRA_PROVISIONING_WIFI_PROXY_PORT, params.mWifiProxyPort);
 
         bundle.putBoolean(EXTRA_PROVISIONING_WIFI_HIDDEN, params.mWifiHidden);
+
+        bundle.putParcelable(EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE, params.mAdminExtrasBundle);
     }
 
     public ProvisioningParams parseIntent(Intent intent) throws ParseException {
@@ -171,6 +181,9 @@
                 R.string.device_owner_error_general);
     }
 
+    // Note: You can't include the EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE in the properties that is
+    // send over Nfc, because there is no publicly documented conversion from PersistableBundle to
+    // String.
     private ProvisioningParams parseProperties(String data)
             throws ParseException {
         ProvisioningParams params = new ProvisioningParams();
@@ -264,6 +277,14 @@
 
         params.mWifiHidden = intent.getBooleanExtra(EXTRA_PROVISIONING_WIFI_HIDDEN,
                 ProvisioningParams.DEFAULT_WIFI_HIDDEN);
+        try {
+            params.mAdminExtrasBundle = (PersistableBundle) intent.getParcelableExtra(
+                    EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE);
+        } catch (ClassCastException e) {
+            throw new ParseException("Extra " + EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE
+                    + " must be of type PersistableBundle.",
+                    R.string.device_owner_error_general, e);
+        }
 
         checkValidityOfProvisioningParams(params);
         return params;
diff --git a/src/com/android/managedprovisioning/ProvisioningParams.java b/src/com/android/managedprovisioning/ProvisioningParams.java
index 6a56e37..25a096f 100644
--- a/src/com/android/managedprovisioning/ProvisioningParams.java
+++ b/src/com/android/managedprovisioning/ProvisioningParams.java
@@ -18,6 +18,7 @@
 
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.PersistableBundle;
 import android.util.Base64;
 import java.util.Locale;
 
@@ -51,6 +52,8 @@
 
     public String mManagedDeviceEmailAddress;
 
+    public PersistableBundle mAdminExtrasBundle;
+
     public String getLocaleAsString() {
         if (mLocale != null) {
             return mLocale.getLanguage() + "_" + mLocale.getCountry();
@@ -87,6 +90,7 @@
         out.writeInt(mDeviceAdminPackageChecksum.length);
         out.writeByteArray(mDeviceAdminPackageChecksum);
         out.writeString(mManagedDeviceEmailAddress);
+        out.writeParcelable(mAdminExtrasBundle, 0 /* default */);
     }
 
     public static final Parcelable.Creator<ProvisioningParams> CREATOR
@@ -111,6 +115,7 @@
             params.mDeviceAdminPackageChecksum = new byte[checksumLength];
             in.readByteArray(params.mDeviceAdminPackageChecksum);
             params.mManagedDeviceEmailAddress = in.readString();
+            params.mAdminExtrasBundle = in.readParcelable(null /* use default classloader */);
             return params;
         }