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