blob: 867e6e87689bf6db95500109491f00b794af8173 [file] [log] [blame]
/*
* Copyright 2014, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.managedprovisioning.task;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_DOWNLOAD_PACKAGE_TASK_MS;
import static com.android.internal.util.Preconditions.checkNotNull;
import android.app.DownloadManager;
import android.app.DownloadManager.Query;
import android.app.DownloadManager.Request;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import com.android.internal.annotations.VisibleForTesting;
import com.android.managedprovisioning.common.ProvisionLogger;
import com.android.managedprovisioning.R;
import com.android.managedprovisioning.common.Globals;
import com.android.managedprovisioning.common.Utils;
import com.android.managedprovisioning.model.PackageDownloadInfo;
import com.android.managedprovisioning.model.ProvisioningParams;
import java.io.File;
/**
* Downloads the management app apk from the url provided by {@link PackageDownloadInfo#location}.
* The location of the downloaded file can be read via {@link #getDownloadedPackageLocation()}.
*/
public class DownloadPackageTask extends AbstractProvisioningTask {
public static final int ERROR_DOWNLOAD_FAILED = 0;
public static final int ERROR_OTHER = 1;
private BroadcastReceiver mReceiver;
private final DownloadManager mDownloadManager;
private final String mPackageName;
private final PackageDownloadInfo mPackageDownloadInfo;
private long mDownloadId;
private final Utils mUtils;
private String mDownloadLocationTo; //local file where the package is downloaded.
private boolean mDoneDownloading;
public DownloadPackageTask(
Context context,
ProvisioningParams provisioningParams,
Callback callback) {
this(new Utils(), context, provisioningParams, callback);
}
@VisibleForTesting
DownloadPackageTask(
Utils utils,
Context context,
ProvisioningParams provisioningParams,
Callback callback) {
super(context, provisioningParams, callback);
mUtils = checkNotNull(utils);
mDownloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
mDownloadManager.setAccessFilename(true);
mPackageName = provisioningParams.inferDeviceAdminPackageName();
mPackageDownloadInfo = checkNotNull(provisioningParams.deviceAdminDownloadInfo);
}
@Override
public int getStatusMsgId() {
return R.string.progress_download;
}
@Override
public void run(int userId) {
startTaskTimer();
if (!mUtils.packageRequiresUpdate(mPackageName, mPackageDownloadInfo.minVersion,
mContext)) {
// Do not log time if package is already on device and does not require an update, as
// that isn't useful.
success();
return;
}
if (!mUtils.isConnectedToNetwork(mContext)) {
ProvisionLogger.loge("DownloadPackageTask: not connected to the network, can't download"
+ " the package");
error(ERROR_OTHER);
return;
}
mReceiver = createDownloadReceiver();
// register the receiver on the worker thread to avoid threading issues with respect to
// the location variable
mContext.registerReceiver(mReceiver,
new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE),
null,
new Handler(Looper.myLooper()));
if (Globals.DEBUG) {
ProvisionLogger.logd("Starting download from " + mPackageDownloadInfo.location);
}
Request request = new Request(Uri.parse(mPackageDownloadInfo.location));
// Note that the apk may not actually be downloaded to this path. This could happen if
// this file already exists.
String path = mContext.getExternalFilesDir(null)
+ "/download_cache/managed_provisioning_downloaded_app.apk";
File downloadedFile = new File(path);
downloadedFile.getParentFile().mkdirs(); // If the folder doesn't exists it is created
request.setDestinationUri(Uri.fromFile(downloadedFile));
if (mPackageDownloadInfo.cookieHeader != null) {
request.addRequestHeader("Cookie", mPackageDownloadInfo.cookieHeader);
if (Globals.DEBUG) {
ProvisionLogger.logd("Downloading with http cookie header: "
+ mPackageDownloadInfo.cookieHeader);
}
}
mDownloadId = mDownloadManager.enqueue(request);
}
@Override
protected int getMetricsCategory() {
return PROVISIONING_DOWNLOAD_PACKAGE_TASK_MS;
}
private BroadcastReceiver createDownloadReceiver() {
return new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(intent.getAction())) {
Query q = new Query();
q.setFilterById(mDownloadId);
Cursor c = mDownloadManager.query(q);
if (c.moveToFirst()) {
int columnIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS);
if (DownloadManager.STATUS_SUCCESSFUL == c.getInt(columnIndex)) {
mDownloadLocationTo = c.getString(
c.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));
c.close();
onDownloadSuccess();
} else if (DownloadManager.STATUS_FAILED == c.getInt(columnIndex)) {
int reason = c.getColumnIndex(DownloadManager.COLUMN_REASON);
c.close();
onDownloadFail(reason);
}
}
}
}
};
}
/**
* For a successful download, check that the downloaded file is the expected file.
* If the package hash is provided then that is used, otherwise a signature hash is used.
*/
private void onDownloadSuccess() {
if (mDoneDownloading) {
// DownloadManager can send success more than once. Only act first time.
return;
}
ProvisionLogger.logd("Downloaded succesfully to: " + mDownloadLocationTo);
mDoneDownloading = true;
stopTaskTimer();
success();
}
public String getDownloadedPackageLocation() {
return mDownloadLocationTo;
}
private void onDownloadFail(int errorCode) {
ProvisionLogger.loge("Downloading package failed.");
ProvisionLogger.loge("COLUMN_REASON in DownloadManager response has value: "
+ errorCode);
error(ERROR_DOWNLOAD_FAILED);
}
public void cleanUp() {
if (mReceiver != null) {
//Unregister receiver.
mContext.unregisterReceiver(mReceiver);
mReceiver = null;
}
boolean removeSuccess = mDownloadManager.remove(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.
}
}
}