1 /* 2 * Copyright 2014, The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.managedprovisioning.task; 17 18 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_DOWNLOAD_PACKAGE_TASK_MS; 19 import static com.android.internal.util.Preconditions.checkNotNull; 20 21 import android.app.DownloadManager; 22 import android.app.DownloadManager.Query; 23 import android.app.DownloadManager.Request; 24 import android.content.BroadcastReceiver; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.database.Cursor; 29 import android.net.Uri; 30 import android.os.Handler; 31 import android.os.Looper; 32 33 import com.android.internal.annotations.VisibleForTesting; 34 import com.android.managedprovisioning.common.ProvisionLogger; 35 import com.android.managedprovisioning.R; 36 import com.android.managedprovisioning.common.Globals; 37 import com.android.managedprovisioning.common.Utils; 38 import com.android.managedprovisioning.model.PackageDownloadInfo; 39 import com.android.managedprovisioning.model.ProvisioningParams; 40 41 import java.io.File; 42 43 /** 44 * Downloads the management app apk from the url provided by {@link PackageDownloadInfo#location}. 45 * The location of the downloaded file can be read via {@link #getDownloadedPackageLocation()}. 46 */ 47 public class DownloadPackageTask extends AbstractProvisioningTask { 48 public static final int ERROR_DOWNLOAD_FAILED = 0; 49 public static final int ERROR_OTHER = 1; 50 51 private BroadcastReceiver mReceiver; 52 private final DownloadManager mDownloadManager; 53 private final String mPackageName; 54 private final PackageDownloadInfo mPackageDownloadInfo; 55 private long mDownloadId; 56 57 private final Utils mUtils; 58 59 private String mDownloadLocationTo; //local file where the package is downloaded. 60 private boolean mDoneDownloading; 61 DownloadPackageTask( Context context, ProvisioningParams provisioningParams, Callback callback)62 public DownloadPackageTask( 63 Context context, 64 ProvisioningParams provisioningParams, 65 Callback callback) { 66 this(new Utils(), context, provisioningParams, callback); 67 } 68 69 @VisibleForTesting DownloadPackageTask( Utils utils, Context context, ProvisioningParams provisioningParams, Callback callback)70 DownloadPackageTask( 71 Utils utils, 72 Context context, 73 ProvisioningParams provisioningParams, 74 Callback callback) { 75 super(context, provisioningParams, callback); 76 77 mUtils = checkNotNull(utils); 78 mDownloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); 79 mDownloadManager.setAccessFilename(true); 80 mPackageName = provisioningParams.inferDeviceAdminPackageName(); 81 mPackageDownloadInfo = checkNotNull(provisioningParams.deviceAdminDownloadInfo); 82 } 83 84 @Override getStatusMsgId()85 public int getStatusMsgId() { 86 return R.string.progress_download; 87 } 88 89 @Override run(int userId)90 public void run(int userId) { 91 startTaskTimer(); 92 if (!mUtils.packageRequiresUpdate(mPackageName, mPackageDownloadInfo.minVersion, 93 mContext)) { 94 // Do not log time if package is already on device and does not require an update, as 95 // that isn't useful. 96 success(); 97 return; 98 } 99 if (!mUtils.isConnectedToNetwork(mContext)) { 100 ProvisionLogger.loge("DownloadPackageTask: not connected to the network, can't download" 101 + " the package"); 102 error(ERROR_OTHER); 103 return; 104 } 105 mReceiver = createDownloadReceiver(); 106 // register the receiver on the worker thread to avoid threading issues with respect to 107 // the location variable 108 mContext.registerReceiver(mReceiver, 109 new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE), 110 null, 111 new Handler(Looper.myLooper())); 112 113 if (Globals.DEBUG) { 114 ProvisionLogger.logd("Starting download from " + mPackageDownloadInfo.location); 115 } 116 117 Request request = new Request(Uri.parse(mPackageDownloadInfo.location)); 118 119 // Note that the apk may not actually be downloaded to this path. This could happen if 120 // this file already exists. 121 String path = mContext.getExternalFilesDir(null) 122 + "/download_cache/managed_provisioning_downloaded_app.apk"; 123 File downloadedFile = new File(path); 124 downloadedFile.getParentFile().mkdirs(); // If the folder doesn't exists it is created 125 request.setDestinationUri(Uri.fromFile(downloadedFile)); 126 127 if (mPackageDownloadInfo.cookieHeader != null) { 128 request.addRequestHeader("Cookie", mPackageDownloadInfo.cookieHeader); 129 if (Globals.DEBUG) { 130 ProvisionLogger.logd("Downloading with http cookie header: " 131 + mPackageDownloadInfo.cookieHeader); 132 } 133 } 134 mDownloadId = mDownloadManager.enqueue(request); 135 } 136 137 @Override getMetricsCategory()138 protected int getMetricsCategory() { 139 return PROVISIONING_DOWNLOAD_PACKAGE_TASK_MS; 140 } 141 createDownloadReceiver()142 private BroadcastReceiver createDownloadReceiver() { 143 return new BroadcastReceiver() { 144 @Override 145 public void onReceive(Context context, Intent intent) { 146 if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(intent.getAction())) { 147 Query q = new Query(); 148 q.setFilterById(mDownloadId); 149 Cursor c = mDownloadManager.query(q); 150 if (c.moveToFirst()) { 151 int columnIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS); 152 if (DownloadManager.STATUS_SUCCESSFUL == c.getInt(columnIndex)) { 153 mDownloadLocationTo = c.getString( 154 c.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME)); 155 c.close(); 156 onDownloadSuccess(); 157 } else if (DownloadManager.STATUS_FAILED == c.getInt(columnIndex)) { 158 int reason = c.getColumnIndex(DownloadManager.COLUMN_REASON); 159 c.close(); 160 onDownloadFail(reason); 161 } 162 } 163 } 164 } 165 }; 166 } 167 168 /** 169 * For a successful download, check that the downloaded file is the expected file. 170 * If the package hash is provided then that is used, otherwise a signature hash is used. 171 */ 172 private void onDownloadSuccess() { 173 if (mDoneDownloading) { 174 // DownloadManager can send success more than once. Only act first time. 175 return; 176 } 177 178 ProvisionLogger.logd("Downloaded succesfully to: " + mDownloadLocationTo); 179 mDoneDownloading = true; 180 stopTaskTimer(); 181 success(); 182 } 183 184 public String getDownloadedPackageLocation() { 185 return mDownloadLocationTo; 186 } 187 188 private void onDownloadFail(int errorCode) { 189 ProvisionLogger.loge("Downloading package failed."); 190 ProvisionLogger.loge("COLUMN_REASON in DownloadManager response has value: " 191 + errorCode); 192 error(ERROR_DOWNLOAD_FAILED); 193 } 194 195 public void cleanUp() { 196 if (mReceiver != null) { 197 //Unregister receiver. 198 mContext.unregisterReceiver(mReceiver); 199 mReceiver = null; 200 } 201 202 boolean removeSuccess = mDownloadManager.remove(mDownloadId) == 1; 203 if (removeSuccess) { 204 ProvisionLogger.logd("Successfully removed installer file."); 205 } else { 206 ProvisionLogger.loge("Could not remove installer file."); 207 // Ignore this error. Failing cleanup should not stop provisioning flow. 208 } 209 } 210 } 211