• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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