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