DownloadPackageTask.java revision 48fdbe03c7cc39accada396a96acff09bdecb3b2
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 */ 16package com.android.managedprovisioning.task; 17 18import android.app.DownloadManager; 19import android.app.DownloadManager.Query; 20import android.app.DownloadManager.Request; 21import android.content.BroadcastReceiver; 22import android.content.Context; 23import android.content.Intent; 24import android.content.IntentFilter; 25import android.content.pm.ActivityInfo; 26import android.content.pm.PackageInfo; 27import android.content.pm.PackageManager; 28import android.database.Cursor; 29import android.net.Uri; 30import android.text.TextUtils; 31import android.util.Base64; 32 33import com.android.managedprovisioning.ProvisionLogger; 34 35import java.io.InputStream; 36import java.io.IOException; 37import java.io.FileInputStream; 38import java.security.MessageDigest; 39import java.security.NoSuchAlgorithmException; 40import java.util.Arrays; 41 42/** 43 * Downloads a given file and checks whether its hash matches a given hash to verify that the 44 * intended file was downloaded. 45 */ 46public class DownloadPackageTask { 47 public static final int ERROR_HASH_MISMATCH = 0; 48 public static final int ERROR_DOWNLOAD_FAILED = 1; 49 public static final int ERROR_OTHER = 2; 50 51 private static final String HASH_TYPE = "SHA-1"; 52 53 private final Context mContext; 54 private final String mDownloadLocationFrom; 55 private final Callback mCallback; 56 private final byte[] mHash; 57 58 private boolean mDoneDownloading; 59 private String mDownloadLocationTo; 60 private long mDownloadId; 61 private BroadcastReceiver mReceiver; 62 63 public DownloadPackageTask (Context context, String downloadLocation, byte[] hash, 64 Callback callback) { 65 mCallback = callback; 66 mContext = context; 67 mDownloadLocationFrom = downloadLocation; 68 mHash = hash; 69 mDoneDownloading = false; 70 } 71 72 public boolean downloadLocationWasProvided() { 73 return !TextUtils.isEmpty(mDownloadLocationFrom); 74 } 75 76 public void run() { 77 mReceiver = createDownloadReceiver(); 78 mContext.registerReceiver(mReceiver, 79 new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)); 80 81 ProvisionLogger.logd("Starting download from " + mDownloadLocationFrom); 82 DownloadManager dm = (DownloadManager) mContext 83 .getSystemService(Context.DOWNLOAD_SERVICE); 84 Request r = new Request(Uri.parse(mDownloadLocationFrom)); 85 mDownloadId = dm.enqueue(r); 86 } 87 88 private BroadcastReceiver createDownloadReceiver() { 89 return new BroadcastReceiver() { 90 @Override 91 public void onReceive(Context context, Intent intent) { 92 if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(intent.getAction())) { 93 Query q = new Query(); 94 q.setFilterById(mDownloadId); 95 DownloadManager dm = (DownloadManager) mContext 96 .getSystemService(Context.DOWNLOAD_SERVICE); 97 Cursor c = dm.query(q); 98 if (c.moveToFirst()) { 99 int columnIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS); 100 if (DownloadManager.STATUS_SUCCESSFUL == c.getInt(columnIndex)) { 101 String location = c.getString( 102 c.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME)); 103 c.close(); 104 onDownloadSuccess(location); 105 } else if (DownloadManager.STATUS_FAILED == c.getInt(columnIndex)){ 106 int reason = c.getColumnIndex(DownloadManager.COLUMN_REASON); 107 c.close(); 108 onDownloadFail(reason); 109 } 110 } 111 } 112 } 113 }; 114 } 115 116 private void onDownloadSuccess(String location) { 117 if (mDoneDownloading) { 118 // DownloadManager can send success more than once. Only act first time. 119 return; 120 } else { 121 mDoneDownloading = true; 122 } 123 124 ProvisionLogger.logd("Downloaded succesfully to: " + location); 125 126 // Check whether hash of downloaded file matches hash given in constructor. 127 byte[] hash = computeHash(location); 128 if (hash == null) { 129 130 // Error should have been reported in computeHash(). 131 return; 132 } 133 134 if (Arrays.equals(mHash, hash)) { 135 ProvisionLogger.logd(HASH_TYPE + "-hashes matched, both are " 136 + byteArrayToString(hash)); 137 mDownloadLocationTo = location; 138 mCallback.onSuccess(); 139 } else { 140 ProvisionLogger.loge(HASH_TYPE + "-hash of downloaded file does not match given hash."); 141 ProvisionLogger.loge(HASH_TYPE + "-hash of downloaded file: " 142 + byteArrayToString(hash)); 143 ProvisionLogger.loge(HASH_TYPE + "-hash provided by programmer: " 144 + byteArrayToString(mHash)); 145 146 mCallback.onError(ERROR_HASH_MISMATCH); 147 } 148 } 149 150 private void onDownloadFail(int errorCode) { 151 ProvisionLogger.loge("Downloading package failed."); 152 ProvisionLogger.loge("COLUMN_REASON in DownloadManager response has value: " 153 + errorCode); 154 mCallback.onError(ERROR_DOWNLOAD_FAILED); 155 } 156 157 private byte[] computeHash(String fileLocation) { 158 InputStream fis = null; 159 MessageDigest md; 160 byte hash[] = null; 161 try { 162 md = MessageDigest.getInstance(HASH_TYPE); 163 } catch (NoSuchAlgorithmException e) { 164 ProvisionLogger.loge("Hashing algorithm " + HASH_TYPE + " not supported.", e); 165 mCallback.onError(ERROR_OTHER); 166 return null; 167 } 168 try { 169 fis = new FileInputStream(fileLocation); 170 171 byte[] buffer = new byte[256]; 172 int n = 0; 173 while (n != -1) { 174 n = fis.read(buffer); 175 if (n > 0) { 176 md.update(buffer, 0, n); 177 } 178 } 179 hash = md.digest(); 180 } catch (IOException e) { 181 ProvisionLogger.loge("IO error.", e); 182 mCallback.onError(ERROR_OTHER); 183 } finally { 184 // Close input stream quietly. 185 try { 186 if (fis != null) { 187 fis.close(); 188 } 189 } catch (IOException e) { 190 // Ignore. 191 } 192 } 193 return hash; 194 } 195 196 public String getDownloadedPackageLocation() { 197 return mDownloadLocationTo; 198 } 199 200 public void cleanUp() { 201 if (mReceiver != null) { 202 //Unregister receiver. 203 mContext.unregisterReceiver(mReceiver); 204 mReceiver = null; 205 206 //Remove download. 207 DownloadManager dm = (DownloadManager) mContext 208 .getSystemService(Context.DOWNLOAD_SERVICE); 209 boolean removeSuccess = dm.remove(mDownloadId) == 1; 210 if (removeSuccess) { 211 ProvisionLogger.logd("Successfully removed the device owner installer file."); 212 } else { 213 ProvisionLogger.loge("Could not remove the device owner installer file."); 214 // Ignore this error. Failing cleanup should not stop provisioning flow. 215 } 216 } 217 } 218 219 // For logging purposes only. 220 String byteArrayToString(byte[] ba) { 221 return Base64.encodeToString(ba, Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP); 222 } 223 224 public abstract static class Callback { 225 public abstract void onSuccess(); 226 public abstract void onError(int errorCode); 227 } 228}