blob: e20c4901ea0d9b162277a22f7f9bd276b0700649 [file] [log] [blame]
Steve Howarda2709362010-07-02 17:12:48 -07001/*
2 * Copyright (C) 2010 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
Steve Howardd58429f2010-09-27 16:32:39 -070017package android.app;
Steve Howarda2709362010-07-02 17:12:48 -070018
Ben Lin726bf6a2016-04-27 11:38:05 -070019import android.annotation.Nullable;
Jeff Sharkey32cd2fb2013-10-02 10:11:22 -070020import android.annotation.SdkConstant;
Jeff Sharkey4233f032017-07-15 12:58:38 -060021import android.annotation.SdkConstant.SdkConstantType;
Jeff Sharkey8d5d0652017-04-04 14:38:39 -060022import android.annotation.SystemApi;
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060023import android.annotation.SystemService;
Sudheer Shanka957954b2019-02-28 13:33:39 -080024import android.annotation.TestApi;
Mathew Inwood61e8ae62018-08-14 14:17:44 +010025import android.annotation.UnsupportedAppUsage;
Steve Howarda2709362010-07-02 17:12:48 -070026import android.content.ContentResolver;
Steve Howardeca77fc2010-09-12 18:49:08 -070027import android.content.ContentUris;
Steve Howarda2709362010-07-02 17:12:48 -070028import android.content.ContentValues;
Steve Howard4f564cd2010-09-22 15:57:25 -070029import android.content.Context;
Steve Howarda2709362010-07-02 17:12:48 -070030import android.database.Cursor;
31import android.database.CursorWrapper;
Steve Howardd58429f2010-09-27 16:32:39 -070032import android.net.ConnectivityManager;
Jeff Sharkey1a303952011-06-16 13:04:20 -070033import android.net.NetworkPolicyManager;
Steve Howardd58429f2010-09-27 16:32:39 -070034import android.net.Uri;
Jeff Sharkey60cfad82016-01-05 17:30:57 -070035import android.os.Build;
Steve Howard4f564cd2010-09-22 15:57:25 -070036import android.os.Environment;
Ben Lin726bf6a2016-04-27 11:38:05 -070037import android.os.FileUtils;
Steve Howarda2709362010-07-02 17:12:48 -070038import android.os.ParcelFileDescriptor;
39import android.provider.Downloads;
Jeff Sharkey4233f032017-07-15 12:58:38 -060040import android.provider.Settings;
Vasu Nori0abbf802011-01-17 15:08:14 -080041import android.provider.Settings.SettingNotFoundException;
Vasu Noric0e50752011-01-20 17:57:54 -080042import android.text.TextUtils;
Steve Howard4f564cd2010-09-22 15:57:25 -070043import android.util.Pair;
Steve Howarda2709362010-07-02 17:12:48 -070044
Steve Howard4f564cd2010-09-22 15:57:25 -070045import java.io.File;
Steve Howarda2709362010-07-02 17:12:48 -070046import java.io.FileNotFoundException;
47import java.util.ArrayList;
Steve Howarda2709362010-07-02 17:12:48 -070048import java.util.List;
Steve Howarda2709362010-07-02 17:12:48 -070049
50/**
51 * The download manager is a system service that handles long-running HTTP downloads. Clients may
52 * request that a URI be downloaded to a particular destination file. The download manager will
53 * conduct the download in the background, taking care of HTTP interactions and retrying downloads
54 * after failures or across connectivity changes and system reboots.
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060055 * <p>
Steve Howard610c4352010-09-30 18:30:04 -070056 * Apps that request downloads through this API should register a broadcast receiver for
57 * {@link #ACTION_NOTIFICATION_CLICKED} to appropriately handle when the user clicks on a running
58 * download in a notification or from the downloads UI.
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060059 * <p>
Nicolas Falliere9530e3a2012-06-18 17:21:06 -070060 * Note that the application must have the {@link android.Manifest.permission#INTERNET}
61 * permission to use this class.
Steve Howarda2709362010-07-02 17:12:48 -070062 */
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060063@SystemService(Context.DOWNLOAD_SERVICE)
Steve Howarda2709362010-07-02 17:12:48 -070064public class DownloadManager {
Vasu Norie7be6bd2010-10-10 14:58:08 -070065
Steve Howarda2709362010-07-02 17:12:48 -070066 /**
67 * An identifier for a particular download, unique across the system. Clients use this ID to
68 * make subsequent calls related to the download.
69 */
Vasu Norief7e33b2010-10-20 13:26:02 -070070 public final static String COLUMN_ID = Downloads.Impl._ID;
Steve Howarda2709362010-07-02 17:12:48 -070071
72 /**
Steve Howard8651bd52010-08-03 12:35:32 -070073 * The client-supplied title for this download. This will be displayed in system notifications.
74 * Defaults to the empty string.
Steve Howarda2709362010-07-02 17:12:48 -070075 */
Vasu Norief7e33b2010-10-20 13:26:02 -070076 public final static String COLUMN_TITLE = Downloads.Impl.COLUMN_TITLE;
Steve Howarda2709362010-07-02 17:12:48 -070077
78 /**
79 * The client-supplied description of this download. This will be displayed in system
Steve Howard8651bd52010-08-03 12:35:32 -070080 * notifications. Defaults to the empty string.
Steve Howarda2709362010-07-02 17:12:48 -070081 */
Vasu Norief7e33b2010-10-20 13:26:02 -070082 public final static String COLUMN_DESCRIPTION = Downloads.Impl.COLUMN_DESCRIPTION;
Steve Howarda2709362010-07-02 17:12:48 -070083
84 /**
85 * URI to be downloaded.
86 */
Vasu Norief7e33b2010-10-20 13:26:02 -070087 public final static String COLUMN_URI = Downloads.Impl.COLUMN_URI;
Steve Howarda2709362010-07-02 17:12:48 -070088
89 /**
Steve Howard8651bd52010-08-03 12:35:32 -070090 * Internet Media Type of the downloaded file. If no value is provided upon creation, this will
91 * initially be null and will be filled in based on the server's response once the download has
92 * started.
Steve Howarda2709362010-07-02 17:12:48 -070093 *
94 * @see <a href="http://www.ietf.org/rfc/rfc1590.txt">RFC 1590, defining Media Types</a>
95 */
96 public final static String COLUMN_MEDIA_TYPE = "media_type";
97
98 /**
Steve Howard8651bd52010-08-03 12:35:32 -070099 * Total size of the download in bytes. This will initially be -1 and will be filled in once
100 * the download starts.
Steve Howarda2709362010-07-02 17:12:48 -0700101 */
102 public final static String COLUMN_TOTAL_SIZE_BYTES = "total_size";
103
104 /**
105 * Uri where downloaded file will be stored. If a destination is supplied by client, that URI
Steve Howard8651bd52010-08-03 12:35:32 -0700106 * will be used here. Otherwise, the value will initially be null and will be filled in with a
107 * generated URI once the download has started.
Steve Howarda2709362010-07-02 17:12:48 -0700108 */
109 public final static String COLUMN_LOCAL_URI = "local_uri";
110
111 /**
Jeff Sharkey60cfad82016-01-05 17:30:57 -0700112 * Path to the downloaded file on disk.
113 * <p>
114 * Note that apps may not have filesystem permissions to directly access
115 * this path. Instead of trying to open this path directly, apps should use
116 * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain access.
117 *
118 * @deprecated apps should transition to using
119 * {@link ContentResolver#openFileDescriptor(Uri, String)}
120 * instead.
Doug Zongkeree04af32010-10-08 13:42:16 -0700121 */
Jeff Sharkey60cfad82016-01-05 17:30:57 -0700122 @Deprecated
Doug Zongkeree04af32010-10-08 13:42:16 -0700123 public final static String COLUMN_LOCAL_FILENAME = "local_filename";
124
125 /**
Steve Howarda2709362010-07-02 17:12:48 -0700126 * Current status of the download, as one of the STATUS_* constants.
127 */
Vasu Norief7e33b2010-10-20 13:26:02 -0700128 public final static String COLUMN_STATUS = Downloads.Impl.COLUMN_STATUS;
Steve Howarda2709362010-07-02 17:12:48 -0700129
130 /**
Steve Howard3e8c1d32010-09-29 17:03:32 -0700131 * Provides more detail on the status of the download. Its meaning depends on the value of
132 * {@link #COLUMN_STATUS}.
Steve Howarda2709362010-07-02 17:12:48 -0700133 *
Steve Howard3e8c1d32010-09-29 17:03:32 -0700134 * When {@link #COLUMN_STATUS} is {@link #STATUS_FAILED}, this indicates the type of error that
135 * occurred. If an HTTP error occurred, this will hold the HTTP status code as defined in RFC
136 * 2616. Otherwise, it will hold one of the ERROR_* constants.
137 *
138 * When {@link #COLUMN_STATUS} is {@link #STATUS_PAUSED}, this indicates why the download is
139 * paused. It will hold one of the PAUSED_* constants.
140 *
141 * If {@link #COLUMN_STATUS} is neither {@link #STATUS_FAILED} nor {@link #STATUS_PAUSED}, this
142 * column's value is undefined.
Steve Howarda2709362010-07-02 17:12:48 -0700143 *
144 * @see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1.1">RFC 2616
145 * status codes</a>
146 */
Steve Howard3e8c1d32010-09-29 17:03:32 -0700147 public final static String COLUMN_REASON = "reason";
Steve Howarda2709362010-07-02 17:12:48 -0700148
149 /**
150 * Number of bytes download so far.
151 */
152 public final static String COLUMN_BYTES_DOWNLOADED_SO_FAR = "bytes_so_far";
153
154 /**
Steve Howardadcb6972010-07-12 17:09:25 -0700155 * Timestamp when the download was last modified, in {@link System#currentTimeMillis
Steve Howarda2709362010-07-02 17:12:48 -0700156 * System.currentTimeMillis()} (wall clock time in UTC).
157 */
Steve Howardadcb6972010-07-12 17:09:25 -0700158 public final static String COLUMN_LAST_MODIFIED_TIMESTAMP = "last_modified_timestamp";
Steve Howarda2709362010-07-02 17:12:48 -0700159
Vasu Nori216fa222010-10-12 23:08:13 -0700160 /**
161 * The URI to the corresponding entry in MediaProvider for this downloaded entry. It is
162 * used to delete the entries from MediaProvider database when it is deleted from the
163 * downloaded list.
164 */
Vasu Norief7e33b2010-10-20 13:26:02 -0700165 public static final String COLUMN_MEDIAPROVIDER_URI = Downloads.Impl.COLUMN_MEDIAPROVIDER_URI;
Steve Howarda2709362010-07-02 17:12:48 -0700166
Sudheer Shanka957954b2019-02-28 13:33:39 -0800167 /** @hide */
168 @TestApi
169 public static final String COLUMN_MEDIASTORE_URI = Downloads.Impl.COLUMN_MEDIASTORE_URI;
170
Steve Howarda2709362010-07-02 17:12:48 -0700171 /**
Jeff Sharkeyb180a652013-09-23 14:23:41 -0700172 * @hide
173 */
174 public final static String COLUMN_ALLOW_WRITE = Downloads.Impl.COLUMN_ALLOW_WRITE;
175
176 /**
Steve Howarda2709362010-07-02 17:12:48 -0700177 * Value of {@link #COLUMN_STATUS} when the download is waiting to start.
178 */
179 public final static int STATUS_PENDING = 1 << 0;
180
181 /**
182 * Value of {@link #COLUMN_STATUS} when the download is currently running.
183 */
184 public final static int STATUS_RUNNING = 1 << 1;
185
186 /**
187 * Value of {@link #COLUMN_STATUS} when the download is waiting to retry or resume.
188 */
189 public final static int STATUS_PAUSED = 1 << 2;
190
191 /**
192 * Value of {@link #COLUMN_STATUS} when the download has successfully completed.
193 */
194 public final static int STATUS_SUCCESSFUL = 1 << 3;
195
196 /**
197 * Value of {@link #COLUMN_STATUS} when the download has failed (and will not be retried).
198 */
199 public final static int STATUS_FAILED = 1 << 4;
200
Steve Howarda2709362010-07-02 17:12:48 -0700201 /**
202 * Value of COLUMN_ERROR_CODE when the download has completed with an error that doesn't fit
203 * under any other error code.
204 */
205 public final static int ERROR_UNKNOWN = 1000;
206
207 /**
Steve Howard3e8c1d32010-09-29 17:03:32 -0700208 * Value of {@link #COLUMN_REASON} when a storage issue arises which doesn't fit under any
Steve Howarda2709362010-07-02 17:12:48 -0700209 * other error code. Use the more specific {@link #ERROR_INSUFFICIENT_SPACE} and
210 * {@link #ERROR_DEVICE_NOT_FOUND} when appropriate.
211 */
212 public final static int ERROR_FILE_ERROR = 1001;
213
214 /**
Steve Howard3e8c1d32010-09-29 17:03:32 -0700215 * Value of {@link #COLUMN_REASON} when an HTTP code was received that download manager
Steve Howarda2709362010-07-02 17:12:48 -0700216 * can't handle.
217 */
218 public final static int ERROR_UNHANDLED_HTTP_CODE = 1002;
219
220 /**
Steve Howard3e8c1d32010-09-29 17:03:32 -0700221 * Value of {@link #COLUMN_REASON} when an error receiving or processing data occurred at
Steve Howarda2709362010-07-02 17:12:48 -0700222 * the HTTP level.
223 */
224 public final static int ERROR_HTTP_DATA_ERROR = 1004;
225
226 /**
Steve Howard3e8c1d32010-09-29 17:03:32 -0700227 * Value of {@link #COLUMN_REASON} when there were too many redirects.
Steve Howarda2709362010-07-02 17:12:48 -0700228 */
229 public final static int ERROR_TOO_MANY_REDIRECTS = 1005;
230
231 /**
Steve Howard3e8c1d32010-09-29 17:03:32 -0700232 * Value of {@link #COLUMN_REASON} when there was insufficient storage space. Typically,
Steve Howarda2709362010-07-02 17:12:48 -0700233 * this is because the SD card is full.
234 */
235 public final static int ERROR_INSUFFICIENT_SPACE = 1006;
236
237 /**
Steve Howard3e8c1d32010-09-29 17:03:32 -0700238 * Value of {@link #COLUMN_REASON} when no external storage device was found. Typically,
Steve Howarda2709362010-07-02 17:12:48 -0700239 * this is because the SD card is not mounted.
240 */
241 public final static int ERROR_DEVICE_NOT_FOUND = 1007;
242
Steve Howardb8e07a52010-07-21 14:53:21 -0700243 /**
Steve Howard3e8c1d32010-09-29 17:03:32 -0700244 * Value of {@link #COLUMN_REASON} when some possibly transient error occurred but we can't
Steve Howard33bbd122010-08-02 17:51:29 -0700245 * resume the download.
246 */
247 public final static int ERROR_CANNOT_RESUME = 1008;
248
249 /**
Steve Howard3e8c1d32010-09-29 17:03:32 -0700250 * Value of {@link #COLUMN_REASON} when the requested destination file already exists (the
Steve Howarda9e87c92010-09-16 12:02:03 -0700251 * download manager will not overwrite an existing file).
252 */
253 public final static int ERROR_FILE_ALREADY_EXISTS = 1009;
254
255 /**
Jeff Sharkeyfdfef572011-06-16 15:07:48 -0700256 * Value of {@link #COLUMN_REASON} when the download has failed because of
257 * {@link NetworkPolicyManager} controls on the requesting application.
258 *
259 * @hide
260 */
261 public final static int ERROR_BLOCKED = 1010;
262
263 /**
Steve Howard3e8c1d32010-09-29 17:03:32 -0700264 * Value of {@link #COLUMN_REASON} when the download is paused because some network error
265 * occurred and the download manager is waiting before retrying the request.
266 */
267 public final static int PAUSED_WAITING_TO_RETRY = 1;
268
269 /**
270 * Value of {@link #COLUMN_REASON} when the download is waiting for network connectivity to
271 * proceed.
272 */
273 public final static int PAUSED_WAITING_FOR_NETWORK = 2;
274
275 /**
276 * Value of {@link #COLUMN_REASON} when the download exceeds a size limit for downloads over
277 * the mobile network and the download manager is waiting for a Wi-Fi connection to proceed.
278 */
279 public final static int PAUSED_QUEUED_FOR_WIFI = 3;
280
281 /**
282 * Value of {@link #COLUMN_REASON} when the download is paused for some other reason.
283 */
284 public final static int PAUSED_UNKNOWN = 4;
285
286 /**
Steve Howardb8e07a52010-07-21 14:53:21 -0700287 * Broadcast intent action sent by the download manager when a download completes.
288 */
Jeff Sharkey32cd2fb2013-10-02 10:11:22 -0700289 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Steve Howardb8e07a52010-07-21 14:53:21 -0700290 public final static String ACTION_DOWNLOAD_COMPLETE = "android.intent.action.DOWNLOAD_COMPLETE";
291
292 /**
Steve Howard610c4352010-09-30 18:30:04 -0700293 * Broadcast intent action sent by the download manager when the user clicks on a running
294 * download, either from a system notification or from the downloads UI.
Steve Howardb8e07a52010-07-21 14:53:21 -0700295 */
Jeff Sharkey32cd2fb2013-10-02 10:11:22 -0700296 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Steve Howardb8e07a52010-07-21 14:53:21 -0700297 public final static String ACTION_NOTIFICATION_CLICKED =
298 "android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED";
299
300 /**
Steve Howarde78fc182010-09-24 14:59:36 -0700301 * Intent action to launch an activity to display all downloads.
302 */
Jeff Sharkey32cd2fb2013-10-02 10:11:22 -0700303 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
Steve Howarde78fc182010-09-24 14:59:36 -0700304 public final static String ACTION_VIEW_DOWNLOADS = "android.intent.action.VIEW_DOWNLOADS";
305
306 /**
Vasu Norie5f92242011-01-24 16:12:20 -0800307 * Intent extra included with {@link #ACTION_VIEW_DOWNLOADS} to start DownloadApp in
308 * sort-by-size mode.
309 */
310 public final static String INTENT_EXTRAS_SORT_BY_SIZE =
311 "android.app.DownloadManager.extra_sortBySize";
312
313 /**
Steve Howardb8e07a52010-07-21 14:53:21 -0700314 * Intent extra included with {@link #ACTION_DOWNLOAD_COMPLETE} intents, indicating the ID (as a
315 * long) of the download that just completed.
316 */
317 public static final String EXTRA_DOWNLOAD_ID = "extra_download_id";
Steve Howarda2709362010-07-02 17:12:48 -0700318
Vasu Nori71b8c232010-10-27 15:22:19 -0700319 /**
320 * When clicks on multiple notifications are received, the following
321 * provides an array of download ids corresponding to the download notification that was
322 * clicked. It can be retrieved by the receiver of this
323 * Intent using {@link android.content.Intent#getLongArrayExtra(String)}.
324 */
325 public static final String EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS = "extra_click_download_ids";
326
Jeff Sharkey8d5d0652017-04-04 14:38:39 -0600327 /** {@hide} */
328 @SystemApi
329 public static final String ACTION_DOWNLOAD_COMPLETED =
330 "android.intent.action.DOWNLOAD_COMPLETED";
331
Vasu Norie16c43b2010-11-06 18:48:08 -0700332 /**
333 * columns to request from DownloadProvider.
334 * @hide
335 */
Mathew Inwood61e8ae62018-08-14 14:17:44 +0100336 @UnsupportedAppUsage
Vasu Norie16c43b2010-11-06 18:48:08 -0700337 public static final String[] UNDERLYING_COLUMNS = new String[] {
Steve Howarda2709362010-07-02 17:12:48 -0700338 Downloads.Impl._ID,
Vasu Norie69924f2010-11-15 13:10:11 -0800339 Downloads.Impl._DATA + " AS " + COLUMN_LOCAL_FILENAME,
Vasu Nori216fa222010-10-12 23:08:13 -0700340 Downloads.Impl.COLUMN_MEDIAPROVIDER_URI,
Vasu Nori5be894e2010-11-02 21:55:30 -0700341 Downloads.Impl.COLUMN_DESTINATION,
Vasu Norief7e33b2010-10-20 13:26:02 -0700342 Downloads.Impl.COLUMN_TITLE,
343 Downloads.Impl.COLUMN_DESCRIPTION,
344 Downloads.Impl.COLUMN_URI,
Vasu Norief7e33b2010-10-20 13:26:02 -0700345 Downloads.Impl.COLUMN_STATUS,
Steve Howarda9e87c92010-09-16 12:02:03 -0700346 Downloads.Impl.COLUMN_FILE_NAME_HINT,
Vasu Norie16c43b2010-11-06 18:48:08 -0700347 Downloads.Impl.COLUMN_MIME_TYPE + " AS " + COLUMN_MEDIA_TYPE,
348 Downloads.Impl.COLUMN_TOTAL_BYTES + " AS " + COLUMN_TOTAL_SIZE_BYTES,
349 Downloads.Impl.COLUMN_LAST_MODIFICATION + " AS " + COLUMN_LAST_MODIFIED_TIMESTAMP,
350 Downloads.Impl.COLUMN_CURRENT_BYTES + " AS " + COLUMN_BYTES_DOWNLOADED_SO_FAR,
Jeff Sharkeyb180a652013-09-23 14:23:41 -0700351 Downloads.Impl.COLUMN_ALLOW_WRITE,
Vasu Norie16c43b2010-11-06 18:48:08 -0700352 /* add the following 'computed' columns to the cursor.
353 * they are not 'returned' by the database, but their inclusion
354 * eliminates need to have lot of methods in CursorTranslator
355 */
356 "'placeholder' AS " + COLUMN_LOCAL_URI,
357 "'placeholder' AS " + COLUMN_REASON
Steve Howarda2709362010-07-02 17:12:48 -0700358 };
359
Steve Howarda2709362010-07-02 17:12:48 -0700360 /**
Steve Howard4f564cd2010-09-22 15:57:25 -0700361 * This class contains all the information necessary to request a new download. The URI is the
Steve Howarda2709362010-07-02 17:12:48 -0700362 * only required parameter.
Steve Howard4f564cd2010-09-22 15:57:25 -0700363 *
364 * Note that the default download destination is a shared volume where the system might delete
365 * your file if it needs to reclaim space for system use. If this is a problem, use a location
366 * on external storage (see {@link #setDestinationUri(Uri)}.
Steve Howarda2709362010-07-02 17:12:48 -0700367 */
368 public static class Request {
369 /**
Steve Howardb8e07a52010-07-21 14:53:21 -0700370 * Bit flag for {@link #setAllowedNetworkTypes} corresponding to
371 * {@link ConnectivityManager#TYPE_MOBILE}.
372 */
373 public static final int NETWORK_MOBILE = 1 << 0;
Steve Howarda2709362010-07-02 17:12:48 -0700374
Steve Howardb8e07a52010-07-21 14:53:21 -0700375 /**
376 * Bit flag for {@link #setAllowedNetworkTypes} corresponding to
377 * {@link ConnectivityManager#TYPE_WIFI}.
378 */
379 public static final int NETWORK_WIFI = 1 << 1;
380
HÃ¥kan3 Johansson011238b2012-02-08 21:13:35 +0100381 /**
382 * Bit flag for {@link #setAllowedNetworkTypes} corresponding to
383 * {@link ConnectivityManager#TYPE_BLUETOOTH}.
384 * @hide
385 */
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -0600386 @Deprecated
HÃ¥kan3 Johansson011238b2012-02-08 21:13:35 +0100387 public static final int NETWORK_BLUETOOTH = 1 << 2;
388
Mathew Inwood61e8ae62018-08-14 14:17:44 +0100389 @UnsupportedAppUsage
Steve Howardb8e07a52010-07-21 14:53:21 -0700390 private Uri mUri;
391 private Uri mDestinationUri;
Steve Howard4f564cd2010-09-22 15:57:25 -0700392 private List<Pair<String, String>> mRequestHeaders = new ArrayList<Pair<String, String>>();
393 private CharSequence mTitle;
394 private CharSequence mDescription;
Steve Howard4f564cd2010-09-22 15:57:25 -0700395 private String mMimeType;
Steve Howardb8e07a52010-07-21 14:53:21 -0700396 private int mAllowedNetworkTypes = ~0; // default to all network types allowed
Jeff Sharkey15ec7d62012-04-17 12:23:40 -0700397 private boolean mRoamingAllowed = true;
398 private boolean mMeteredAllowed = true;
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -0600399 private int mFlags = 0;
Steve Howard90fb15a2010-09-09 16:13:41 -0700400 private boolean mIsVisibleInDownloadsUi = true;
Vasu Nori5be894e2010-11-02 21:55:30 -0700401 private boolean mScannable = false;
Vasu Nori1cde3fb2010-11-05 11:02:52 -0700402 /** if a file is designated as a MediaScanner scannable file, the following value is
403 * stored in the database column {@link Downloads.Impl#COLUMN_MEDIA_SCANNED}.
404 */
Sudheer Shanka684c02a2019-01-29 17:41:15 -0800405 private static final int SCANNABLE_VALUE_YES = Downloads.Impl.MEDIA_NOT_SCANNED;
Vasu Nori1cde3fb2010-11-05 11:02:52 -0700406 // value of 1 is stored in the above column by DownloadProvider after it is scanned by
407 // MediaScanner
408 /** if a file is designated as a file that should not be scanned by MediaScanner,
409 * the following value is stored in the database column
410 * {@link Downloads.Impl#COLUMN_MEDIA_SCANNED}.
411 */
Sudheer Shanka684c02a2019-01-29 17:41:15 -0800412 private static final int SCANNABLE_VALUE_NO = Downloads.Impl.MEDIA_NOT_SCANNABLE;
Steve Howarda2709362010-07-02 17:12:48 -0700413
414 /**
Vasu Nori4c6e5df2010-10-26 17:00:16 -0700415 * This download is visible but only shows in the notifications
416 * while it's in progress.
417 */
418 public static final int VISIBILITY_VISIBLE = 0;
419
420 /**
421 * This download is visible and shows in the notifications while
422 * in progress and after completion.
423 */
424 public static final int VISIBILITY_VISIBLE_NOTIFY_COMPLETED = 1;
425
426 /**
427 * This download doesn't show in the UI or in the notifications.
428 */
429 public static final int VISIBILITY_HIDDEN = 2;
430
Vasu Norif9e85232011-02-10 14:59:54 -0800431 /**
432 * This download shows in the notifications after completion ONLY.
433 * It is usuable only with
Vasu Nori37281302011-03-07 11:25:01 -0800434 * {@link DownloadManager#addCompletedDownload(String, String,
435 * boolean, String, String, long, boolean)}.
Vasu Norif9e85232011-02-10 14:59:54 -0800436 */
437 public static final int VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION = 3;
438
Vasu Nori4c6e5df2010-10-26 17:00:16 -0700439 /** can take any of the following values: {@link #VISIBILITY_HIDDEN}
Vasu Norif9e85232011-02-10 14:59:54 -0800440 * {@link #VISIBILITY_VISIBLE_NOTIFY_COMPLETED}, {@link #VISIBILITY_VISIBLE},
441 * {@link #VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION}
Vasu Nori4c6e5df2010-10-26 17:00:16 -0700442 */
443 private int mNotificationVisibility = VISIBILITY_VISIBLE;
444
445 /**
Alex Klyubin66629bc2015-06-11 14:34:44 -0700446 * @param uri the HTTP or HTTPS URI to download.
Steve Howarda2709362010-07-02 17:12:48 -0700447 */
448 public Request(Uri uri) {
449 if (uri == null) {
450 throw new NullPointerException();
451 }
452 String scheme = uri.getScheme();
Paul Westbrook86a60192010-09-15 12:55:49 -0700453 if (scheme == null || (!scheme.equals("http") && !scheme.equals("https"))) {
454 throw new IllegalArgumentException("Can only download HTTP/HTTPS URIs: " + uri);
Steve Howarda2709362010-07-02 17:12:48 -0700455 }
456 mUri = uri;
457 }
458
Vasu Noric0e50752011-01-20 17:57:54 -0800459 Request(String uriString) {
460 mUri = Uri.parse(uriString);
461 }
462
Steve Howarda2709362010-07-02 17:12:48 -0700463 /**
Steve Howard4f564cd2010-09-22 15:57:25 -0700464 * Set the local destination for the downloaded file. Must be a file URI to a path on
Steve Howarda2709362010-07-02 17:12:48 -0700465 * external storage, and the calling application must have the WRITE_EXTERNAL_STORAGE
466 * permission.
Vasu Nori5be894e2010-11-02 21:55:30 -0700467 * <p>
468 * The downloaded file is not scanned by MediaScanner.
469 * But it can be made scannable by calling {@link #allowScanningByMediaScanner()}.
470 * <p>
Steve Howard4f564cd2010-09-22 15:57:25 -0700471 * By default, downloads are saved to a generated filename in the shared download cache and
472 * may be deleted by the system at any time to reclaim space.
Steve Howarda2709362010-07-02 17:12:48 -0700473 *
474 * @return this object
475 */
476 public Request setDestinationUri(Uri uri) {
477 mDestinationUri = uri;
478 return this;
479 }
480
481 /**
Jeff Sharkey45d01ea2013-03-25 17:30:16 -0700482 * Set the local destination for the downloaded file to a path within
483 * the application's external files directory (as returned by
484 * {@link Context#getExternalFilesDir(String)}.
Vasu Nori5be894e2010-11-02 21:55:30 -0700485 * <p>
Jeff Sharkey45d01ea2013-03-25 17:30:16 -0700486 * The downloaded file is not scanned by MediaScanner. But it can be
487 * made scannable by calling {@link #allowScanningByMediaScanner()}.
Steve Howard4f564cd2010-09-22 15:57:25 -0700488 *
Jeff Sharkey45d01ea2013-03-25 17:30:16 -0700489 * @param context the {@link Context} to use in determining the external
490 * files directory
491 * @param dirType the directory type to pass to
492 * {@link Context#getExternalFilesDir(String)}
493 * @param subPath the path within the external directory, including the
494 * destination filename
Steve Howard4f564cd2010-09-22 15:57:25 -0700495 * @return this object
Jeff Sharkey45d01ea2013-03-25 17:30:16 -0700496 * @throws IllegalStateException If the external storage directory
497 * cannot be found or created.
Steve Howard4f564cd2010-09-22 15:57:25 -0700498 */
499 public Request setDestinationInExternalFilesDir(Context context, String dirType,
500 String subPath) {
Jeff Sharkey45d01ea2013-03-25 17:30:16 -0700501 final File file = context.getExternalFilesDir(dirType);
502 if (file == null) {
503 throw new IllegalStateException("Failed to get external storage files directory");
504 } else if (file.exists()) {
505 if (!file.isDirectory()) {
506 throw new IllegalStateException(file.getAbsolutePath() +
507 " already exists and is not a directory");
508 }
509 } else {
510 if (!file.mkdirs()) {
511 throw new IllegalStateException("Unable to create directory: "+
512 file.getAbsolutePath());
513 }
514 }
515 setDestinationFromBase(file, subPath);
Steve Howard4f564cd2010-09-22 15:57:25 -0700516 return this;
517 }
518
519 /**
Jeff Sharkey45d01ea2013-03-25 17:30:16 -0700520 * Set the local destination for the downloaded file to a path within
521 * the public external storage directory (as returned by
522 * {@link Environment#getExternalStoragePublicDirectory(String)}).
523 * <p>
524 * The downloaded file is not scanned by MediaScanner. But it can be
525 * made scannable by calling {@link #allowScanningByMediaScanner()}.
Steve Howard4f564cd2010-09-22 15:57:25 -0700526 *
Jeff Sharkey45d01ea2013-03-25 17:30:16 -0700527 * @param dirType the directory type to pass to {@link Environment#getExternalStoragePublicDirectory(String)}
528 * @param subPath the path within the external directory, including the
529 * destination filename
Steve Howard4f564cd2010-09-22 15:57:25 -0700530 * @return this object
Jeff Sharkey45d01ea2013-03-25 17:30:16 -0700531 * @throws IllegalStateException If the external storage directory
532 * cannot be found or created.
Steve Howard4f564cd2010-09-22 15:57:25 -0700533 */
534 public Request setDestinationInExternalPublicDir(String dirType, String subPath) {
Vasu Nori6916b032010-12-19 20:31:00 -0800535 File file = Environment.getExternalStoragePublicDirectory(dirType);
Jeff Sharkey45d01ea2013-03-25 17:30:16 -0700536 if (file == null) {
537 throw new IllegalStateException("Failed to get external storage public directory");
538 } else if (file.exists()) {
Vasu Nori6916b032010-12-19 20:31:00 -0800539 if (!file.isDirectory()) {
540 throw new IllegalStateException(file.getAbsolutePath() +
541 " already exists and is not a directory");
542 }
543 } else {
Roger Chen1740ada2012-12-17 13:31:17 +0800544 if (!file.mkdirs()) {
Vasu Nori6916b032010-12-19 20:31:00 -0800545 throw new IllegalStateException("Unable to create directory: "+
546 file.getAbsolutePath());
547 }
548 }
549 setDestinationFromBase(file, subPath);
Steve Howard4f564cd2010-09-22 15:57:25 -0700550 return this;
551 }
552
553 private void setDestinationFromBase(File base, String subPath) {
554 if (subPath == null) {
555 throw new NullPointerException("subPath cannot be null");
556 }
557 mDestinationUri = Uri.withAppendedPath(Uri.fromFile(base), subPath);
558 }
559
560 /**
Vasu Nori5be894e2010-11-02 21:55:30 -0700561 * If the file to be downloaded is to be scanned by MediaScanner, this method
562 * should be called before {@link DownloadManager#enqueue(Request)} is called.
563 */
564 public void allowScanningByMediaScanner() {
565 mScannable = true;
566 }
567
568 /**
Steve Howard4f564cd2010-09-22 15:57:25 -0700569 * Add an HTTP header to be included with the download request. The header will be added to
570 * the end of the list.
Steve Howarda2709362010-07-02 17:12:48 -0700571 * @param header HTTP header name
572 * @param value header value
573 * @return this object
Steve Howard4f564cd2010-09-22 15:57:25 -0700574 * @see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2">HTTP/1.1
575 * Message Headers</a>
Steve Howarda2709362010-07-02 17:12:48 -0700576 */
Steve Howard4f564cd2010-09-22 15:57:25 -0700577 public Request addRequestHeader(String header, String value) {
578 if (header == null) {
579 throw new NullPointerException("header cannot be null");
580 }
581 if (header.contains(":")) {
582 throw new IllegalArgumentException("header may not contain ':'");
583 }
584 if (value == null) {
585 value = "";
586 }
587 mRequestHeaders.add(Pair.create(header, value));
Steve Howarda2709362010-07-02 17:12:48 -0700588 return this;
589 }
590
591 /**
Steve Howard610c4352010-09-30 18:30:04 -0700592 * Set the title of this download, to be displayed in notifications (if enabled). If no
593 * title is given, a default one will be assigned based on the download filename, once the
594 * download starts.
Steve Howarda2709362010-07-02 17:12:48 -0700595 * @return this object
596 */
Steve Howard4f564cd2010-09-22 15:57:25 -0700597 public Request setTitle(CharSequence title) {
Steve Howarda2709362010-07-02 17:12:48 -0700598 mTitle = title;
599 return this;
600 }
601
602 /**
603 * Set a description of this download, to be displayed in notifications (if enabled)
604 * @return this object
605 */
Steve Howard4f564cd2010-09-22 15:57:25 -0700606 public Request setDescription(CharSequence description) {
Steve Howarda2709362010-07-02 17:12:48 -0700607 mDescription = description;
608 return this;
609 }
610
611 /**
Steve Howard4f564cd2010-09-22 15:57:25 -0700612 * Set the MIME content type of this download. This will override the content type declared
Steve Howarda2709362010-07-02 17:12:48 -0700613 * in the server's response.
Steve Howard4f564cd2010-09-22 15:57:25 -0700614 * @see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7">HTTP/1.1
615 * Media Types</a>
Steve Howarda2709362010-07-02 17:12:48 -0700616 * @return this object
617 */
Steve Howard4f564cd2010-09-22 15:57:25 -0700618 public Request setMimeType(String mimeType) {
619 mMimeType = mimeType;
Steve Howarda2709362010-07-02 17:12:48 -0700620 return this;
621 }
622
623 /**
Steve Howard8e15afe2010-07-28 17:12:40 -0700624 * Control whether a system notification is posted by the download manager while this
625 * download is running. If enabled, the download manager posts notifications about downloads
626 * through the system {@link android.app.NotificationManager}. By default, a notification is
627 * shown.
Steve Howarda2709362010-07-02 17:12:48 -0700628 *
Steve Howard8e15afe2010-07-28 17:12:40 -0700629 * If set to false, this requires the permission
630 * android.permission.DOWNLOAD_WITHOUT_NOTIFICATION.
631 *
632 * @param show whether the download manager should show a notification for this download.
Steve Howarda2709362010-07-02 17:12:48 -0700633 * @return this object
Vasu Nori4c6e5df2010-10-26 17:00:16 -0700634 * @deprecated use {@link #setNotificationVisibility(int)}
Steve Howarda2709362010-07-02 17:12:48 -0700635 */
Vasu Nori4c6e5df2010-10-26 17:00:16 -0700636 @Deprecated
Steve Howard8e15afe2010-07-28 17:12:40 -0700637 public Request setShowRunningNotification(boolean show) {
Vasu Nori4c6e5df2010-10-26 17:00:16 -0700638 return (show) ? setNotificationVisibility(VISIBILITY_VISIBLE) :
639 setNotificationVisibility(VISIBILITY_HIDDEN);
640 }
641
642 /**
643 * Control whether a system notification is posted by the download manager while this
644 * download is running or when it is completed.
645 * If enabled, the download manager posts notifications about downloads
646 * through the system {@link android.app.NotificationManager}.
647 * By default, a notification is shown only when the download is in progress.
648 *<p>
649 * It can take the following values: {@link #VISIBILITY_HIDDEN},
650 * {@link #VISIBILITY_VISIBLE},
651 * {@link #VISIBILITY_VISIBLE_NOTIFY_COMPLETED}.
652 *<p>
653 * If set to {@link #VISIBILITY_HIDDEN}, this requires the permission
654 * android.permission.DOWNLOAD_WITHOUT_NOTIFICATION.
655 *
656 * @param visibility the visibility setting value
657 * @return this object
658 */
659 public Request setNotificationVisibility(int visibility) {
660 mNotificationVisibility = visibility;
Steve Howarda2709362010-07-02 17:12:48 -0700661 return this;
662 }
663
Steve Howardb8e07a52010-07-21 14:53:21 -0700664 /**
Jeff Sharkey792e0912012-04-16 11:52:18 -0700665 * Restrict the types of networks over which this download may proceed.
666 * By default, all network types are allowed. Consider using
667 * {@link #setAllowedOverMetered(boolean)} instead, since it's more
668 * flexible.
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -0600669 * <p>
670 * As of {@link android.os.Build.VERSION_CODES#N}, setting only the
671 * {@link #NETWORK_WIFI} flag here is equivalent to calling
672 * {@link #setAllowedOverMetered(boolean)} with {@code false}.
Jeff Sharkey792e0912012-04-16 11:52:18 -0700673 *
Steve Howardb8e07a52010-07-21 14:53:21 -0700674 * @param flags any combination of the NETWORK_* bit flags.
675 * @return this object
676 */
Steve Howarda2709362010-07-02 17:12:48 -0700677 public Request setAllowedNetworkTypes(int flags) {
Steve Howardb8e07a52010-07-21 14:53:21 -0700678 mAllowedNetworkTypes = flags;
679 return this;
Steve Howarda2709362010-07-02 17:12:48 -0700680 }
681
Steve Howardb8e07a52010-07-21 14:53:21 -0700682 /**
683 * Set whether this download may proceed over a roaming connection. By default, roaming is
684 * allowed.
685 * @param allowed whether to allow a roaming connection to be used
686 * @return this object
687 */
Steve Howarda2709362010-07-02 17:12:48 -0700688 public Request setAllowedOverRoaming(boolean allowed) {
Steve Howardb8e07a52010-07-21 14:53:21 -0700689 mRoamingAllowed = allowed;
690 return this;
Steve Howarda2709362010-07-02 17:12:48 -0700691 }
692
693 /**
Jeff Sharkey15ec7d62012-04-17 12:23:40 -0700694 * Set whether this download may proceed over a metered network
695 * connection. By default, metered networks are allowed.
696 *
697 * @see ConnectivityManager#isActiveNetworkMetered()
698 */
699 public Request setAllowedOverMetered(boolean allow) {
700 mMeteredAllowed = allow;
701 return this;
702 }
703
704 /**
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -0600705 * Specify that to run this download, the device needs to be plugged in.
706 * This defaults to false.
707 *
708 * @param requiresCharging Whether or not the device is plugged in.
709 * @see android.app.job.JobInfo.Builder#setRequiresCharging(boolean)
710 */
711 public Request setRequiresCharging(boolean requiresCharging) {
712 if (requiresCharging) {
713 mFlags |= Downloads.Impl.FLAG_REQUIRES_CHARGING;
714 } else {
715 mFlags &= ~Downloads.Impl.FLAG_REQUIRES_CHARGING;
716 }
717 return this;
718 }
719
720 /**
721 * Specify that to run, the download needs the device to be in idle
722 * mode. This defaults to false.
723 * <p>
724 * Idle mode is a loose definition provided by the system, which means
725 * that the device is not in use, and has not been in use for some time.
726 *
727 * @param requiresDeviceIdle Whether or not the device need be within an
728 * idle maintenance window.
729 * @see android.app.job.JobInfo.Builder#setRequiresDeviceIdle(boolean)
730 */
731 public Request setRequiresDeviceIdle(boolean requiresDeviceIdle) {
732 if (requiresDeviceIdle) {
733 mFlags |= Downloads.Impl.FLAG_REQUIRES_DEVICE_IDLE;
734 } else {
735 mFlags &= ~Downloads.Impl.FLAG_REQUIRES_DEVICE_IDLE;
736 }
737 return this;
738 }
739
740 /**
Steve Howard90fb15a2010-09-09 16:13:41 -0700741 * Set whether this download should be displayed in the system's Downloads UI. True by
742 * default.
743 * @param isVisible whether to display this download in the Downloads UI
744 * @return this object
745 */
746 public Request setVisibleInDownloadsUi(boolean isVisible) {
747 mIsVisibleInDownloadsUi = isVisible;
748 return this;
749 }
750
751 /**
Steve Howarda2709362010-07-02 17:12:48 -0700752 * @return ContentValues to be passed to DownloadProvider.insert()
753 */
Steve Howardb8e07a52010-07-21 14:53:21 -0700754 ContentValues toContentValues(String packageName) {
Steve Howarda2709362010-07-02 17:12:48 -0700755 ContentValues values = new ContentValues();
756 assert mUri != null;
Vasu Norief7e33b2010-10-20 13:26:02 -0700757 values.put(Downloads.Impl.COLUMN_URI, mUri.toString());
Steve Howardb8e07a52010-07-21 14:53:21 -0700758 values.put(Downloads.Impl.COLUMN_IS_PUBLIC_API, true);
Vasu Norief7e33b2010-10-20 13:26:02 -0700759 values.put(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE, packageName);
Steve Howarda2709362010-07-02 17:12:48 -0700760
761 if (mDestinationUri != null) {
Jeff Sharkey4233f032017-07-15 12:58:38 -0600762 values.put(Downloads.Impl.COLUMN_DESTINATION,
763 Downloads.Impl.DESTINATION_FILE_URI);
764 values.put(Downloads.Impl.COLUMN_FILE_NAME_HINT,
765 mDestinationUri.toString());
Steve Howarda2709362010-07-02 17:12:48 -0700766 } else {
Vasu Norief7e33b2010-10-20 13:26:02 -0700767 values.put(Downloads.Impl.COLUMN_DESTINATION,
Jeff Sharkey4233f032017-07-15 12:58:38 -0600768 Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE);
Steve Howarda2709362010-07-02 17:12:48 -0700769 }
Vasu Nori5be894e2010-11-02 21:55:30 -0700770 // is the file supposed to be media-scannable?
Vasu Nori1cde3fb2010-11-05 11:02:52 -0700771 values.put(Downloads.Impl.COLUMN_MEDIA_SCANNED, (mScannable) ? SCANNABLE_VALUE_YES :
772 SCANNABLE_VALUE_NO);
Steve Howarda2709362010-07-02 17:12:48 -0700773
774 if (!mRequestHeaders.isEmpty()) {
Steve Howardea9147d2010-07-13 19:02:45 -0700775 encodeHttpHeaders(values);
Steve Howarda2709362010-07-02 17:12:48 -0700776 }
777
Vasu Norief7e33b2010-10-20 13:26:02 -0700778 putIfNonNull(values, Downloads.Impl.COLUMN_TITLE, mTitle);
779 putIfNonNull(values, Downloads.Impl.COLUMN_DESCRIPTION, mDescription);
780 putIfNonNull(values, Downloads.Impl.COLUMN_MIME_TYPE, mMimeType);
Steve Howarda2709362010-07-02 17:12:48 -0700781
Vasu Nori4c6e5df2010-10-26 17:00:16 -0700782 values.put(Downloads.Impl.COLUMN_VISIBILITY, mNotificationVisibility);
Steve Howardb8e07a52010-07-21 14:53:21 -0700783 values.put(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES, mAllowedNetworkTypes);
784 values.put(Downloads.Impl.COLUMN_ALLOW_ROAMING, mRoamingAllowed);
Jeff Sharkey15ec7d62012-04-17 12:23:40 -0700785 values.put(Downloads.Impl.COLUMN_ALLOW_METERED, mMeteredAllowed);
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -0600786 values.put(Downloads.Impl.COLUMN_FLAGS, mFlags);
Steve Howard90fb15a2010-09-09 16:13:41 -0700787 values.put(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, mIsVisibleInDownloadsUi);
Steve Howardb8e07a52010-07-21 14:53:21 -0700788
Steve Howarda2709362010-07-02 17:12:48 -0700789 return values;
790 }
791
Steve Howardea9147d2010-07-13 19:02:45 -0700792 private void encodeHttpHeaders(ContentValues values) {
793 int index = 0;
Steve Howard4f564cd2010-09-22 15:57:25 -0700794 for (Pair<String, String> header : mRequestHeaders) {
795 String headerString = header.first + ": " + header.second;
Steve Howardea9147d2010-07-13 19:02:45 -0700796 values.put(Downloads.Impl.RequestHeaders.INSERT_KEY_PREFIX + index, headerString);
797 index++;
798 }
799 }
800
Steve Howard4f564cd2010-09-22 15:57:25 -0700801 private void putIfNonNull(ContentValues contentValues, String key, Object value) {
Steve Howarda2709362010-07-02 17:12:48 -0700802 if (value != null) {
Steve Howard4f564cd2010-09-22 15:57:25 -0700803 contentValues.put(key, value.toString());
Steve Howarda2709362010-07-02 17:12:48 -0700804 }
805 }
806 }
807
808 /**
809 * This class may be used to filter download manager queries.
810 */
811 public static class Query {
Steve Howardf054e192010-09-01 18:26:26 -0700812 /**
813 * Constant for use with {@link #orderBy}
814 * @hide
815 */
816 public static final int ORDER_ASCENDING = 1;
817
818 /**
819 * Constant for use with {@link #orderBy}
820 * @hide
821 */
822 public static final int ORDER_DESCENDING = 2;
823
Steve Howard64c48b82010-10-07 17:53:52 -0700824 private long[] mIds = null;
Steve Howarda2709362010-07-02 17:12:48 -0700825 private Integer mStatusFlags = null;
Ben Lince763d82016-05-02 16:45:45 -0700826 private String mFilterString = null;
Vasu Norief7e33b2010-10-20 13:26:02 -0700827 private String mOrderByColumn = Downloads.Impl.COLUMN_LAST_MODIFICATION;
Steve Howardf054e192010-09-01 18:26:26 -0700828 private int mOrderDirection = ORDER_DESCENDING;
Steve Howard90fb15a2010-09-09 16:13:41 -0700829 private boolean mOnlyIncludeVisibleInDownloadsUi = false;
Steve Howarda2709362010-07-02 17:12:48 -0700830
831 /**
Steve Howard64c48b82010-10-07 17:53:52 -0700832 * Include only the downloads with the given IDs.
Steve Howarda2709362010-07-02 17:12:48 -0700833 * @return this object
834 */
Steve Howard64c48b82010-10-07 17:53:52 -0700835 public Query setFilterById(long... ids) {
836 mIds = ids;
Steve Howarda2709362010-07-02 17:12:48 -0700837 return this;
838 }
839
840 /**
Ben Lince763d82016-05-02 16:45:45 -0700841 *
842 * Include only the downloads that contains the given string in its name.
843 * @return this object
844 * @hide
845 */
846 public Query setFilterByString(@Nullable String filter) {
847 mFilterString = filter;
848 return this;
849 }
850
851 /**
Steve Howarda2709362010-07-02 17:12:48 -0700852 * Include only downloads with status matching any the given status flags.
853 * @param flags any combination of the STATUS_* bit flags
854 * @return this object
855 */
856 public Query setFilterByStatus(int flags) {
857 mStatusFlags = flags;
858 return this;
859 }
860
861 /**
Steve Howard90fb15a2010-09-09 16:13:41 -0700862 * Controls whether this query includes downloads not visible in the system's Downloads UI.
863 * @param value if true, this query will only include downloads that should be displayed in
864 * the system's Downloads UI; if false (the default), this query will include
865 * both visible and invisible downloads.
866 * @return this object
867 * @hide
868 */
Mathew Inwood61e8ae62018-08-14 14:17:44 +0100869 @UnsupportedAppUsage
Steve Howard90fb15a2010-09-09 16:13:41 -0700870 public Query setOnlyIncludeVisibleInDownloadsUi(boolean value) {
871 mOnlyIncludeVisibleInDownloadsUi = value;
872 return this;
873 }
874
875 /**
Steve Howardf054e192010-09-01 18:26:26 -0700876 * Change the sort order of the returned Cursor.
877 *
878 * @param column one of the COLUMN_* constants; currently, only
879 * {@link #COLUMN_LAST_MODIFIED_TIMESTAMP} and {@link #COLUMN_TOTAL_SIZE_BYTES} are
880 * supported.
881 * @param direction either {@link #ORDER_ASCENDING} or {@link #ORDER_DESCENDING}
882 * @return this object
883 * @hide
884 */
Mathew Inwood8c854f82018-09-14 12:35:36 +0100885 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
Steve Howardf054e192010-09-01 18:26:26 -0700886 public Query orderBy(String column, int direction) {
887 if (direction != ORDER_ASCENDING && direction != ORDER_DESCENDING) {
888 throw new IllegalArgumentException("Invalid direction: " + direction);
889 }
890
891 if (column.equals(COLUMN_LAST_MODIFIED_TIMESTAMP)) {
Vasu Norief7e33b2010-10-20 13:26:02 -0700892 mOrderByColumn = Downloads.Impl.COLUMN_LAST_MODIFICATION;
Steve Howardf054e192010-09-01 18:26:26 -0700893 } else if (column.equals(COLUMN_TOTAL_SIZE_BYTES)) {
Vasu Norief7e33b2010-10-20 13:26:02 -0700894 mOrderByColumn = Downloads.Impl.COLUMN_TOTAL_BYTES;
Steve Howardf054e192010-09-01 18:26:26 -0700895 } else {
896 throw new IllegalArgumentException("Cannot order by " + column);
897 }
898 mOrderDirection = direction;
899 return this;
900 }
901
902 /**
Steve Howarda2709362010-07-02 17:12:48 -0700903 * Run this query using the given ContentResolver.
904 * @param projection the projection to pass to ContentResolver.query()
905 * @return the Cursor returned by ContentResolver.query()
906 */
Steve Howardeca77fc2010-09-12 18:49:08 -0700907 Cursor runQuery(ContentResolver resolver, String[] projection, Uri baseUri) {
908 Uri uri = baseUri;
Steve Howard90fb15a2010-09-09 16:13:41 -0700909 List<String> selectionParts = new ArrayList<String>();
Steve Howard64c48b82010-10-07 17:53:52 -0700910 String[] selectionArgs = null;
Steve Howarda2709362010-07-02 17:12:48 -0700911
Ben Lince763d82016-05-02 16:45:45 -0700912 int whereArgsCount = (mIds == null) ? 0 : mIds.length;
913 whereArgsCount = (mFilterString == null) ? whereArgsCount : whereArgsCount + 1;
914 selectionArgs = new String[whereArgsCount];
915
916 if (whereArgsCount > 0) {
917 if (mIds != null) {
918 selectionParts.add(getWhereClauseForIds(mIds));
919 getWhereArgsForIds(mIds, selectionArgs);
920 }
921
922 if (mFilterString != null) {
923 selectionParts.add(Downloads.Impl.COLUMN_TITLE + " LIKE ?");
924 selectionArgs[selectionArgs.length - 1] = "%" + mFilterString + "%";
925 }
Steve Howarda2709362010-07-02 17:12:48 -0700926 }
927
928 if (mStatusFlags != null) {
929 List<String> parts = new ArrayList<String>();
930 if ((mStatusFlags & STATUS_PENDING) != 0) {
Vasu Norief7e33b2010-10-20 13:26:02 -0700931 parts.add(statusClause("=", Downloads.Impl.STATUS_PENDING));
Steve Howarda2709362010-07-02 17:12:48 -0700932 }
933 if ((mStatusFlags & STATUS_RUNNING) != 0) {
Vasu Norief7e33b2010-10-20 13:26:02 -0700934 parts.add(statusClause("=", Downloads.Impl.STATUS_RUNNING));
Steve Howarda2709362010-07-02 17:12:48 -0700935 }
936 if ((mStatusFlags & STATUS_PAUSED) != 0) {
Steve Howard3e8c1d32010-09-29 17:03:32 -0700937 parts.add(statusClause("=", Downloads.Impl.STATUS_PAUSED_BY_APP));
938 parts.add(statusClause("=", Downloads.Impl.STATUS_WAITING_TO_RETRY));
939 parts.add(statusClause("=", Downloads.Impl.STATUS_WAITING_FOR_NETWORK));
940 parts.add(statusClause("=", Downloads.Impl.STATUS_QUEUED_FOR_WIFI));
Steve Howarda2709362010-07-02 17:12:48 -0700941 }
942 if ((mStatusFlags & STATUS_SUCCESSFUL) != 0) {
Vasu Norief7e33b2010-10-20 13:26:02 -0700943 parts.add(statusClause("=", Downloads.Impl.STATUS_SUCCESS));
Steve Howarda2709362010-07-02 17:12:48 -0700944 }
945 if ((mStatusFlags & STATUS_FAILED) != 0) {
946 parts.add("(" + statusClause(">=", 400)
947 + " AND " + statusClause("<", 600) + ")");
948 }
Steve Howard90fb15a2010-09-09 16:13:41 -0700949 selectionParts.add(joinStrings(" OR ", parts));
Steve Howarda2709362010-07-02 17:12:48 -0700950 }
Steve Howardf054e192010-09-01 18:26:26 -0700951
Steve Howard90fb15a2010-09-09 16:13:41 -0700952 if (mOnlyIncludeVisibleInDownloadsUi) {
953 selectionParts.add(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI + " != '0'");
954 }
955
Vasu Nori216fa222010-10-12 23:08:13 -0700956 // only return rows which are not marked 'deleted = 1'
957 selectionParts.add(Downloads.Impl.COLUMN_DELETED + " != '1'");
958
Steve Howard90fb15a2010-09-09 16:13:41 -0700959 String selection = joinStrings(" AND ", selectionParts);
Steve Howardf054e192010-09-01 18:26:26 -0700960 String orderDirection = (mOrderDirection == ORDER_ASCENDING ? "ASC" : "DESC");
961 String orderBy = mOrderByColumn + " " + orderDirection;
962
Steve Howard64c48b82010-10-07 17:53:52 -0700963 return resolver.query(uri, projection, selection, selectionArgs, orderBy);
Steve Howarda2709362010-07-02 17:12:48 -0700964 }
965
966 private String joinStrings(String joiner, Iterable<String> parts) {
967 StringBuilder builder = new StringBuilder();
968 boolean first = true;
969 for (String part : parts) {
970 if (!first) {
971 builder.append(joiner);
972 }
973 builder.append(part);
974 first = false;
975 }
976 return builder.toString();
977 }
978
979 private String statusClause(String operator, int value) {
Vasu Norief7e33b2010-10-20 13:26:02 -0700980 return Downloads.Impl.COLUMN_STATUS + operator + "'" + value + "'";
Steve Howarda2709362010-07-02 17:12:48 -0700981 }
982 }
983
Jeff Sharkey60cfad82016-01-05 17:30:57 -0700984 private final ContentResolver mResolver;
985 private final String mPackageName;
Jeff Sharkey60cfad82016-01-05 17:30:57 -0700986
Steve Howardeca77fc2010-09-12 18:49:08 -0700987 private Uri mBaseUri = Downloads.Impl.CONTENT_URI;
Jeff Sharkeyaec99bf2016-01-07 09:58:40 -0700988 private boolean mAccessFilename;
Steve Howarda2709362010-07-02 17:12:48 -0700989
990 /**
991 * @hide
992 */
Jeff Sharkey60cfad82016-01-05 17:30:57 -0700993 public DownloadManager(Context context) {
994 mResolver = context.getContentResolver();
995 mPackageName = context.getPackageName();
Jeff Sharkeyaec99bf2016-01-07 09:58:40 -0700996
997 // Callers can access filename columns when targeting old platform
998 // versions; otherwise we throw telling them it's deprecated.
999 mAccessFilename = context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N;
Steve Howarda2709362010-07-02 17:12:48 -07001000 }
1001
1002 /**
Steve Howardeca77fc2010-09-12 18:49:08 -07001003 * Makes this object access the download provider through /all_downloads URIs rather than
1004 * /my_downloads URIs, for clients that have permission to do so.
1005 * @hide
1006 */
Mathew Inwood61e8ae62018-08-14 14:17:44 +01001007 @UnsupportedAppUsage
Steve Howardeca77fc2010-09-12 18:49:08 -07001008 public void setAccessAllDownloads(boolean accessAllDownloads) {
1009 if (accessAllDownloads) {
1010 mBaseUri = Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI;
1011 } else {
1012 mBaseUri = Downloads.Impl.CONTENT_URI;
1013 }
1014 }
1015
Jeff Sharkeyaec99bf2016-01-07 09:58:40 -07001016 /** {@hide} */
Mathew Inwood8c854f82018-09-14 12:35:36 +01001017 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
Jeff Sharkeyaec99bf2016-01-07 09:58:40 -07001018 public void setAccessFilename(boolean accessFilename) {
1019 mAccessFilename = accessFilename;
1020 }
1021
Steve Howardeca77fc2010-09-12 18:49:08 -07001022 /**
Steve Howarda2709362010-07-02 17:12:48 -07001023 * Enqueue a new download. The download will start automatically once the download manager is
1024 * ready to execute it and connectivity is available.
1025 *
1026 * @param request the parameters specifying this download
1027 * @return an ID for the download, unique across the system. This ID is used to make future
1028 * calls related to this download.
1029 */
1030 public long enqueue(Request request) {
Steve Howardb8e07a52010-07-21 14:53:21 -07001031 ContentValues values = request.toContentValues(mPackageName);
Vasu Norief7e33b2010-10-20 13:26:02 -07001032 Uri downloadUri = mResolver.insert(Downloads.Impl.CONTENT_URI, values);
Steve Howarda2709362010-07-02 17:12:48 -07001033 long id = Long.parseLong(downloadUri.getLastPathSegment());
1034 return id;
1035 }
1036
1037 /**
Vasu Nori216fa222010-10-12 23:08:13 -07001038 * Marks the specified download as 'to be deleted'. This is done when a completed download
1039 * is to be removed but the row was stored without enough info to delete the corresponding
1040 * metadata from Mediaprovider database. Actual cleanup of this row is done in DownloadService.
1041 *
1042 * @param ids the IDs of the downloads to be marked 'deleted'
1043 * @return the number of downloads actually updated
1044 * @hide
1045 */
1046 public int markRowDeleted(long... ids) {
1047 if (ids == null || ids.length == 0) {
1048 // called with nothing to remove!
1049 throw new IllegalArgumentException("input param 'ids' can't be null");
1050 }
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001051 return mResolver.delete(mBaseUri, getWhereClauseForIds(ids), getWhereArgsForIds(ids));
Vasu Nori216fa222010-10-12 23:08:13 -07001052 }
1053
1054 /**
Steve Howard64c48b82010-10-07 17:53:52 -07001055 * Cancel downloads and remove them from the download manager. Each download will be stopped if
Vasu Nori17ee56c2011-02-28 08:35:39 -08001056 * it was running, and it will no longer be accessible through the download manager.
1057 * If there is a downloaded file, partial or complete, it is deleted.
Steve Howarda2709362010-07-02 17:12:48 -07001058 *
Steve Howard64c48b82010-10-07 17:53:52 -07001059 * @param ids the IDs of the downloads to remove
1060 * @return the number of downloads actually removed
Steve Howarda2709362010-07-02 17:12:48 -07001061 */
Steve Howard64c48b82010-10-07 17:53:52 -07001062 public int remove(long... ids) {
Vasu Norie16c43b2010-11-06 18:48:08 -07001063 return markRowDeleted(ids);
Steve Howarda2709362010-07-02 17:12:48 -07001064 }
1065
1066 /**
1067 * Query the download manager about downloads that have been requested.
1068 * @param query parameters specifying filters for this query
1069 * @return a Cursor over the result set of downloads, with columns consisting of all the
1070 * COLUMN_* constants.
1071 */
1072 public Cursor query(Query query) {
Sudheer Shankae93db512019-01-11 16:05:28 -08001073 return query(query, UNDERLYING_COLUMNS);
1074 }
1075
1076 /** @hide */
1077 public Cursor query(Query query, String[] projection) {
1078 Cursor underlyingCursor = query.runQuery(mResolver, projection, mBaseUri);
Steve Howardf054e192010-09-01 18:26:26 -07001079 if (underlyingCursor == null) {
1080 return null;
1081 }
Jeff Sharkeyaec99bf2016-01-07 09:58:40 -07001082 return new CursorTranslator(underlyingCursor, mBaseUri, mAccessFilename);
Steve Howarda2709362010-07-02 17:12:48 -07001083 }
1084
1085 /**
1086 * Open a downloaded file for reading. The download must have completed.
1087 * @param id the ID of the download
1088 * @return a read-only {@link ParcelFileDescriptor}
1089 * @throws FileNotFoundException if the destination file does not already exist
1090 */
1091 public ParcelFileDescriptor openDownloadedFile(long id) throws FileNotFoundException {
1092 return mResolver.openFileDescriptor(getDownloadUri(id), "r");
1093 }
1094
1095 /**
John Spurlock92a262c2013-11-04 16:25:38 -05001096 * Returns the {@link Uri} of the given downloaded file id, if the file is
1097 * downloaded successfully. Otherwise, null is returned.
Vasu Nori5be894e2010-11-02 21:55:30 -07001098 *
1099 * @param id the id of the downloaded file.
Jeff Sharkeyb11683b2015-07-29 10:15:34 -07001100 * @return the {@link Uri} of the given downloaded file id, if download was
1101 * successful. null otherwise.
Vasu Nori5be894e2010-11-02 21:55:30 -07001102 */
1103 public Uri getUriForDownloadedFile(long id) {
1104 // to check if the file is in cache, get its destination from the database
1105 Query query = new Query().setFilterById(id);
1106 Cursor cursor = null;
1107 try {
1108 cursor = query(query);
1109 if (cursor == null) {
1110 return null;
1111 }
Leon Scrogginsd75e64a2010-12-13 15:48:40 -05001112 if (cursor.moveToFirst()) {
Vasu Nori6e2b2a62010-11-16 17:58:22 -08001113 int status = cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_STATUS));
Vasu Nori5be894e2010-11-02 21:55:30 -07001114 if (DownloadManager.STATUS_SUCCESSFUL == status) {
Jeff Sharkeydf42d732016-09-16 16:57:34 -06001115 return ContentUris.withAppendedId(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, id);
Vasu Nori5be894e2010-11-02 21:55:30 -07001116 }
1117 }
1118 } finally {
1119 if (cursor != null) {
1120 cursor.close();
1121 }
1122 }
1123 // downloaded file not found or its status is not 'successfully completed'
1124 return null;
1125 }
1126
1127 /**
John Spurlock92a262c2013-11-04 16:25:38 -05001128 * Returns the media type of the given downloaded file id, if the file was
1129 * downloaded successfully. Otherwise, null is returned.
Vasu Nori6e2b2a62010-11-16 17:58:22 -08001130 *
1131 * @param id the id of the downloaded file.
John Spurlock92a262c2013-11-04 16:25:38 -05001132 * @return the media type of the given downloaded file id, if download was successful. null
Vasu Nori6e2b2a62010-11-16 17:58:22 -08001133 * otherwise.
1134 */
1135 public String getMimeTypeForDownloadedFile(long id) {
1136 Query query = new Query().setFilterById(id);
1137 Cursor cursor = null;
1138 try {
1139 cursor = query(query);
1140 if (cursor == null) {
1141 return null;
1142 }
1143 while (cursor.moveToFirst()) {
1144 return cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_MEDIA_TYPE));
1145 }
1146 } finally {
1147 if (cursor != null) {
1148 cursor.close();
1149 }
1150 }
1151 // downloaded file not found or its status is not 'successfully completed'
1152 return null;
1153 }
1154
1155 /**
Steve Howard64c48b82010-10-07 17:53:52 -07001156 * Restart the given downloads, which must have already completed (successfully or not). This
Steve Howard90fb15a2010-09-09 16:13:41 -07001157 * method will only work when called from within the download manager's process.
Steve Howard64c48b82010-10-07 17:53:52 -07001158 * @param ids the IDs of the downloads
Steve Howard90fb15a2010-09-09 16:13:41 -07001159 * @hide
1160 */
Steve Howard64c48b82010-10-07 17:53:52 -07001161 public void restartDownload(long... ids) {
1162 Cursor cursor = query(new Query().setFilterById(ids));
Steve Howard90fb15a2010-09-09 16:13:41 -07001163 try {
Steve Howard64c48b82010-10-07 17:53:52 -07001164 for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
1165 int status = cursor.getInt(cursor.getColumnIndex(COLUMN_STATUS));
1166 if (status != STATUS_SUCCESSFUL && status != STATUS_FAILED) {
1167 throw new IllegalArgumentException("Cannot restart incomplete download: "
1168 + cursor.getLong(cursor.getColumnIndex(COLUMN_ID)));
1169 }
Steve Howard90fb15a2010-09-09 16:13:41 -07001170 }
1171 } finally {
1172 cursor.close();
1173 }
1174
1175 ContentValues values = new ContentValues();
1176 values.put(Downloads.Impl.COLUMN_CURRENT_BYTES, 0);
1177 values.put(Downloads.Impl.COLUMN_TOTAL_BYTES, -1);
1178 values.putNull(Downloads.Impl._DATA);
1179 values.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_PENDING);
Jeff Sharkey54781202013-01-17 17:27:33 -08001180 values.put(Downloads.Impl.COLUMN_FAILED_CONNECTIONS, 0);
Steve Howard64c48b82010-10-07 17:53:52 -07001181 mResolver.update(mBaseUri, values, getWhereClauseForIds(ids), getWhereArgsForIds(ids));
Steve Howard90fb15a2010-09-09 16:13:41 -07001182 }
1183
1184 /**
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001185 * Force the given downloads to proceed even if their size is larger than
1186 * {@link #getMaxBytesOverMobile(Context)}.
1187 *
1188 * @hide
1189 */
1190 public void forceDownload(long... ids) {
1191 ContentValues values = new ContentValues();
1192 values.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_PENDING);
1193 values.put(Downloads.Impl.COLUMN_CONTROL, Downloads.Impl.CONTROL_RUN);
1194 values.put(Downloads.Impl.COLUMN_BYPASS_RECOMMENDED_SIZE_LIMIT, 1);
1195 mResolver.update(mBaseUri, values, getWhereClauseForIds(ids), getWhereArgsForIds(ids));
1196 }
1197
1198 /**
Vasu Nori0abbf802011-01-17 15:08:14 -08001199 * Returns maximum size, in bytes, of downloads that may go over a mobile connection; or null if
1200 * there's no limit
1201 *
1202 * @param context the {@link Context} to use for accessing the {@link ContentResolver}
1203 * @return maximum size, in bytes, of downloads that may go over a mobile connection; or null if
1204 * there's no limit
1205 */
1206 public static Long getMaxBytesOverMobile(Context context) {
1207 try {
Jeff Brownbf6f6f92012-09-25 15:03:20 -07001208 return Settings.Global.getLong(context.getContentResolver(),
1209 Settings.Global.DOWNLOAD_MAX_BYTES_OVER_MOBILE);
Vasu Nori0abbf802011-01-17 15:08:14 -08001210 } catch (SettingNotFoundException exc) {
1211 return null;
1212 }
1213 }
1214
1215 /**
Ben Lin726bf6a2016-04-27 11:38:05 -07001216 * Rename the given download if the download has completed
1217 *
1218 * @param context the {@link Context} to use in case need to update MediaProvider
1219 * @param id the downloaded id
1220 * @param displayName the new name to rename to
1221 * @return true if rename was successful, false otherwise
1222 * @hide
1223 */
1224 public boolean rename(Context context, long id, String displayName) {
1225 if (!FileUtils.isValidFatFilename(displayName)) {
1226 throw new SecurityException(displayName + " is not a valid filename");
1227 }
1228
1229 Query query = new Query().setFilterById(id);
1230 Cursor cursor = null;
1231 String oldDisplayName = null;
1232 String mimeType = null;
1233 try {
1234 cursor = query(query);
1235 if (cursor == null) {
1236 return false;
1237 }
1238 if (cursor.moveToFirst()) {
1239 int status = cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_STATUS));
1240 if (DownloadManager.STATUS_SUCCESSFUL != status) {
1241 return false;
1242 }
1243 oldDisplayName = cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_TITLE));
1244 mimeType = cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_MEDIA_TYPE));
1245 }
1246 } finally {
1247 if (cursor != null) {
1248 cursor.close();
1249 }
1250 }
1251
1252 if (oldDisplayName == null || mimeType == null) {
1253 throw new IllegalStateException(
1254 "Document with id " + id + " does not exist");
1255 }
1256
1257 final File parent = Environment.getExternalStoragePublicDirectory(
1258 Environment.DIRECTORY_DOWNLOADS);
1259
1260 final File before = new File(parent, oldDisplayName);
1261 final File after = new File(parent, displayName);
1262
1263 if (after.exists()) {
1264 throw new IllegalStateException("Already exists " + after);
1265 }
1266 if (!before.renameTo(after)) {
1267 throw new IllegalStateException("Failed to rename to " + after);
1268 }
1269
Ben Lin726bf6a2016-04-27 11:38:05 -07001270 ContentValues values = new ContentValues();
1271 values.put(Downloads.Impl.COLUMN_TITLE, displayName);
1272 values.put(Downloads.Impl._DATA, after.toString());
1273 values.putNull(Downloads.Impl.COLUMN_MEDIAPROVIDER_URI);
1274 long[] ids = {id};
1275
1276 return (mResolver.update(mBaseUri, values, getWhereClauseForIds(ids),
1277 getWhereArgsForIds(ids)) == 1);
1278 }
1279
1280 /**
Vasu Nori0abbf802011-01-17 15:08:14 -08001281 * Returns recommended maximum size, in bytes, of downloads that may go over a mobile
1282 * connection; or null if there's no recommended limit. The user will have the option to bypass
1283 * this limit.
1284 *
1285 * @param context the {@link Context} to use for accessing the {@link ContentResolver}
1286 * @return recommended maximum size, in bytes, of downloads that may go over a mobile
1287 * connection; or null if there's no recommended limit.
1288 */
1289 public static Long getRecommendedMaxBytesOverMobile(Context context) {
1290 try {
Jeff Brownbf6f6f92012-09-25 15:03:20 -07001291 return Settings.Global.getLong(context.getContentResolver(),
1292 Settings.Global.DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE);
Vasu Nori0abbf802011-01-17 15:08:14 -08001293 } catch (SettingNotFoundException exc) {
1294 return null;
1295 }
1296 }
Vasu Noric0e50752011-01-20 17:57:54 -08001297
Jeff Sharkey8fc27e82012-04-04 20:40:58 -07001298 /** {@hide} */
1299 public static boolean isActiveNetworkExpensive(Context context) {
1300 // TODO: connect to NetworkPolicyManager
1301 return false;
1302 }
1303
1304 /** {@hide} */
1305 public static long getActiveNetworkWarningBytes(Context context) {
1306 // TODO: connect to NetworkPolicyManager
1307 return -1;
1308 }
1309
Vasu Noric0e50752011-01-20 17:57:54 -08001310 /**
1311 * Adds a file to the downloads database system, so it could appear in Downloads App
1312 * (and thus become eligible for management by the Downloads App).
1313 * <p>
1314 * It is helpful to make the file scannable by MediaScanner by setting the param
1315 * isMediaScannerScannable to true. It makes the file visible in media managing
1316 * applications such as Gallery App, which could be a useful purpose of using this API.
1317 *
1318 * @param title the title that would appear for this file in Downloads App.
1319 * @param description the description that would appear for this file in Downloads App.
1320 * @param isMediaScannerScannable true if the file is to be scanned by MediaScanner. Files
1321 * scanned by MediaScanner appear in the applications used to view media (for example,
Sudheer Shanka684c02a2019-01-29 17:41:15 -08001322 * Gallery app).
Vasu Noric0e50752011-01-20 17:57:54 -08001323 * @param mimeType mimetype of the file.
1324 * @param path absolute pathname to the file. The file should be world-readable, so that it can
1325 * be managed by the Downloads App and any other app that is used to read it (for example,
1326 * Gallery app to display the file, if the file contents represent a video/image).
1327 * @param length length of the downloaded file
Vasu Norif9e85232011-02-10 14:59:54 -08001328 * @param showNotification true if a notification is to be sent, false otherwise
Vasu Noric0e50752011-01-20 17:57:54 -08001329 * @return an ID for the download entry added to the downloads app, unique across the system
1330 * This ID is used to make future calls related to this download.
1331 */
Vasu Nori37281302011-03-07 11:25:01 -08001332 public long addCompletedDownload(String title, String description,
Vasu Norif9e85232011-02-10 14:59:54 -08001333 boolean isMediaScannerScannable, String mimeType, String path, long length,
1334 boolean showNotification) {
Jeff Sharkeyb180a652013-09-23 14:23:41 -07001335 return addCompletedDownload(title, description, isMediaScannerScannable, mimeType, path,
Edward Cunninghamabb2c5a2016-03-25 20:56:31 +00001336 length, showNotification, false, null, null);
1337 }
1338
1339 /**
1340 * Adds a file to the downloads database system, so it could appear in Downloads App
1341 * (and thus become eligible for management by the Downloads App).
1342 * <p>
1343 * It is helpful to make the file scannable by MediaScanner by setting the param
1344 * isMediaScannerScannable to true. It makes the file visible in media managing
1345 * applications such as Gallery App, which could be a useful purpose of using this API.
1346 *
1347 * @param title the title that would appear for this file in Downloads App.
1348 * @param description the description that would appear for this file in Downloads App.
1349 * @param isMediaScannerScannable true if the file is to be scanned by MediaScanner. Files
1350 * scanned by MediaScanner appear in the applications used to view media (for example,
Sudheer Shanka684c02a2019-01-29 17:41:15 -08001351 * Gallery app).
Edward Cunninghamabb2c5a2016-03-25 20:56:31 +00001352 * @param mimeType mimetype of the file.
1353 * @param path absolute pathname to the file. The file should be world-readable, so that it can
1354 * be managed by the Downloads App and any other app that is used to read it (for example,
1355 * Gallery app to display the file, if the file contents represent a video/image).
1356 * @param length length of the downloaded file
1357 * @param showNotification true if a notification is to be sent, false otherwise
1358 * @param uri the original HTTP URI of the download
1359 * @param referer the HTTP Referer for the download
1360 * @return an ID for the download entry added to the downloads app, unique across the system
1361 * This ID is used to make future calls related to this download.
1362 */
1363 public long addCompletedDownload(String title, String description,
1364 boolean isMediaScannerScannable, String mimeType, String path, long length,
1365 boolean showNotification, Uri uri, Uri referer) {
1366 return addCompletedDownload(title, description, isMediaScannerScannable, mimeType, path,
1367 length, showNotification, false, uri, referer);
Jeff Sharkeyb180a652013-09-23 14:23:41 -07001368 }
1369
1370 /** {@hide} */
1371 public long addCompletedDownload(String title, String description,
1372 boolean isMediaScannerScannable, String mimeType, String path, long length,
1373 boolean showNotification, boolean allowWrite) {
Edward Cunninghamabb2c5a2016-03-25 20:56:31 +00001374 return addCompletedDownload(title, description, isMediaScannerScannable, mimeType, path,
1375 length, showNotification, allowWrite, null, null);
1376 }
1377
1378 /** {@hide} */
1379 public long addCompletedDownload(String title, String description,
1380 boolean isMediaScannerScannable, String mimeType, String path, long length,
1381 boolean showNotification, boolean allowWrite, Uri uri, Uri referer) {
Vasu Noric0e50752011-01-20 17:57:54 -08001382 // make sure the input args are non-null/non-zero
1383 validateArgumentIsNonEmpty("title", title);
1384 validateArgumentIsNonEmpty("description", description);
1385 validateArgumentIsNonEmpty("path", path);
1386 validateArgumentIsNonEmpty("mimeType", mimeType);
Jeff Sharkey33f95ed2012-05-02 17:07:54 -07001387 if (length < 0) {
Vasu Noric0e50752011-01-20 17:57:54 -08001388 throw new IllegalArgumentException(" invalid value for param: totalBytes");
1389 }
1390
1391 // if there is already an entry with the given path name in downloads.db, return its id
Edward Cunninghamabb2c5a2016-03-25 20:56:31 +00001392 Request request;
1393 if (uri != null) {
1394 request = new Request(uri);
1395 } else {
1396 request = new Request(NON_DOWNLOADMANAGER_DOWNLOAD);
1397 }
1398 request.setTitle(title)
Vasu Noric0e50752011-01-20 17:57:54 -08001399 .setDescription(description)
1400 .setMimeType(mimeType);
Edward Cunninghamabb2c5a2016-03-25 20:56:31 +00001401 if (referer != null) {
1402 request.addRequestHeader("Referer", referer.toString());
1403 }
Vasu Noric0e50752011-01-20 17:57:54 -08001404 ContentValues values = request.toContentValues(null);
1405 values.put(Downloads.Impl.COLUMN_DESTINATION,
1406 Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD);
1407 values.put(Downloads.Impl._DATA, path);
1408 values.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_SUCCESS);
1409 values.put(Downloads.Impl.COLUMN_TOTAL_BYTES, length);
1410 values.put(Downloads.Impl.COLUMN_MEDIA_SCANNED,
1411 (isMediaScannerScannable) ? Request.SCANNABLE_VALUE_YES :
1412 Request.SCANNABLE_VALUE_NO);
Vasu Norif9e85232011-02-10 14:59:54 -08001413 values.put(Downloads.Impl.COLUMN_VISIBILITY, (showNotification) ?
1414 Request.VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION : Request.VISIBILITY_HIDDEN);
Jeff Sharkeyb180a652013-09-23 14:23:41 -07001415 values.put(Downloads.Impl.COLUMN_ALLOW_WRITE, allowWrite ? 1 : 0);
Vasu Noric0e50752011-01-20 17:57:54 -08001416 Uri downloadUri = mResolver.insert(Downloads.Impl.CONTENT_URI, values);
1417 if (downloadUri == null) {
1418 return -1;
1419 }
1420 return Long.parseLong(downloadUri.getLastPathSegment());
1421 }
Jeff Sharkeyb180a652013-09-23 14:23:41 -07001422
Vasu Noric0e50752011-01-20 17:57:54 -08001423 private static final String NON_DOWNLOADMANAGER_DOWNLOAD =
1424 "non-dwnldmngr-download-dont-retry2download";
1425
1426 private static void validateArgumentIsNonEmpty(String paramName, String val) {
1427 if (TextUtils.isEmpty(val)) {
1428 throw new IllegalArgumentException(paramName + " can't be null");
1429 }
1430 }
1431
Vasu Nori0abbf802011-01-17 15:08:14 -08001432 /**
Steve Howarda2709362010-07-02 17:12:48 -07001433 * Get the DownloadProvider URI for the download with the given ID.
Jeff Sharkeyb180a652013-09-23 14:23:41 -07001434 *
1435 * @hide
Steve Howarda2709362010-07-02 17:12:48 -07001436 */
Jeff Sharkeyb180a652013-09-23 14:23:41 -07001437 public Uri getDownloadUri(long id) {
Jeff Sharkey15471942016-09-16 12:04:05 -06001438 return ContentUris.withAppendedId(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, id);
Steve Howarda2709362010-07-02 17:12:48 -07001439 }
1440
1441 /**
Steve Howard64c48b82010-10-07 17:53:52 -07001442 * Get a parameterized SQL WHERE clause to select a bunch of IDs.
1443 */
Mathew Inwood61e8ae62018-08-14 14:17:44 +01001444 @UnsupportedAppUsage
Steve Howard64c48b82010-10-07 17:53:52 -07001445 static String getWhereClauseForIds(long[] ids) {
1446 StringBuilder whereClause = new StringBuilder();
Vasu Norie7be6bd2010-10-10 14:58:08 -07001447 whereClause.append("(");
Steve Howard64c48b82010-10-07 17:53:52 -07001448 for (int i = 0; i < ids.length; i++) {
1449 if (i > 0) {
Vasu Norie7be6bd2010-10-10 14:58:08 -07001450 whereClause.append("OR ");
Steve Howard64c48b82010-10-07 17:53:52 -07001451 }
Vasu Norie7be6bd2010-10-10 14:58:08 -07001452 whereClause.append(Downloads.Impl._ID);
1453 whereClause.append(" = ? ");
Steve Howard64c48b82010-10-07 17:53:52 -07001454 }
1455 whereClause.append(")");
1456 return whereClause.toString();
1457 }
1458
1459 /**
1460 * Get the selection args for a clause returned by {@link #getWhereClauseForIds(long[])}.
1461 */
Mathew Inwood61e8ae62018-08-14 14:17:44 +01001462 @UnsupportedAppUsage
Steve Howard64c48b82010-10-07 17:53:52 -07001463 static String[] getWhereArgsForIds(long[] ids) {
1464 String[] whereArgs = new String[ids.length];
Ben Lince763d82016-05-02 16:45:45 -07001465 return getWhereArgsForIds(ids, whereArgs);
Steve Howard64c48b82010-10-07 17:53:52 -07001466 }
1467
1468 /**
Ben Lince763d82016-05-02 16:45:45 -07001469 * Get selection args for a clause returned by {@link #getWhereClauseForIds(long[])}
1470 * and write it to the supplied args array.
1471 */
1472 static String[] getWhereArgsForIds(long[] ids, String[] args) {
1473 assert(args.length >= ids.length);
1474 for (int i = 0; i < ids.length; i++) {
1475 args[i] = Long.toString(ids[i]);
1476 }
1477 return args;
1478 }
1479
1480
1481 /**
Steve Howarda2709362010-07-02 17:12:48 -07001482 * This class wraps a cursor returned by DownloadProvider -- the "underlying cursor" -- and
1483 * presents a different set of columns, those defined in the DownloadManager.COLUMN_* constants.
1484 * Some columns correspond directly to underlying values while others are computed from
1485 * underlying data.
1486 */
1487 private static class CursorTranslator extends CursorWrapper {
Jeff Sharkey60cfad82016-01-05 17:30:57 -07001488 private final Uri mBaseUri;
Jeff Sharkeyaec99bf2016-01-07 09:58:40 -07001489 private final boolean mAccessFilename;
Steve Howardeca77fc2010-09-12 18:49:08 -07001490
Jeff Sharkeyaec99bf2016-01-07 09:58:40 -07001491 public CursorTranslator(Cursor cursor, Uri baseUri, boolean accessFilename) {
Steve Howarda2709362010-07-02 17:12:48 -07001492 super(cursor);
Steve Howardeca77fc2010-09-12 18:49:08 -07001493 mBaseUri = baseUri;
Jeff Sharkeyaec99bf2016-01-07 09:58:40 -07001494 mAccessFilename = accessFilename;
Steve Howarda2709362010-07-02 17:12:48 -07001495 }
1496
1497 @Override
Steve Howarda2709362010-07-02 17:12:48 -07001498 public int getInt(int columnIndex) {
1499 return (int) getLong(columnIndex);
1500 }
1501
1502 @Override
1503 public long getLong(int columnIndex) {
Vasu Norie16c43b2010-11-06 18:48:08 -07001504 if (getColumnName(columnIndex).equals(COLUMN_REASON)) {
1505 return getReason(super.getInt(getColumnIndex(Downloads.Impl.COLUMN_STATUS)));
1506 } else if (getColumnName(columnIndex).equals(COLUMN_STATUS)) {
1507 return translateStatus(super.getInt(getColumnIndex(Downloads.Impl.COLUMN_STATUS)));
1508 } else {
1509 return super.getLong(columnIndex);
1510 }
Steve Howarda2709362010-07-02 17:12:48 -07001511 }
1512
1513 @Override
1514 public String getString(int columnIndex) {
Jeff Sharkey60cfad82016-01-05 17:30:57 -07001515 final String columnName = getColumnName(columnIndex);
1516 switch (columnName) {
1517 case COLUMN_LOCAL_URI:
1518 return getLocalUri();
1519 case COLUMN_LOCAL_FILENAME:
Jeff Sharkeyaec99bf2016-01-07 09:58:40 -07001520 if (!mAccessFilename) {
Jeff Sharkey0c2ccd02016-03-22 10:05:47 -06001521 throw new SecurityException(
Jeff Sharkey60cfad82016-01-05 17:30:57 -07001522 "COLUMN_LOCAL_FILENAME is deprecated;"
1523 + " use ContentResolver.openFileDescriptor() instead");
1524 }
1525 default:
1526 return super.getString(columnIndex);
1527 }
Steve Howardeca77fc2010-09-12 18:49:08 -07001528 }
1529
1530 private String getLocalUri() {
Vasu Norie16c43b2010-11-06 18:48:08 -07001531 long destinationType = getLong(getColumnIndex(Downloads.Impl.COLUMN_DESTINATION));
1532 if (destinationType == Downloads.Impl.DESTINATION_FILE_URI ||
Vasu Noric0e50752011-01-20 17:57:54 -08001533 destinationType == Downloads.Impl.DESTINATION_EXTERNAL ||
1534 destinationType == Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD) {
Jeff Sharkey281c1822016-03-30 19:46:42 -06001535 String localPath = super.getString(getColumnIndex(COLUMN_LOCAL_FILENAME));
Steve Howard99047d72010-09-29 17:41:37 -07001536 if (localPath == null) {
1537 return null;
1538 }
1539 return Uri.fromFile(new File(localPath)).toString();
Steve Howardbb0d23b2010-09-22 18:56:29 -07001540 }
1541
Steve Howardeca77fc2010-09-12 18:49:08 -07001542 // return content URI for cache download
Vasu Norie16c43b2010-11-06 18:48:08 -07001543 long downloadId = getLong(getColumnIndex(Downloads.Impl._ID));
Jeff Sharkey15471942016-09-16 12:04:05 -06001544 return ContentUris.withAppendedId(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, downloadId).toString();
Steve Howarda2709362010-07-02 17:12:48 -07001545 }
1546
Steve Howard3e8c1d32010-09-29 17:03:32 -07001547 private long getReason(int status) {
1548 switch (translateStatus(status)) {
1549 case STATUS_FAILED:
1550 return getErrorCode(status);
1551
1552 case STATUS_PAUSED:
1553 return getPausedReason(status);
1554
1555 default:
1556 return 0; // arbitrary value when status is not an error
Steve Howarda2709362010-07-02 17:12:48 -07001557 }
Steve Howard3e8c1d32010-09-29 17:03:32 -07001558 }
1559
1560 private long getPausedReason(int status) {
1561 switch (status) {
1562 case Downloads.Impl.STATUS_WAITING_TO_RETRY:
1563 return PAUSED_WAITING_TO_RETRY;
1564
1565 case Downloads.Impl.STATUS_WAITING_FOR_NETWORK:
1566 return PAUSED_WAITING_FOR_NETWORK;
1567
1568 case Downloads.Impl.STATUS_QUEUED_FOR_WIFI:
1569 return PAUSED_QUEUED_FOR_WIFI;
1570
1571 default:
1572 return PAUSED_UNKNOWN;
1573 }
1574 }
1575
1576 private long getErrorCode(int status) {
Steve Howard33bbd122010-08-02 17:51:29 -07001577 if ((400 <= status && status < Downloads.Impl.MIN_ARTIFICIAL_ERROR_STATUS)
1578 || (500 <= status && status < 600)) {
Steve Howarda2709362010-07-02 17:12:48 -07001579 // HTTP status code
1580 return status;
1581 }
1582
1583 switch (status) {
Vasu Norief7e33b2010-10-20 13:26:02 -07001584 case Downloads.Impl.STATUS_FILE_ERROR:
Steve Howarda2709362010-07-02 17:12:48 -07001585 return ERROR_FILE_ERROR;
1586
Vasu Norief7e33b2010-10-20 13:26:02 -07001587 case Downloads.Impl.STATUS_UNHANDLED_HTTP_CODE:
1588 case Downloads.Impl.STATUS_UNHANDLED_REDIRECT:
Steve Howarda2709362010-07-02 17:12:48 -07001589 return ERROR_UNHANDLED_HTTP_CODE;
1590
Vasu Norief7e33b2010-10-20 13:26:02 -07001591 case Downloads.Impl.STATUS_HTTP_DATA_ERROR:
Steve Howarda2709362010-07-02 17:12:48 -07001592 return ERROR_HTTP_DATA_ERROR;
1593
Vasu Norief7e33b2010-10-20 13:26:02 -07001594 case Downloads.Impl.STATUS_TOO_MANY_REDIRECTS:
Steve Howarda2709362010-07-02 17:12:48 -07001595 return ERROR_TOO_MANY_REDIRECTS;
1596
Vasu Norief7e33b2010-10-20 13:26:02 -07001597 case Downloads.Impl.STATUS_INSUFFICIENT_SPACE_ERROR:
Steve Howarda2709362010-07-02 17:12:48 -07001598 return ERROR_INSUFFICIENT_SPACE;
1599
Vasu Norief7e33b2010-10-20 13:26:02 -07001600 case Downloads.Impl.STATUS_DEVICE_NOT_FOUND_ERROR:
Steve Howarda2709362010-07-02 17:12:48 -07001601 return ERROR_DEVICE_NOT_FOUND;
1602
Steve Howard33bbd122010-08-02 17:51:29 -07001603 case Downloads.Impl.STATUS_CANNOT_RESUME:
1604 return ERROR_CANNOT_RESUME;
1605
Steve Howarda9e87c92010-09-16 12:02:03 -07001606 case Downloads.Impl.STATUS_FILE_ALREADY_EXISTS_ERROR:
1607 return ERROR_FILE_ALREADY_EXISTS;
1608
Steve Howarda2709362010-07-02 17:12:48 -07001609 default:
1610 return ERROR_UNKNOWN;
1611 }
1612 }
1613
Steve Howard3e8c1d32010-09-29 17:03:32 -07001614 private int translateStatus(int status) {
Steve Howarda2709362010-07-02 17:12:48 -07001615 switch (status) {
Vasu Norief7e33b2010-10-20 13:26:02 -07001616 case Downloads.Impl.STATUS_PENDING:
Steve Howarda2709362010-07-02 17:12:48 -07001617 return STATUS_PENDING;
1618
Vasu Norief7e33b2010-10-20 13:26:02 -07001619 case Downloads.Impl.STATUS_RUNNING:
Steve Howarda2709362010-07-02 17:12:48 -07001620 return STATUS_RUNNING;
1621
Steve Howard3e8c1d32010-09-29 17:03:32 -07001622 case Downloads.Impl.STATUS_PAUSED_BY_APP:
1623 case Downloads.Impl.STATUS_WAITING_TO_RETRY:
1624 case Downloads.Impl.STATUS_WAITING_FOR_NETWORK:
1625 case Downloads.Impl.STATUS_QUEUED_FOR_WIFI:
Steve Howarda2709362010-07-02 17:12:48 -07001626 return STATUS_PAUSED;
1627
Vasu Norief7e33b2010-10-20 13:26:02 -07001628 case Downloads.Impl.STATUS_SUCCESS:
Steve Howarda2709362010-07-02 17:12:48 -07001629 return STATUS_SUCCESSFUL;
1630
1631 default:
Vasu Norief7e33b2010-10-20 13:26:02 -07001632 assert Downloads.Impl.isStatusError(status);
Steve Howarda2709362010-07-02 17:12:48 -07001633 return STATUS_FAILED;
1634 }
1635 }
1636 }
1637}