Fix the DeviceOwner Provisioning case where the app has to be downloaded.

We cannot infer the device admin component by inspecting the package
until it has been downloaded.

Change-Id: I62c21416e3bedaee7b2c77a9ba523bd8fecaaaad
diff --git a/src/com/android/managedprovisioning/BootReminder.java b/src/com/android/managedprovisioning/BootReminder.java
index 202cb80..b91d144 100644
--- a/src/com/android/managedprovisioning/BootReminder.java
+++ b/src/com/android/managedprovisioning/BootReminder.java
@@ -18,6 +18,7 @@
 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE;
 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE;
 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME;
 
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -41,6 +42,11 @@
     private static final String PROFILE_OWNER_PREFERENCES_NAME =
             "profile-owner-provisioning-resume";
 
+    private static final String[] PROFILE_OWNER_STRING_EXTRAS = {
+        // Key for the device admin package name
+        EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME
+    };
+
     private static final String[] PROFILE_OWNER_COMPONENT_NAME_EXTRAS = {
         // Key for the device admin component name
         EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME
@@ -143,6 +149,7 @@
     private static IntentStore getProfileOwnerIntentStore(Context context) {
         return new IntentStore(context,PROFILE_OWNER_INTENT_TARGET, PROFILE_OWNER_PREFERENCES_NAME)
                 .setComponentNameKeys(PROFILE_OWNER_COMPONENT_NAME_EXTRAS)
+                .setStringKeys(PROFILE_OWNER_STRING_EXTRAS)
                 .setPersistableBundleKeys(PROFILE_OWNER_PERSISTABLE_BUNDLE_EXTRAS)
                 .setAccountKeys(PROFILE_OWNER_ACCOUNT_EXTRAS);
     }
diff --git a/src/com/android/managedprovisioning/DeviceOwnerProvisioningActivity.java b/src/com/android/managedprovisioning/DeviceOwnerProvisioningActivity.java
index 65cbe6c..0121780 100644
--- a/src/com/android/managedprovisioning/DeviceOwnerProvisioningActivity.java
+++ b/src/com/android/managedprovisioning/DeviceOwnerProvisioningActivity.java
@@ -148,7 +148,7 @@
         // Parse the incoming intent.
         MessageParser parser = new MessageParser();
         try {
-            mParams = parser.parseIntent(getIntent(), this);
+            mParams = parser.parseIntent(getIntent());
         } catch (Utils.IllegalProvisioningArgumentException e) {
             ProvisionLogger.loge("Could not read data from intent", e);
             error(R.string.device_owner_error_general, false /* no factory reset */);
diff --git a/src/com/android/managedprovisioning/DeviceOwnerProvisioningService.java b/src/com/android/managedprovisioning/DeviceOwnerProvisioningService.java
index 06f2773..3d5dd54 100644
--- a/src/com/android/managedprovisioning/DeviceOwnerProvisioningService.java
+++ b/src/com/android/managedprovisioning/DeviceOwnerProvisioningService.java
@@ -176,7 +176,13 @@
 
             // Send complete intent to mdm.
             Intent result = new Intent(ACTION_PROFILE_PROVISIONING_COMPLETE);
-            result.setPackage(mParams.mDeviceAdminComponentName.getPackageName());
+            try {
+                result.setComponent(mParams.inferDeviceAdminComponentName(context));
+            } catch (Utils.IllegalProvisioningArgumentException e) {
+                stopSelf();
+                ProvisionLogger.loge("Failed to infer the device admin component name", e);
+                return;
+            }
             result.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES |
                     Intent.FLAG_RECEIVER_FOREGROUND);
             if (mParams.mAdminExtrasBundle != null) {
@@ -240,12 +246,22 @@
                     });
 
         mInstallPackageTask = new InstallPackageTask(this,
-                params.getDeviceAdminPackageName(), params.mDeviceAdminPackageDownloadLocation,
+                params.inferDeviceAdminPackageName(), params.mDeviceAdminPackageDownloadLocation,
                 new InstallPackageTask.Callback() {
                     @Override
                     public void onSuccess() {
                         progressUpdate(R.string.progress_set_owner);
-                        mSetDevicePolicyTask.run();
+                        try {
+                            // Now that the app has been installed, we can look for the device admin
+                            // component in it.
+                            mSetDevicePolicyTask.run(mParams.inferDeviceAdminComponentName(
+                                    DeviceOwnerProvisioningService.this));
+                        } catch (Utils.IllegalProvisioningArgumentException e) {
+                            error(R.string.device_owner_error_general);
+                            ProvisionLogger.loge("Failed to infer the device admin component name",
+                                    e);
+                            return;
+                        }
                     }
 
                     @Override
@@ -263,9 +279,7 @@
                         }
                     }
                 });
-
         mSetDevicePolicyTask = new SetDevicePolicyTask(this,
-                params.mDeviceAdminComponentName,
                 getResources().getString(R.string.default_owned_device_username),
                 new SetDevicePolicyTask.Callback() {
                     @Override
@@ -290,7 +304,7 @@
                 });
 
         mDeleteNonRequiredAppsTask = new DeleteNonRequiredAppsTask(
-                this, params.getDeviceAdminPackageName(), R.array.required_apps_managed_device,
+                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,
                 new DeleteNonRequiredAppsTask.Callback() {
diff --git a/src/com/android/managedprovisioning/IntentStore.java b/src/com/android/managedprovisioning/IntentStore.java
index 8e0ad74..3656f09 100644
--- a/src/com/android/managedprovisioning/IntentStore.java
+++ b/src/com/android/managedprovisioning/IntentStore.java
@@ -138,7 +138,9 @@
         }
         for (String key : mComponentNameKeys) {
             ComponentName cn = (ComponentName) data.getParcelable(key);
-            editor.putString(key, cn.flattenToString());
+            if (cn != null) {
+                editor.putString(key, cn.flattenToString());
+            }
         }
         editor.putBoolean(IS_SET, true);
         editor.commit();
@@ -191,8 +193,10 @@
         }
         for (String key : mComponentNameKeys) {
             if (mPrefs.contains(key)) {
-                result.putExtra(key, ComponentName.unflattenFromString(
-                        mPrefs.getString(key, null)));
+                String st = mPrefs.getString(key, null);
+                if (st != null) {
+                    result.putExtra(key, ComponentName.unflattenFromString(st));
+                }
             }
         }
 
diff --git a/src/com/android/managedprovisioning/MessageParser.java b/src/com/android/managedprovisioning/MessageParser.java
index d379e86..2e8e09e 100644
--- a/src/com/android/managedprovisioning/MessageParser.java
+++ b/src/com/android/managedprovisioning/MessageParser.java
@@ -114,7 +114,7 @@
         EXTRA_PROVISIONING_WIFI_PROXY_HOST,
         EXTRA_PROVISIONING_WIFI_PROXY_BYPASS,
         EXTRA_PROVISIONING_WIFI_PAC_URL,
-        EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME,
+        EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME,
         EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION,
         EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER,
         EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM
@@ -152,6 +152,8 @@
         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_DEVICE_ADMIN_PACKAGE_NAME,
+                params.mDeviceAdminPackageName);
         bundle.putParcelable(EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME,
                 params.mDeviceAdminComponentName);
         bundle.putString(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION,
@@ -174,17 +176,17 @@
         bundle.putBoolean(EXTRA_PROVISIONING_SKIP_ENCRYPTION, params.mSkipEncryption);
     }
 
-    public ProvisioningParams parseIntent(Intent intent, Context c)
+    public ProvisioningParams parseIntent(Intent intent)
             throws IllegalProvisioningArgumentException {
         ProvisionLogger.logi("Processing intent.");
         if (intent.hasExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)) {
-            return parseNfcIntent(intent, c);
+            return parseNfcIntent(intent);
         } else {
-            return parseNonNfcIntent(intent, c);
+            return parseNonNfcIntent(intent);
         }
     }
 
-    public ProvisioningParams parseNfcIntent(Intent nfcIntent, Context c)
+    public ProvisioningParams parseNfcIntent(Intent nfcIntent)
             throws IllegalProvisioningArgumentException {
         ProvisionLogger.logi("Processing Nfc Payload.");
         // Only one first message with NFC_MIME_TYPE is used.
@@ -198,7 +200,7 @@
 
             if (MIME_TYPE_PROVISIONING_NFC.equals(mimeType)) {
                 ProvisioningParams params = parseProperties(new String(firstRecord.getPayload()
-                                , UTF_8), c);
+                                , UTF_8));
                 params.mStartedByNfc = true;
                 return params;
             }
@@ -210,7 +212,7 @@
     // Note: EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE property contains a Properties object
     // serialized into String. See Properties.store() and Properties.load() for more details.
     // The property value is optional.
-    private ProvisioningParams parseProperties(String data, Context c)
+    private ProvisioningParams parseProperties(String data)
             throws IllegalProvisioningArgumentException {
         ProvisioningParams params = new ProvisioningParams();
         try {
@@ -229,7 +231,14 @@
             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.mDeviceAdminComponentName = findDeviceAdminFromProperties(props, c);
+            params.mDeviceAdminPackageName
+                    = 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(
+                        componentNameString);
+            }
             params.mDeviceAdminPackageDownloadLocation
                     = props.getProperty(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION);
             params.mDeviceAdminPackageDownloadCookieHeader = props.getProperty(
@@ -284,7 +293,7 @@
         }
     }
 
-    public ProvisioningParams parseNonNfcIntent(Intent intent, Context c)
+    public ProvisioningParams parseNonNfcIntent(Intent intent)
             throws IllegalProvisioningArgumentException {
         ProvisionLogger.logi("Processing intent.");
         ProvisioningParams params = new ProvisioningParams();
@@ -300,7 +309,10 @@
         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 = Utils.findDeviceAdminFromIntent(intent, c);
+        params.mDeviceAdminComponentName = (ComponentName) intent.getParcelableExtra(
+                EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME);
+        params.mDeviceAdminPackageName
+                = intent.getStringExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME);
         params.mDeviceAdminPackageDownloadLocation
                 = intent.getStringExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION);
         params.mDeviceAdminPackageDownloadCookieHeader = intent.getStringExtra(
@@ -340,24 +352,16 @@
         return params;
     }
 
-    private ComponentName findDeviceAdminFromProperties(Properties props, Context c)
-            throws IllegalProvisioningArgumentException {
-        String packageName = props.getProperty(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME);
-        ComponentName component = null;
-        String s;
-        if ((s = props.getProperty(EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME)) != null) {
-            component = ComponentName.unflattenFromString(s);
-        }
-        return Utils.findDeviceAdmin(packageName, component, c);
-    }
-
     /**
      * Check whether necessary fields are set.
      */
     private void checkValidityOfProvisioningParams(ProvisioningParams params)
             throws IllegalProvisioningArgumentException  {
-        // The presence of the device admin component name was already checked when calling
-        // Utils.findDeviceAdmin()
+        if (TextUtils.isEmpty(params.mDeviceAdminPackageName)
+                && params.mDeviceAdminComponentName == 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) {
diff --git a/src/com/android/managedprovisioning/ProvisioningParams.java b/src/com/android/managedprovisioning/ProvisioningParams.java
index 16238a6..dd7a089 100644
--- a/src/com/android/managedprovisioning/ProvisioningParams.java
+++ b/src/com/android/managedprovisioning/ProvisioningParams.java
@@ -16,13 +16,13 @@
 
 package com.android.managedprovisioning;
 
+import android.content.Context;
 import android.content.ComponentName;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.PersistableBundle;
 import android.util.Base64;
 import java.util.Locale;
-
 /**
  * Provisioning Parameters for DeviceOwner Provisioning
  */
@@ -46,8 +46,29 @@
     public String mWifiProxyBypassHosts;
     public String mWifiPacUrl;
 
+    // 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;
 
+    private ComponentName mInferedDeviceAdminComponentName;
+
+    String inferDeviceAdminPackageName() {
+        if (mDeviceAdminComponentName != null) {
+            return mDeviceAdminComponentName.getPackageName();
+        }
+        return mDeviceAdminPackageName;
+    }
+
+    // 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);
+        }
+        return mInferedDeviceAdminComponentName;
+    }
+
     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.
@@ -59,10 +80,6 @@
     public boolean mLeaveAllSystemAppsEnabled;
     public boolean mSkipEncryption;
 
-    public String getDeviceAdminPackageName() {
-        return mDeviceAdminComponentName.getPackageName();
-    }
-
     public String getLocaleAsString() {
         if (mLocale != null) {
             return mLocale.getLanguage() + "_" + mLocale.getCountry();
@@ -93,6 +110,7 @@
         out.writeString(mWifiProxyHost);
         out.writeInt(mWifiProxyPort);
         out.writeString(mWifiProxyBypassHosts);
+        out.writeString(mDeviceAdminPackageName);
         out.writeParcelable(mDeviceAdminComponentName, 0 /* default */);
         out.writeString(mDeviceAdminPackageDownloadLocation);
         out.writeString(mDeviceAdminPackageDownloadCookieHeader);
@@ -119,6 +137,7 @@
             params.mWifiProxyHost = in.readString();
             params.mWifiProxyPort = in.readInt();
             params.mWifiProxyBypassHosts = in.readString();
+            params.mDeviceAdminPackageName = in.readString();
             params.mDeviceAdminComponentName = (ComponentName)
                     in.readParcelable(null /* use default classloader */);
             params.mDeviceAdminPackageDownloadLocation = in.readString();
diff --git a/src/com/android/managedprovisioning/task/SetDevicePolicyTask.java b/src/com/android/managedprovisioning/task/SetDevicePolicyTask.java
index 29b8a96..2a7b7f5 100644
--- a/src/com/android/managedprovisioning/task/SetDevicePolicyTask.java
+++ b/src/com/android/managedprovisioning/task/SetDevicePolicyTask.java
@@ -35,26 +35,25 @@
 
     private final Callback mCallback;
     private final Context mContext;
-    private final String mAdminPackage;
-    private final ComponentName mAdminComponent;
+    private String mAdminPackage;
+    private ComponentName mAdminComponent;
     private final String mOwner;
 
     private PackageManager mPackageManager;
     private DevicePolicyManager mDevicePolicyManager;
 
-    public SetDevicePolicyTask(Context context, ComponentName adminComponent, String owner,
-            Callback callback) {
+    public SetDevicePolicyTask(Context context, String owner, Callback callback) {
         mCallback = callback;
         mContext = context;
-        mAdminComponent = adminComponent;
-        mAdminPackage = adminComponent.getPackageName();
         mOwner = owner;
         mPackageManager = mContext.getPackageManager();
         mDevicePolicyManager = (DevicePolicyManager) mContext.
                 getSystemService(Context.DEVICE_POLICY_SERVICE);
     }
 
-    public void run() {
+    public void run(ComponentName adminComponent) {
+        mAdminComponent = adminComponent;
+        mAdminPackage = mAdminComponent.getPackageName();
         enableDevicePolicyApp();
         setActiveAdmin();
         setDeviceOwner();