Merge "Support launching a device initialization agent during provisioning."
diff --git a/src/com/android/managedprovisioning/DeviceOwnerProvisioningActivity.java b/src/com/android/managedprovisioning/DeviceOwnerProvisioningActivity.java
index 0121780..78c5557 100644
--- a/src/com/android/managedprovisioning/DeviceOwnerProvisioningActivity.java
+++ b/src/com/android/managedprovisioning/DeviceOwnerProvisioningActivity.java
@@ -16,6 +16,9 @@
package com.android.managedprovisioning;
+import static android.app.admin.DeviceAdminReceiver.ACTION_READY_FOR_USER_INITIALIZATION;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE;
+
import android.app.Activity;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
@@ -23,6 +26,7 @@
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.SystemProperties;
@@ -36,11 +40,11 @@
import android.widget.TextView;
import com.android.managedprovisioning.task.AddWifiNetworkTask;
-import com.android.managedprovisioning.Utils.IllegalProvisioningArgumentException;
import com.android.setupwizard.navigationbar.SetupWizardNavBar;
import com.android.setupwizard.navigationbar.SetupWizardNavBar.NavigationBarListener;
import java.util.ArrayList;
+import java.util.List;
/**
* This activity starts device owner provisioning:
@@ -261,17 +265,43 @@
private void onProvisioningSuccess() {
- // The Setup wizards listens to this flag and finishes itself when it is set.
- // It then fires a home intent, which we catch in the HomeReceiverActivity before sending
- // the intent to notify the mdm that provisioning is complete.
- Global.putInt(getContentResolver(), Global.DEVICE_PROVISIONED, 1);
- Secure.putInt(getContentResolver(), Secure.USER_SETUP_COMPLETE, 1);
-
+ if (mParams.mDeviceInitializerComponentName != null) {
+ Intent result = new Intent(ACTION_READY_FOR_USER_INITIALIZATION);
+ result.setComponent(mParams.mDeviceInitializerComponentName);
+ result.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES |
+ Intent.FLAG_RECEIVER_FOREGROUND);
+ if (mParams.mAdminExtrasBundle != null) {
+ result.putExtra(EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE,
+ mParams.mAdminExtrasBundle);
+ }
+ List<ResolveInfo> matchingReceivers =
+ getPackageManager().queryBroadcastReceivers(result, 0);
+ if (matchingReceivers.size() > 0) {
+ // Notify the device initializer that it can now perform pre-user-setup tasks.
+ sendBroadcast(result);
+ } else {
+ ProvisionLogger.logi("Initializer component doesn't have a receiver for "
+ + "android.app.action.READY_FOR_USER_INITIALIZATION. Skipping broadcast "
+ + "and finishing user initialization.");
+ provisionDevice();
+ }
+ } else {
+ // No initializer, set the device provisioned ourselves.
+ provisionDevice();
+ }
// Note: the DeviceOwnerProvisioningService will stop itself.
setResult(Activity.RESULT_OK);
finish();
}
+ private void provisionDevice() {
+ // The Setup wizards listens to this flag and finishes itself when it is set.
+ // It then fires a home intent, which we catch in the HomeReceiverActivity before
+ // sending the intent to notify the mdm that provisioning is complete.
+ Global.putInt(getContentResolver(), Global.DEVICE_PROVISIONED, 1);
+ Secure.putInt(getContentResolver(), Secure.USER_SETUP_COMPLETE, 1);
+ }
+
private void requestEncryption(MessageParser messageParser, ProvisioningParams params) {
Intent encryptIntent = new Intent(DeviceOwnerProvisioningActivity.this,
EncryptDeviceActivity.class);
diff --git a/src/com/android/managedprovisioning/DeviceOwnerProvisioningService.java b/src/com/android/managedprovisioning/DeviceOwnerProvisioningService.java
index 3d5dd54..f5913e4 100644
--- a/src/com/android/managedprovisioning/DeviceOwnerProvisioningService.java
+++ b/src/com/android/managedprovisioning/DeviceOwnerProvisioningService.java
@@ -194,7 +194,6 @@
}
}
-
/**
* This is the core method of this class. It goes through every provisioning step.
* Each task checks if it is required and executes if it is.
@@ -203,30 +202,33 @@
if (DEBUG) ProvisionLogger.logd("Starting device owner provisioning");
// Construct Tasks. Do not start them yet.
- 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.mWifiSsid,
+ params.mWifiHidden, params.mWifiSecurityType, params.mWifiPassword,
+ params.mWifiProxyHost, params.mWifiProxyPort, params.mWifiProxyBypassHosts,
+ params.mWifiPacUrl, new AddWifiNetworkTask.Callback() {
+ @Override
+ public void onSuccess() {
+ progressUpdate(R.string.progress_download);
+ mDownloadPackageTask.run();
+ }
+
+ @Override
+ public void onError(){
+ error(R.string.device_owner_error_wifi);
+ }
+ });
+
+
+ mDownloadPackageTask = new DownloadPackageTask(
+ this, params, new DownloadPackageTask.Callback() {
@Override
public void onSuccess() {
- mDownloadPackageTask.run();
- }
-
- @Override
- public void onError(){
- error(R.string.device_owner_error_wifi);
- }
- });
-
- mDownloadPackageTask = new DownloadPackageTask(this,
- params.mDeviceAdminPackageDownloadLocation, params.mDeviceAdminPackageChecksum,
- params.mDeviceAdminPackageDownloadCookieHeader, new DownloadPackageTask.Callback() {
- @Override
- public void onSuccess() {
- String downloadLocation =
- mDownloadPackageTask.getDownloadedPackageLocation();
progressUpdate(R.string.progress_install);
- mInstallPackageTask.run(downloadLocation);
+ mInstallPackageTask.run(
+ mDownloadPackageTask.getDownloadedPackageLocation(
+ DownloadPackageTask.DEVICE_OWNER),
+ mDownloadPackageTask.getDownloadedPackageLocation(
+ DownloadPackageTask.INITIALIZER));
}
@Override
@@ -245,9 +247,8 @@
}
});
- mInstallPackageTask = new InstallPackageTask(this,
- params.inferDeviceAdminPackageName(), params.mDeviceAdminPackageDownloadLocation,
- new InstallPackageTask.Callback() {
+ mInstallPackageTask = new InstallPackageTask(
+ this, params, new InstallPackageTask.Callback() {
@Override
public void onSuccess() {
progressUpdate(R.string.progress_set_owner);
@@ -281,6 +282,8 @@
});
mSetDevicePolicyTask = new SetDevicePolicyTask(this,
getResources().getString(R.string.default_owned_device_username),
+ params.mDeviceInitializerComponentName,
+ getResources().getString(R.string.default_owned_device_username),
new SetDevicePolicyTask.Callback() {
@Override
public void onSuccess() {
diff --git a/src/com/android/managedprovisioning/MessageParser.java b/src/com/android/managedprovisioning/MessageParser.java
index 2e8e09e..26fce8c 100644
--- a/src/com/android/managedprovisioning/MessageParser.java
+++ b/src/com/android/managedprovisioning/MessageParser.java
@@ -33,6 +33,11 @@
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;
import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM;
import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED;
import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_SKIP_ENCRYPTION;
import static android.app.admin.DevicePolicyManager.MIME_TYPE_PROVISIONING_NFC;
@@ -77,17 +82,25 @@
* <p>
* Intent was received directly.
* The intent contains the extra {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME} or
- * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME} (which is deprecated but that we still
- * support), and may contain {@link #EXTRA_PROVISIONING_TIME_ZONE},
- * {@link #EXTRA_PROVISIONING_LOCAL_TIME}, and {@link #EXTRA_PROVISIONING_LOCALE}. A download
- * location may be specified in {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}
- * 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_DEVICE_ADMIN_PACKAGE_NAME} (which is deprecated and supported for
+ * legacy reasons only), and may contain {@link #EXTRA_PROVISIONING_TIME_ZONE},
+ * {@link #EXTRA_PROVISIONING_LOCAL_TIME}, {@link #EXTRA_PROVISIONING_LOCALE}, and
+ * {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME}. A download
+ * location for the device admin may be specified in
+ * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION} 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}. A download location for the device
+ * initializer may be specified in
+ * {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION} together with an
+ * optional http cookie header
+ * {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER} accompanied by the
+ * SHA-1 sum of the target file {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM}.
+ * Additional information to send through to the device initializer and admin may be specified in
* {@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}.
- * The boolean {@link #EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED} indicates wheter system
- * apps should not be disabled.
+ * The optional boolean {@link #EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED} indicates whether
+ * system apps should not be disabled. The optional boolean
+ * {@link #EXTRA_PROVISIONING_SKIP_ENCRYPTION} specifies whether the device should be encrypted.
* 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},
@@ -117,7 +130,10 @@
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
+ EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM,
+ EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION,
+ EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER,
+ EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM
};
protected static final String[] DEVICE_OWNER_LONG_EXTRAS = {
@@ -140,7 +156,8 @@
};
protected static final String[] DEVICE_OWNER_COMPONENT_NAME_EXTRAS = {
- EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME
+ EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME,
+ EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME
};
public void addProvisioningParamsToBundle(Bundle bundle, ProvisioningParams params) {
@@ -162,6 +179,14 @@
params.mDeviceAdminPackageDownloadCookieHeader);
bundle.putString(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM,
params.getDeviceAdminPackageChecksumAsString());
+ bundle.putParcelable(EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME,
+ params.mDeviceInitializerComponentName);
+ bundle.putString(EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION,
+ params.mDeviceInitializerPackageDownloadLocation);
+ bundle.putString(EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER,
+ params.mDeviceInitializerPackageDownloadCookieHeader);
+ bundle.putString(EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM,
+ params.getDeviceInitializerPackageChecksumAsString());
bundle.putLong(EXTRA_PROVISIONING_LOCAL_TIME, params.mLocalTime);
@@ -246,6 +271,19 @@
if ((s = props.getProperty(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM)) != null) {
params.mDeviceAdminPackageChecksum = stringToByteArray(s);
}
+ String name = props.getProperty(
+ EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME);
+ if (name != null) {
+ params.mDeviceInitializerComponentName = ComponentName.unflattenFromString(name);
+ }
+ params.mDeviceInitializerPackageDownloadLocation = props.getProperty(
+ EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION);
+ params.mDeviceInitializerPackageDownloadCookieHeader = 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);
+ }
if ((s = props.getProperty(EXTRA_PROVISIONING_LOCAL_TIME)) != null) {
params.mLocalTime = Long.parseLong(s);
@@ -321,6 +359,19 @@
if (hashString != null) {
params.mDeviceAdminPackageChecksum = stringToByteArray(hashString);
}
+ String name = intent.getStringExtra(
+ EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME);
+ if (name != null) {
+ params.mDeviceInitializerComponentName = ComponentName.unflattenFromString(name);
+ }
+ params.mDeviceInitializerPackageDownloadLocation = intent.getStringExtra(
+ EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION);
+ params.mDeviceInitializerPackageDownloadCookieHeader = 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.mLocalTime = intent.getLongExtra(EXTRA_PROVISIONING_LOCAL_TIME,
ProvisioningParams.DEFAULT_LOCAL_TIME);
diff --git a/src/com/android/managedprovisioning/ProvisioningParams.java b/src/com/android/managedprovisioning/ProvisioningParams.java
index dd7a089..2d833dd 100644
--- a/src/com/android/managedprovisioning/ProvisioningParams.java
+++ b/src/com/android/managedprovisioning/ProvisioningParams.java
@@ -49,10 +49,28 @@
// 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;
private ComponentName mInferedDeviceAdminComponentName;
- String inferDeviceAdminPackageName() {
+ 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 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 PersistableBundle mAdminExtrasBundle;
+
+ public boolean mStartedByNfc; // True iff provisioning flow was started by Nfc bump.
+
+ public boolean mLeaveAllSystemAppsEnabled;
+ public boolean mSkipEncryption;
+
+ public String inferDeviceAdminPackageName() {
if (mDeviceAdminComponentName != null) {
return mDeviceAdminComponentName.getPackageName();
}
@@ -69,17 +87,6 @@
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.
-
- public PersistableBundle mAdminExtrasBundle;
-
- public boolean mStartedByNfc; // True iff provisioning flow was started by Nfc bump.
-
- public boolean mLeaveAllSystemAppsEnabled;
- public boolean mSkipEncryption;
-
public String getLocaleAsString() {
if (mLocale != null) {
return mLocale.getLanguage() + "_" + mLocale.getCountry();
@@ -93,6 +100,11 @@
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);
+ }
+
@Override
public int describeContents() {
return 0;
@@ -116,6 +128,11 @@
out.writeString(mDeviceAdminPackageDownloadCookieHeader);
out.writeInt(mDeviceAdminPackageChecksum.length);
out.writeByteArray(mDeviceAdminPackageChecksum);
+ out.writeParcelable(mDeviceInitializerComponentName, 0 /* default */);
+ 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);
@@ -145,6 +162,13 @@
int checksumLength = in.readInt();
params.mDeviceAdminPackageChecksum = new byte[checksumLength];
in.readByteArray(params.mDeviceAdminPackageChecksum);
+ params.mDeviceInitializerComponentName = (ComponentName)
+ in.readParcelable(null /* use default classloader */);
+ 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;
diff --git a/src/com/android/managedprovisioning/task/DownloadPackageTask.java b/src/com/android/managedprovisioning/task/DownloadPackageTask.java
index 38ef5f1..79185b2 100644
--- a/src/com/android/managedprovisioning/task/DownloadPackageTask.java
+++ b/src/com/android/managedprovisioning/task/DownloadPackageTask.java
@@ -31,6 +31,7 @@
import android.util.Base64;
import com.android.managedprovisioning.ProvisionLogger;
+import com.android.managedprovisioning.ProvisioningParams;
import java.io.InputStream;
import java.io.IOException;
@@ -38,45 +39,55 @@
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
/**
- * Downloads a given file and checks whether its hash matches a given hash to verify that the
- * intended file was downloaded.
+ * 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
+ * verify that the downloaded files are the ones that are expected.
*/
public class DownloadPackageTask {
public static final int ERROR_HASH_MISMATCH = 0;
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;
- private final String mDownloadLocationFrom;
private final Callback mCallback;
- private final byte[] mHash;
- private final String mHttpCookieHeader;
-
- private boolean mDoneDownloading;
- private String mDownloadLocationTo;
- private long mDownloadId;
private BroadcastReceiver mReceiver;
+ private final DownloadManager mDlm;
- public DownloadPackageTask (Context context, String downloadLocation, byte[] hash,
- String httpCookieHeader, Callback callback) {
+ private Set<DownloadInfo> mDownloads;
+
+ public DownloadPackageTask (Context context, ProvisioningParams params, Callback callback) {
mCallback = callback;
mContext = context;
- mDownloadLocationFrom = downloadLocation;
- mHash = hash;
- mHttpCookieHeader = httpCookieHeader;
- mDoneDownloading = false;
- }
+ mDlm = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
- public boolean downloadLocationWasProvided() {
- return !TextUtils.isEmpty(mDownloadLocationFrom);
+ mDownloads = new HashSet<DownloadInfo>();
+ if (!TextUtils.isEmpty(params.mDeviceAdminPackageDownloadLocation)) {
+ mDownloads.add(new DownloadInfo(
+ params.mDeviceAdminPackageDownloadLocation,
+ params.mDeviceAdminPackageChecksum,
+ params.mDeviceAdminPackageDownloadCookieHeader,
+ DEVICE_OWNER));
+ }
+ if (!TextUtils.isEmpty(params.mDeviceInitializerPackageDownloadLocation)) {
+ mDownloads.add(new DownloadInfo(
+ params.mDeviceInitializerPackageDownloadLocation,
+ params.mDeviceInitializerPackageChecksum,
+ params.mDeviceInitializerPackageDownloadCookieHeader,
+ INITIALIZER));
+ }
}
public void run() {
- if (!downloadLocationWasProvided()) {
+ if (mDownloads.size() == 0) {
mCallback.onSuccess();
return;
}
@@ -84,38 +95,49 @@
mContext.registerReceiver(mReceiver,
new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
- ProvisionLogger.logd("Starting download from " + mDownloadLocationFrom);
DownloadManager dm = (DownloadManager) mContext
.getSystemService(Context.DOWNLOAD_SERVICE);
- Request request = new Request(Uri.parse(mDownloadLocationFrom));
- if (mHttpCookieHeader != null) {
- request.addRequestHeader("Cookie", mHttpCookieHeader);
- ProvisionLogger.logd("Downloading with http cookie header: " + mHttpCookieHeader);
+ for (DownloadInfo downloadInfo : mDownloads) {
+ ProvisionLogger.logd("Starting download from " + downloadInfo.mDownloadLocationFrom);
+
+ Request request = new Request(Uri.parse(downloadInfo.mDownloadLocationFrom));
+ if (downloadInfo.mHttpCookieHeader != null) {
+ request.addRequestHeader("Cookie", downloadInfo.mHttpCookieHeader);
+ ProvisionLogger.logd(
+ "Downloading with http cookie header: " + downloadInfo.mHttpCookieHeader);
+ }
+ downloadInfo.mDownloadId = dm.enqueue(request);
}
- mDownloadId = dm.enqueue(request);
}
private BroadcastReceiver createDownloadReceiver() {
return new BroadcastReceiver() {
+ /**
+ * Whenever the download manager finishes a download, record the successful download for
+ * the corresponding DownloadInfo.
+ */
@Override
public void onReceive(Context context, Intent intent) {
if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(intent.getAction())) {
Query q = new Query();
- q.setFilterById(mDownloadId);
- DownloadManager dm = (DownloadManager) mContext
- .getSystemService(Context.DOWNLOAD_SERVICE);
- Cursor c = dm.query(q);
- if (c.moveToFirst()) {
- int columnIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS);
- if (DownloadManager.STATUS_SUCCESSFUL == c.getInt(columnIndex)) {
- String location = c.getString(
- c.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));
- c.close();
- onDownloadSuccess(location);
- } else if (DownloadManager.STATUS_FAILED == c.getInt(columnIndex)){
- int reason = c.getColumnIndex(DownloadManager.COLUMN_REASON);
- c.close();
- onDownloadFail(reason);
+ for (DownloadInfo downloadInfo : mDownloads) {
+ q.setFilterById(downloadInfo.mDownloadId);
+ Cursor c = mDlm.query(q);
+ if (c.moveToFirst()) {
+ long downloadId =
+ c.getLong(c.getColumnIndex(DownloadManager.COLUMN_ID));
+ int columnIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS);
+ if (DownloadManager.STATUS_SUCCESSFUL == c.getInt(columnIndex)) {
+ String location = c.getString(
+ c.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));
+ c.close();
+ onDownloadSuccess(downloadId, location);
+ } else if (DownloadManager.STATUS_FAILED == c.getInt(columnIndex)){
+ int reason = c.getInt(
+ c.getColumnIndex(DownloadManager.COLUMN_REASON));
+ c.close();
+ onDownloadFail(reason);
+ }
}
}
}
@@ -123,40 +145,61 @@
};
}
- private void onDownloadSuccess(String location) {
- if (mDoneDownloading) {
+ /**
+ * For a given successful download, check that the downloaded file is the expected file. Check
+ * if this was the last file the task had to download and finish the DownloadPackageTask if that
+ * is the case.
+ * @param downloadId the unique download id for the completed download.
+ * @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;
+ }
+ }
+ if (downloadInfo == null || downloadInfo.mDoneDownloading) {
// DownloadManager can send success more than once. Only act first time.
return;
} else {
- mDoneDownloading = true;
+ downloadInfo.mDoneDownloading = true;
}
-
ProvisionLogger.logd("Downloaded succesfully to: " + location);
// Check whether hash of downloaded file matches hash given in constructor.
byte[] hash = computeHash(location);
if (hash == null) {
-
// Error should have been reported in computeHash().
return;
}
- if (Arrays.equals(mHash, hash)) {
+ if (Arrays.equals(downloadInfo.mHash, hash)) {
ProvisionLogger.logd(HASH_TYPE + "-hashes matched, both are "
+ byteArrayToString(hash));
- mDownloadLocationTo = location;
- mCallback.onSuccess();
+ downloadInfo.mLocation = location;
+ downloadInfo.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(mHash));
+ + byteArrayToString(downloadInfo.mHash));
mCallback.onError(ERROR_HASH_MISMATCH);
}
}
+ private void checkSuccess() {
+ for (DownloadInfo info : mDownloads) {
+ if (!info.mSuccess) {
+ return;
+ }
+ }
+ mCallback.onSuccess();
+ }
+
private void onDownloadFail(int errorCode) {
ProvisionLogger.loge("Downloading package failed.");
ProvisionLogger.loge("COLUMN_REASON in DownloadManager response has value: "
@@ -203,8 +246,13 @@
return hash;
}
- public String getDownloadedPackageLocation() {
- return mDownloadLocationTo;
+ public String getDownloadedPackageLocation(String packageType) {
+ for (DownloadInfo info : mDownloads) {
+ if (packageType.equals(info.mPackageType)) {
+ return info.mLocation;
+ }
+ }
+ return "";
}
public void cleanUp() {
@@ -217,12 +265,14 @@
//Remove download.
DownloadManager dm = (DownloadManager) mContext
.getSystemService(Context.DOWNLOAD_SERVICE);
- boolean removeSuccess = dm.remove(mDownloadId) == 1;
- if (removeSuccess) {
- ProvisionLogger.logd("Successfully removed the device owner installer file.");
- } else {
- ProvisionLogger.loge("Could not remove the device owner installer file.");
- // Ignore this error. Failing cleanup should not stop provisioning flow.
+ for (DownloadInfo info : mDownloads) {
+ boolean removeSuccess = dm.remove(info.mDownloadId) == 1;
+ if (removeSuccess) {
+ ProvisionLogger.logd("Successfully removed installer file.");
+ } else {
+ ProvisionLogger.loge("Could not remove installer file.");
+ // Ignore this error. Failing cleanup should not stop provisioning flow.
+ }
}
}
@@ -235,4 +285,24 @@
public abstract void onSuccess();
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;
+ public long mDownloadId;
+ public String mLocation;
+ public boolean mDoneDownloading;
+ public boolean mSuccess;
+
+ public DownloadInfo(String downloadLocation, byte[] hash, String httpCookieHeader,
+ String packageType) {
+ mDownloadLocationFrom = downloadLocation;
+ mHash = hash;
+ mHttpCookieHeader = httpCookieHeader;
+ mPackageType = packageType;
+ mDoneDownloading = false;
+ }
+ }
}
diff --git a/src/com/android/managedprovisioning/task/InstallPackageTask.java b/src/com/android/managedprovisioning/task/InstallPackageTask.java
index b0cc438..5eaa43a 100644
--- a/src/com/android/managedprovisioning/task/InstallPackageTask.java
+++ b/src/com/android/managedprovisioning/task/InstallPackageTask.java
@@ -27,14 +27,17 @@
import android.Manifest.permission;
import com.android.managedprovisioning.ProvisionLogger;
+import com.android.managedprovisioning.ProvisioningParams;
import java.io.File;
+import java.util.HashSet;
+import java.util.Set;
/**
- * Installs a device owner package from a given path.
+ * Optionally installs device owner and device initializer packages.
* <p>
- * Before installing it is checked whether the file at the specified path contains the given package
- * and the given admin receiver.
+ * Before installing it is checked whether each file at the specified paths contains the correct
+ * package and admin receiver.
* </p>
*/
public class InstallPackageTask {
@@ -43,73 +46,89 @@
private final Context mContext;
private final Callback mCallback;
- private final String mPackageName;
+ private final String mDeviceAdminPackageName;
+ private final String mDeviceInitializerPackageName;
+ private final boolean mDownloadedAdmin;
+ private final boolean mDownloadedInitializer;
- private String mPackageLocation;
- private final String mDownloadLocationFrom;
private PackageManager mPm;
private int mPackageVerifierEnable;
+ private Set<InstallInfo> mPackagesToInstall;
- public InstallPackageTask (Context context, String packageName, String downloadLocation,
- Callback callback) {
+ public InstallPackageTask (Context context, ProvisioningParams params, Callback callback) {
mCallback = callback;
mContext = context;
- mPackageLocation = null; // Initialized in run().
- mPackageName = packageName;
- mDownloadLocationFrom = downloadLocation;
+ mDeviceAdminPackageName = params.inferDeviceAdminPackageName();
+ mDeviceInitializerPackageName = params.mDeviceInitializerComponentName.getPackageName();
+ mDownloadedAdmin = !TextUtils.isEmpty(params.mDeviceAdminPackageDownloadLocation);
+ mDownloadedInitializer =
+ !TextUtils.isEmpty(params.mDeviceInitializerPackageDownloadLocation);
+ mPackagesToInstall = new HashSet<InstallInfo>();
}
- public boolean downloadLocationWasProvided() {
- return !TextUtils.isEmpty(mDownloadLocationFrom);
- }
-
- public void run(String packageLocation) {
- if (TextUtils.isEmpty(packageLocation)) {
- if (!downloadLocationWasProvided()) {
- ProvisionLogger.loge("Package Location is empty, nothing to install.");
- mCallback.onSuccess();
- return;
+ public void run(String deviceAdminPackageLocation, String deviceInitializerPackageLocation) {
+ if (mDownloadedAdmin) {
+ if (!TextUtils.isEmpty(deviceAdminPackageLocation)) {
+ mPackagesToInstall.add(
+ new InstallInfo(mDeviceAdminPackageName, deviceAdminPackageLocation));
} else {
ProvisionLogger.loge("Package Location is empty.");
mCallback.onError(ERROR_PACKAGE_INVALID);
return;
}
}
- mPackageLocation = packageLocation;
+ if (mDownloadedInitializer) {
+ if (!TextUtils.isEmpty(deviceInitializerPackageLocation)) {
+ mPackagesToInstall.add(new InstallInfo(
+ mDeviceInitializerPackageName, deviceInitializerPackageLocation));
+ } else {
+ ProvisionLogger.loge("Package Location is empty.");
+ mCallback.onError(ERROR_PACKAGE_INVALID);
+ return;
+ }
+ }
+
+ if (mPackagesToInstall.size() == 0) {
+ ProvisionLogger.loge("Nothing to install");
+ mCallback.onSuccess();
+ return;
+ }
+ ProvisionLogger.logi("Installing package(s)");
PackageInstallObserver observer = new PackageInstallObserver();
mPm = mContext.getPackageManager();
- if (packageContentIsCorrect()) {
- // Temporarily turn off package verification.
- mPackageVerifierEnable = Global.getInt(mContext.getContentResolver(),
- Global.PACKAGE_VERIFIER_ENABLE, 1);
- Global.putInt(mContext.getContentResolver(), Global.PACKAGE_VERIFIER_ENABLE, 0);
+ for (InstallInfo info : mPackagesToInstall) {
+ if (packageContentIsCorrect(info.mPackageName, info.mLocation)) {
+ // Temporarily turn off package verification.
+ mPackageVerifierEnable = Global.getInt(mContext.getContentResolver(),
+ Global.PACKAGE_VERIFIER_ENABLE, 1);
+ Global.putInt(mContext.getContentResolver(), Global.PACKAGE_VERIFIER_ENABLE, 0);
- Uri packageUri = Uri.parse("file://" + mPackageLocation);
-
- // Allow for replacing an existing package.
- // Needed in case this task is performed multiple times.
- mPm.installPackage(packageUri, observer,
- /* flags */ PackageManager.INSTALL_REPLACE_EXISTING, mContext.getPackageName());
- } else {
- // Error should have been reported in packageContentIsCorrect().
- return;
+ // Allow for replacing an existing package.
+ // Needed in case this task is performed multiple times.
+ mPm.installPackage(Uri.parse("file://" + info.mLocation),
+ observer,
+ /* flags */ PackageManager.INSTALL_REPLACE_EXISTING,
+ mContext.getPackageName());
+ } else {
+ // Error should have been reported in packageContentIsCorrect().
+ return;
+ }
}
}
- private boolean packageContentIsCorrect() {
- PackageInfo pi = mPm.getPackageArchiveInfo(mPackageLocation,
- PackageManager.GET_RECEIVERS);
+ private boolean packageContentIsCorrect(String packageName, String packageLocation) {
+ PackageInfo pi = mPm.getPackageArchiveInfo(packageLocation, PackageManager.GET_RECEIVERS);
if (pi == null) {
ProvisionLogger.loge("Package could not be parsed successfully.");
mCallback.onError(ERROR_PACKAGE_INVALID);
return false;
}
- if (!pi.packageName.equals(mPackageName)) {
+ if (!pi.packageName.equals(packageName)) {
ProvisionLogger.loge("Package name in apk (" + pi.packageName
+ ") does not match package name specified by programmer ("
- + mPackageName + ").");
+ + packageName + ").");
mCallback.onError(ERROR_PACKAGE_INVALID);
return false;
}
@@ -127,34 +146,68 @@
private class PackageInstallObserver extends IPackageInstallObserver.Stub {
@Override
public void packageInstalled(String packageName, int returnCode) {
- // Set package verification flag to its original value.
- Global.putInt(mContext.getContentResolver(), Global.PACKAGE_VERIFIER_ENABLE,
- mPackageVerifierEnable);
-
- if (returnCode == PackageManager.INSTALL_SUCCEEDED
- && mPackageName.equals(packageName)) {
- ProvisionLogger.logd("Package " + mPackageName + " is succesfully installed.");
-
- mCallback.onSuccess();
+ InstallInfo installInfo = null;
+ for (InstallInfo info : mPackagesToInstall) {
+ if (packageName.equals(info.mPackageName)) {
+ installInfo = info;
+ }
+ }
+ if (installInfo == null) {
+ ProvisionLogger.loge("Package doesn't have expected package name.");
+ mCallback.onError(ERROR_PACKAGE_INVALID);
+ return;
+ }
+ if (returnCode == PackageManager.INSTALL_SUCCEEDED) {
+ installInfo.mDoneInstalling = true;
+ ProvisionLogger.logd(
+ "Package " + installInfo.mPackageName + " is succesfully installed.");
+ checkSuccess();
} else {
if (returnCode == PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE) {
- ProvisionLogger.logd("Current version of " + mPackageName
- + " higher than the version to be installed.");
- ProvisionLogger.logd("Package " + mPackageName + " was not reinstalled.");
- mCallback.onSuccess();
- return;
+ installInfo.mDoneInstalling = true;
+ ProvisionLogger.logd("Current version of " + installInfo.mPackageName
+ + " higher than the version to be installed. It was not reinstalled.");
+ checkSuccess();
+ } else {
+ ProvisionLogger.logd(
+ "Installing package " + installInfo.mPackageName + " failed.");
+ ProvisionLogger.logd(
+ "Errorcode returned by IPackageInstallObserver = " + returnCode);
+ mCallback.onError(ERROR_INSTALLATION_FAILED);
}
-
- ProvisionLogger.logd("Installing package " + mPackageName + " failed.");
- ProvisionLogger.logd("Errorcode returned by IPackageInstallObserver = "
- + returnCode);
- mCallback.onError(ERROR_INSTALLATION_FAILED);
}
}
}
+ /**
+ * Calls the success callback once all of the packages that needed to be installed are
+ * successfully installed.
+ */
+ private void checkSuccess() {
+ for (InstallInfo info : mPackagesToInstall) {
+ if (!info.mDoneInstalling) {
+ return;
+ }
+ }
+ // Set package verification flag to its original value.
+ Global.putInt(mContext.getContentResolver(), Global.PACKAGE_VERIFIER_ENABLE,
+ mPackageVerifierEnable);
+ mCallback.onSuccess();
+ }
+
public abstract static class Callback {
public abstract void onSuccess();
public abstract void onError(int errorCode);
}
+
+ private static class InstallInfo {
+ public String mPackageName;
+ public String mLocation;
+ public boolean mDoneInstalling;
+
+ public InstallInfo(String packageName, String location) {
+ mPackageName = packageName;
+ mLocation = location;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/com/android/managedprovisioning/task/SetDevicePolicyTask.java b/src/com/android/managedprovisioning/task/SetDevicePolicyTask.java
index 2a7b7f5..82a60a8 100644
--- a/src/com/android/managedprovisioning/task/SetDevicePolicyTask.java
+++ b/src/com/android/managedprovisioning/task/SetDevicePolicyTask.java
@@ -19,15 +19,15 @@
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.text.TextUtils;
import com.android.managedprovisioning.ProvisionLogger;
+/**
+ * This tasks sets a given component as the owner of the device. If provided it also sets a given
+ * component as the device initializer, which can perform additional setup steps at the end of
+ * provisioning before setting the device as provisioned.
+ */
public class SetDevicePolicyTask {
public static final int ERROR_PACKAGE_NOT_INSTALLED = 0;
public static final int ERROR_NO_RECEIVER = 1;
@@ -37,47 +37,77 @@
private final Context mContext;
private String mAdminPackage;
private ComponentName mAdminComponent;
- private final String mOwner;
+ private final String mOwnerName;
+ private ComponentName mInitializerComponent;
+ private String mInitializerPackageName;
+ private String mInitializerName;
private PackageManager mPackageManager;
private DevicePolicyManager mDevicePolicyManager;
- public SetDevicePolicyTask(Context context, String owner, Callback callback) {
+ public SetDevicePolicyTask(Context context, String ownerName,
+ ComponentName initializerComponent, String initializerName, Callback callback) {
mCallback = callback;
mContext = context;
- mOwner = owner;
+ mOwnerName = ownerName;
+ mInitializerComponent = initializerComponent;
+ if (mInitializerComponent != null) {
+ mInitializerPackageName = initializerComponent.getPackageName();
+ mInitializerName = initializerName;
+ }
+
mPackageManager = mContext.getPackageManager();
mDevicePolicyManager = (DevicePolicyManager) mContext.
getSystemService(Context.DEVICE_POLICY_SERVICE);
}
public void run(ComponentName adminComponent) {
- mAdminComponent = adminComponent;
- mAdminPackage = mAdminComponent.getPackageName();
- enableDevicePolicyApp();
- setActiveAdmin();
- setDeviceOwner();
+ try {
+ mAdminComponent = adminComponent;
+ mAdminPackage = mAdminComponent.getPackageName();
+
+ enableDevicePolicyApp(mAdminPackage);
+ setActiveAdmin(mAdminComponent);
+ setDeviceOwner(mAdminPackage, mOwnerName);
+
+ if (mInitializerComponent != null) {
+ enableDevicePolicyApp(mInitializerPackageName);
+ setActiveAdmin(mInitializerComponent);
+ setDeviceInitializer(mInitializerComponent, mInitializerName);
+ }
+ } catch (Exception e) {
+ ProvisionLogger.loge("Failure setting device owner or initializer", e);
+ mCallback.onError(ERROR_OTHER);
+ return;
+ }
+
mCallback.onSuccess();
}
- private void enableDevicePolicyApp() {
- int enabledSetting = mPackageManager
- .getApplicationEnabledSetting(mAdminPackage);
+ private void enableDevicePolicyApp(String packageName) {
+ int enabledSetting = mPackageManager.getApplicationEnabledSetting(packageName);
if (enabledSetting != PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
- mPackageManager.setApplicationEnabledSetting(mAdminPackage,
+ mPackageManager.setApplicationEnabledSetting(packageName,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, 0);
}
}
- public void setActiveAdmin() {
- ProvisionLogger.logd("Setting " + mAdminComponent + " as active admin.");
- mDevicePolicyManager.setActiveAdmin(mAdminComponent, true);
+ public void setActiveAdmin(ComponentName component) {
+ ProvisionLogger.logd("Setting " + component + " as active admin.");
+ mDevicePolicyManager.setActiveAdmin(component, true);
}
- public void setDeviceOwner() {
- ProvisionLogger.logd("Setting " + mAdminPackage + " as device owner " + mOwner + ".");
- if (!mDevicePolicyManager.isDeviceOwner(mAdminPackage)) {
- mDevicePolicyManager.setDeviceOwner(mAdminPackage, mOwner);
+ public void setDeviceOwner(String packageName, String owner) {
+ ProvisionLogger.logd("Setting " + packageName + " as device owner " + owner + ".");
+ if (!mDevicePolicyManager.isDeviceOwner(packageName)) {
+ mDevicePolicyManager.setDeviceOwner(packageName, owner);
+ }
+ }
+
+ public void setDeviceInitializer(ComponentName component, String owner) {
+ ProvisionLogger.logd("Setting " + component + " as device initializer " + owner + ".");
+ if (!mDevicePolicyManager.isDeviceInitializerApp(component.getPackageName())) {
+ mDevicePolicyManager.setDeviceInitializer(null, component, owner);
}
}