blob: 00b6864c0393e48d355aa128671782eb799531fb [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
17package android.net;
18
19import android.content.ContentResolver;
20import android.content.ContentValues;
21import android.database.Cursor;
22import android.database.CursorWrapper;
23import android.os.ParcelFileDescriptor;
24import android.provider.Downloads;
25import android.util.Log;
26
Steve Howardadcb6972010-07-12 17:09:25 -070027import java.io.File;
Steve Howarda2709362010-07-02 17:12:48 -070028import java.io.FileNotFoundException;
29import java.util.ArrayList;
30import java.util.Arrays;
31import java.util.HashMap;
32import java.util.HashSet;
33import java.util.List;
34import java.util.Map;
35import java.util.Set;
36
37/**
38 * The download manager is a system service that handles long-running HTTP downloads. Clients may
39 * request that a URI be downloaded to a particular destination file. The download manager will
40 * conduct the download in the background, taking care of HTTP interactions and retrying downloads
41 * after failures or across connectivity changes and system reboots.
42 *
43 * Instances of this class should be obtained through
44 * {@link android.content.Context#getSystemService(String)} by passing
45 * {@link android.content.Context#DOWNLOAD_SERVICE}.
46 *
47 * @hide
48 */
49public class DownloadManager {
50 /**
51 * An identifier for a particular download, unique across the system. Clients use this ID to
52 * make subsequent calls related to the download.
53 */
54 public final static String COLUMN_ID = "id";
55
56 /**
57 * The client-supplied title for this download. This will be displayed in system notifications,
58 * if enabled.
59 */
60 public final static String COLUMN_TITLE = "title";
61
62 /**
63 * The client-supplied description of this download. This will be displayed in system
64 * notifications, if enabled.
65 */
66 public final static String COLUMN_DESCRIPTION = "description";
67
68 /**
69 * URI to be downloaded.
70 */
71 public final static String COLUMN_URI = "uri";
72
73 /**
74 * Internet Media Type of the downloaded file. This will be filled in based on the server's
75 * response once the download has started.
76 *
77 * @see <a href="http://www.ietf.org/rfc/rfc1590.txt">RFC 1590, defining Media Types</a>
78 */
79 public final static String COLUMN_MEDIA_TYPE = "media_type";
80
81 /**
82 * Total size of the download in bytes. This will be filled in once the download starts.
83 */
84 public final static String COLUMN_TOTAL_SIZE_BYTES = "total_size";
85
86 /**
87 * Uri where downloaded file will be stored. If a destination is supplied by client, that URI
88 * will be used here. Otherwise, the value will be filled in with a generated URI once the
89 * download has started.
90 */
91 public final static String COLUMN_LOCAL_URI = "local_uri";
92
93 /**
94 * Current status of the download, as one of the STATUS_* constants.
95 */
96 public final static String COLUMN_STATUS = "status";
97
98 /**
99 * Indicates the type of error that occurred, when {@link #COLUMN_STATUS} is
100 * {@link #STATUS_FAILED}. If an HTTP error occurred, this will hold the HTTP status code as
101 * defined in RFC 2616. Otherwise, it will hold one of the ERROR_* constants.
102 *
103 * If {@link #COLUMN_STATUS} is not {@link #STATUS_FAILED}, this column's value is undefined.
104 *
105 * @see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1.1">RFC 2616
106 * status codes</a>
107 */
108 public final static String COLUMN_ERROR_CODE = "error_code";
109
110 /**
111 * Number of bytes download so far.
112 */
113 public final static String COLUMN_BYTES_DOWNLOADED_SO_FAR = "bytes_so_far";
114
115 /**
Steve Howardadcb6972010-07-12 17:09:25 -0700116 * Timestamp when the download was last modified, in {@link System#currentTimeMillis
Steve Howarda2709362010-07-02 17:12:48 -0700117 * System.currentTimeMillis()} (wall clock time in UTC).
118 */
Steve Howardadcb6972010-07-12 17:09:25 -0700119 public final static String COLUMN_LAST_MODIFIED_TIMESTAMP = "last_modified_timestamp";
Steve Howarda2709362010-07-02 17:12:48 -0700120
121
122 /**
123 * Value of {@link #COLUMN_STATUS} when the download is waiting to start.
124 */
125 public final static int STATUS_PENDING = 1 << 0;
126
127 /**
128 * Value of {@link #COLUMN_STATUS} when the download is currently running.
129 */
130 public final static int STATUS_RUNNING = 1 << 1;
131
132 /**
133 * Value of {@link #COLUMN_STATUS} when the download is waiting to retry or resume.
134 */
135 public final static int STATUS_PAUSED = 1 << 2;
136
137 /**
138 * Value of {@link #COLUMN_STATUS} when the download has successfully completed.
139 */
140 public final static int STATUS_SUCCESSFUL = 1 << 3;
141
142 /**
143 * Value of {@link #COLUMN_STATUS} when the download has failed (and will not be retried).
144 */
145 public final static int STATUS_FAILED = 1 << 4;
146
147
148 /**
149 * Value of COLUMN_ERROR_CODE when the download has completed with an error that doesn't fit
150 * under any other error code.
151 */
152 public final static int ERROR_UNKNOWN = 1000;
153
154 /**
155 * Value of {@link #COLUMN_ERROR_CODE} when a storage issue arises which doesn't fit under any
156 * other error code. Use the more specific {@link #ERROR_INSUFFICIENT_SPACE} and
157 * {@link #ERROR_DEVICE_NOT_FOUND} when appropriate.
158 */
159 public final static int ERROR_FILE_ERROR = 1001;
160
161 /**
162 * Value of {@link #COLUMN_ERROR_CODE} when an HTTP code was received that download manager
163 * can't handle.
164 */
165 public final static int ERROR_UNHANDLED_HTTP_CODE = 1002;
166
167 /**
168 * Value of {@link #COLUMN_ERROR_CODE} when an error receiving or processing data occurred at
169 * the HTTP level.
170 */
171 public final static int ERROR_HTTP_DATA_ERROR = 1004;
172
173 /**
174 * Value of {@link #COLUMN_ERROR_CODE} when there were too many redirects.
175 */
176 public final static int ERROR_TOO_MANY_REDIRECTS = 1005;
177
178 /**
179 * Value of {@link #COLUMN_ERROR_CODE} when there was insufficient storage space. Typically,
180 * this is because the SD card is full.
181 */
182 public final static int ERROR_INSUFFICIENT_SPACE = 1006;
183
184 /**
185 * Value of {@link #COLUMN_ERROR_CODE} when no external storage device was found. Typically,
186 * this is because the SD card is not mounted.
187 */
188 public final static int ERROR_DEVICE_NOT_FOUND = 1007;
189
190
191 // this array must contain all public columns
192 private static final String[] COLUMNS = new String[] {
193 COLUMN_ID,
194 COLUMN_TITLE,
195 COLUMN_DESCRIPTION,
196 COLUMN_URI,
197 COLUMN_MEDIA_TYPE,
198 COLUMN_TOTAL_SIZE_BYTES,
199 COLUMN_LOCAL_URI,
200 COLUMN_STATUS,
201 COLUMN_ERROR_CODE,
202 COLUMN_BYTES_DOWNLOADED_SO_FAR,
Steve Howardadcb6972010-07-12 17:09:25 -0700203 COLUMN_LAST_MODIFIED_TIMESTAMP
Steve Howarda2709362010-07-02 17:12:48 -0700204 };
205
206 // columns to request from DownloadProvider
207 private static final String[] UNDERLYING_COLUMNS = new String[] {
208 Downloads.Impl._ID,
209 Downloads.COLUMN_TITLE,
210 Downloads.COLUMN_DESCRIPTION,
211 Downloads.COLUMN_URI,
212 Downloads.COLUMN_MIME_TYPE,
213 Downloads.COLUMN_TOTAL_BYTES,
214 Downloads._DATA,
215 Downloads.COLUMN_STATUS,
Steve Howardadcb6972010-07-12 17:09:25 -0700216 Downloads.COLUMN_CURRENT_BYTES,
217 Downloads.COLUMN_LAST_MODIFICATION,
Steve Howarda2709362010-07-02 17:12:48 -0700218 };
219
220 private static final Set<String> LONG_COLUMNS = new HashSet<String>(
221 Arrays.asList(COLUMN_ID, COLUMN_TOTAL_SIZE_BYTES, COLUMN_STATUS, COLUMN_ERROR_CODE,
Steve Howardadcb6972010-07-12 17:09:25 -0700222 COLUMN_BYTES_DOWNLOADED_SO_FAR, COLUMN_LAST_MODIFIED_TIMESTAMP));
Steve Howarda2709362010-07-02 17:12:48 -0700223
224 /**
225 * This class contains all the information necessary to request a new download. The URI is the
226 * only required parameter.
227 */
228 public static class Request {
229 /**
230 * Bit flag for setShowNotification indicated a notification should be created while the
231 * download is running.
232 */
233 private static final int NOTIFICATION_WHEN_RUNNING = 1;
234
235 Uri mUri;
236 Uri mDestinationUri;
237 Map<String, String> mRequestHeaders = new HashMap<String, String>();
238 String mTitle;
239 String mDescription;
240 int mNotificationFlags;
241
242 private String mMediaType;
243
244 /**
245 * @param uri the HTTP URI to download.
246 */
247 public Request(Uri uri) {
248 if (uri == null) {
249 throw new NullPointerException();
250 }
251 String scheme = uri.getScheme();
252 if (scheme == null || !scheme.equals("http")) {
253 throw new IllegalArgumentException("Can only download HTTP URIs: " + uri);
254 }
255 mUri = uri;
256 }
257
258 /**
259 * Set the local destination for the downloaded data. Must be a file URI to a path on
260 * external storage, and the calling application must have the WRITE_EXTERNAL_STORAGE
261 * permission.
262 *
263 * By default, downloads are saved to a generated file in the download cache and may be
264 * deleted by the download manager at any time.
265 *
266 * @return this object
267 */
268 public Request setDestinationUri(Uri uri) {
269 mDestinationUri = uri;
270 return this;
271 }
272
273 /**
274 * Set an HTTP header to be included with the download request.
275 * @param header HTTP header name
276 * @param value header value
277 * @return this object
278 */
279 public Request setRequestHeader(String header, String value) {
280 mRequestHeaders.put(header, value);
281 return this;
282 }
283
284 /**
285 * Set the title of this download, to be displayed in notifications (if enabled)
286 * @return this object
287 */
288 public Request setTitle(String title) {
289 mTitle = title;
290 return this;
291 }
292
293 /**
294 * Set a description of this download, to be displayed in notifications (if enabled)
295 * @return this object
296 */
297 public Request setDescription(String description) {
298 mDescription = description;
299 return this;
300 }
301
302 /**
303 * Set the Internet Media Type of this download. This will override the media type declared
304 * in the server's response.
305 * @see <a href="http://www.ietf.org/rfc/rfc1590.txt">RFC 1590, defining Media Types</a>
306 * @return this object
307 */
308 public Request setMediaType(String mediaType) {
309 mMediaType = mediaType;
310 return this;
311 }
312
313 /**
314 * Control system notifications posted by the download manager for this download. If
315 * enabled, the download manager posts notifications about downloads through the system
316 * {@link android.app.NotificationManager}.
317 *
318 * @param flags any combination of the NOTIFICATION_* bit flags
319 * @return this object
320 */
321 public Request setShowNotification(int flags) {
322 mNotificationFlags = flags;
323 return this;
324 }
325
326 public Request setAllowedNetworkTypes(int flags) {
327 // TODO allowed networks support
328 throw new UnsupportedOperationException();
329 }
330
331 public Request setAllowedOverRoaming(boolean allowed) {
332 // TODO roaming support
333 throw new UnsupportedOperationException();
334 }
335
336 /**
337 * @return ContentValues to be passed to DownloadProvider.insert()
338 */
339 ContentValues toContentValues() {
340 ContentValues values = new ContentValues();
341 assert mUri != null;
342 values.put(Downloads.COLUMN_URI, mUri.toString());
343
344 if (mDestinationUri != null) {
Steve Howardadcb6972010-07-12 17:09:25 -0700345 values.put(Downloads.COLUMN_DESTINATION, Downloads.Impl.DESTINATION_FILE_URI);
346 values.put(Downloads.COLUMN_FILE_NAME_HINT, mDestinationUri.toString());
Steve Howarda2709362010-07-02 17:12:48 -0700347 } else {
348 values.put(Downloads.COLUMN_DESTINATION,
349 Downloads.DESTINATION_CACHE_PARTITION_PURGEABLE);
350 }
351
352 if (!mRequestHeaders.isEmpty()) {
Steve Howardea9147d2010-07-13 19:02:45 -0700353 encodeHttpHeaders(values);
Steve Howarda2709362010-07-02 17:12:48 -0700354 }
355
356 putIfNonNull(values, Downloads.COLUMN_TITLE, mTitle);
357 putIfNonNull(values, Downloads.COLUMN_DESCRIPTION, mDescription);
358 putIfNonNull(values, Downloads.COLUMN_MIME_TYPE, mMediaType);
359
360 int visibility = Downloads.VISIBILITY_HIDDEN;
361 if ((mNotificationFlags & NOTIFICATION_WHEN_RUNNING) != 0) {
362 visibility = Downloads.VISIBILITY_VISIBLE;
363 }
364 values.put(Downloads.COLUMN_VISIBILITY, visibility);
365
366 return values;
367 }
368
Steve Howardea9147d2010-07-13 19:02:45 -0700369 private void encodeHttpHeaders(ContentValues values) {
370 int index = 0;
371 for (Map.Entry<String, String> entry : mRequestHeaders.entrySet()) {
372 String headerString = entry.getKey() + ": " + entry.getValue();
373 values.put(Downloads.Impl.RequestHeaders.INSERT_KEY_PREFIX + index, headerString);
374 index++;
375 }
376 }
377
Steve Howarda2709362010-07-02 17:12:48 -0700378 private void putIfNonNull(ContentValues contentValues, String key, String value) {
379 if (value != null) {
380 contentValues.put(key, value);
381 }
382 }
383 }
384
385 /**
386 * This class may be used to filter download manager queries.
387 */
388 public static class Query {
389 private Long mId;
390 private Integer mStatusFlags = null;
391
392 /**
393 * Include only the download with the given ID.
394 * @return this object
395 */
396 public Query setFilterById(long id) {
397 mId = id;
398 return this;
399 }
400
401 /**
402 * Include only downloads with status matching any the given status flags.
403 * @param flags any combination of the STATUS_* bit flags
404 * @return this object
405 */
406 public Query setFilterByStatus(int flags) {
407 mStatusFlags = flags;
408 return this;
409 }
410
411 /**
412 * Run this query using the given ContentResolver.
413 * @param projection the projection to pass to ContentResolver.query()
414 * @return the Cursor returned by ContentResolver.query()
415 */
416 Cursor runQuery(ContentResolver resolver, String[] projection) {
417 Uri uri = Downloads.CONTENT_URI;
418 String selection = null;
419
420 if (mId != null) {
421 uri = Uri.withAppendedPath(uri, mId.toString());
422 }
423
424 if (mStatusFlags != null) {
425 List<String> parts = new ArrayList<String>();
426 if ((mStatusFlags & STATUS_PENDING) != 0) {
427 parts.add(statusClause("=", Downloads.STATUS_PENDING));
428 }
429 if ((mStatusFlags & STATUS_RUNNING) != 0) {
430 parts.add(statusClause("=", Downloads.STATUS_RUNNING));
431 }
432 if ((mStatusFlags & STATUS_PAUSED) != 0) {
433 parts.add(statusClause("=", Downloads.STATUS_PENDING_PAUSED));
434 parts.add(statusClause("=", Downloads.STATUS_RUNNING_PAUSED));
435 }
436 if ((mStatusFlags & STATUS_SUCCESSFUL) != 0) {
437 parts.add(statusClause("=", Downloads.STATUS_SUCCESS));
438 }
439 if ((mStatusFlags & STATUS_FAILED) != 0) {
440 parts.add("(" + statusClause(">=", 400)
441 + " AND " + statusClause("<", 600) + ")");
442 }
443 selection = joinStrings(" OR ", parts);
444 Log.w("DownloadManagerPublic", selection);
445 }
Steve Howardadcb6972010-07-12 17:09:25 -0700446 String orderBy = Downloads.COLUMN_LAST_MODIFICATION + " DESC";
447 return resolver.query(uri, projection, selection, null, orderBy);
Steve Howarda2709362010-07-02 17:12:48 -0700448 }
449
450 private String joinStrings(String joiner, Iterable<String> parts) {
451 StringBuilder builder = new StringBuilder();
452 boolean first = true;
453 for (String part : parts) {
454 if (!first) {
455 builder.append(joiner);
456 }
457 builder.append(part);
458 first = false;
459 }
460 return builder.toString();
461 }
462
463 private String statusClause(String operator, int value) {
464 return Downloads.COLUMN_STATUS + operator + "'" + value + "'";
465 }
466 }
467
468 private ContentResolver mResolver;
469
470 /**
471 * @hide
472 */
473 public DownloadManager(ContentResolver resolver) {
474 mResolver = resolver;
475 }
476
477 /**
478 * Enqueue a new download. The download will start automatically once the download manager is
479 * ready to execute it and connectivity is available.
480 *
481 * @param request the parameters specifying this download
482 * @return an ID for the download, unique across the system. This ID is used to make future
483 * calls related to this download.
484 */
485 public long enqueue(Request request) {
486 ContentValues values = request.toContentValues();
487 Uri downloadUri = mResolver.insert(Downloads.CONTENT_URI, values);
488 long id = Long.parseLong(downloadUri.getLastPathSegment());
489 return id;
490 }
491
492 /**
493 * Cancel a download and remove it from the download manager. The download will be stopped if
494 * it was running, and it will no longer be accessible through the download manager. If a file
495 * was already downloaded, it will not be deleted.
496 *
497 * @param id the ID of the download
498 */
499 public void remove(long id) {
500 int numDeleted = mResolver.delete(getDownloadUri(id), null, null);
501 if (numDeleted == 0) {
502 throw new IllegalArgumentException("Download " + id + " does not exist");
503 }
504 }
505
506 /**
507 * Query the download manager about downloads that have been requested.
508 * @param query parameters specifying filters for this query
509 * @return a Cursor over the result set of downloads, with columns consisting of all the
510 * COLUMN_* constants.
511 */
512 public Cursor query(Query query) {
513 Cursor underlyingCursor = query.runQuery(mResolver, UNDERLYING_COLUMNS);
514 return new CursorTranslator(underlyingCursor);
515 }
516
517 /**
518 * Open a downloaded file for reading. The download must have completed.
519 * @param id the ID of the download
520 * @return a read-only {@link ParcelFileDescriptor}
521 * @throws FileNotFoundException if the destination file does not already exist
522 */
523 public ParcelFileDescriptor openDownloadedFile(long id) throws FileNotFoundException {
524 return mResolver.openFileDescriptor(getDownloadUri(id), "r");
525 }
526
527 /**
528 * Get the DownloadProvider URI for the download with the given ID.
529 */
530 private Uri getDownloadUri(long id) {
531 Uri downloadUri = Uri.withAppendedPath(Downloads.CONTENT_URI, Long.toString(id));
532 return downloadUri;
533 }
534
535 /**
536 * This class wraps a cursor returned by DownloadProvider -- the "underlying cursor" -- and
537 * presents a different set of columns, those defined in the DownloadManager.COLUMN_* constants.
538 * Some columns correspond directly to underlying values while others are computed from
539 * underlying data.
540 */
541 private static class CursorTranslator extends CursorWrapper {
542 public CursorTranslator(Cursor cursor) {
543 super(cursor);
544 }
545
546 @Override
547 public int getColumnIndex(String columnName) {
548 return Arrays.asList(COLUMNS).indexOf(columnName);
549 }
550
551 @Override
552 public int getColumnIndexOrThrow(String columnName) throws IllegalArgumentException {
553 int index = getColumnIndex(columnName);
554 if (index == -1) {
555 throw new IllegalArgumentException();
556 }
557 return index;
558 }
559
560 @Override
561 public String getColumnName(int columnIndex) {
562 int numColumns = COLUMNS.length;
563 if (columnIndex < 0 || columnIndex >= numColumns) {
564 throw new IllegalArgumentException("Invalid column index " + columnIndex + ", "
565 + numColumns + " columns exist");
566 }
567 return COLUMNS[columnIndex];
568 }
569
570 @Override
571 public String[] getColumnNames() {
572 String[] returnColumns = new String[COLUMNS.length];
573 System.arraycopy(COLUMNS, 0, returnColumns, 0, COLUMNS.length);
574 return returnColumns;
575 }
576
577 @Override
578 public int getColumnCount() {
579 return COLUMNS.length;
580 }
581
582 @Override
583 public byte[] getBlob(int columnIndex) {
584 throw new UnsupportedOperationException();
585 }
586
587 @Override
588 public double getDouble(int columnIndex) {
589 return getLong(columnIndex);
590 }
591
592 private boolean isLongColumn(String column) {
593 return LONG_COLUMNS.contains(column);
594 }
595
596 @Override
597 public float getFloat(int columnIndex) {
598 return (float) getDouble(columnIndex);
599 }
600
601 @Override
602 public int getInt(int columnIndex) {
603 return (int) getLong(columnIndex);
604 }
605
606 @Override
607 public long getLong(int columnIndex) {
608 return translateLong(getColumnName(columnIndex));
609 }
610
611 @Override
612 public short getShort(int columnIndex) {
613 return (short) getLong(columnIndex);
614 }
615
616 @Override
617 public String getString(int columnIndex) {
618 return translateString(getColumnName(columnIndex));
619 }
620
621 private String translateString(String column) {
622 if (isLongColumn(column)) {
623 return Long.toString(translateLong(column));
624 }
625 if (column.equals(COLUMN_TITLE)) {
626 return getUnderlyingString(Downloads.COLUMN_TITLE);
627 }
628 if (column.equals(COLUMN_DESCRIPTION)) {
629 return getUnderlyingString(Downloads.COLUMN_DESCRIPTION);
630 }
631 if (column.equals(COLUMN_URI)) {
632 return getUnderlyingString(Downloads.COLUMN_URI);
633 }
634 if (column.equals(COLUMN_MEDIA_TYPE)) {
635 return getUnderlyingString(Downloads.COLUMN_MIME_TYPE);
636 }
637 assert column.equals(COLUMN_LOCAL_URI);
Steve Howardadcb6972010-07-12 17:09:25 -0700638 return Uri.fromFile(new File(getUnderlyingString(Downloads._DATA))).toString();
Steve Howarda2709362010-07-02 17:12:48 -0700639 }
640
641 private long translateLong(String column) {
642 if (!isLongColumn(column)) {
643 // mimic behavior of underlying cursor -- most likely, throw NumberFormatException
644 return Long.valueOf(translateString(column));
645 }
646
647 if (column.equals(COLUMN_ID)) {
648 return getUnderlyingLong(Downloads.Impl._ID);
649 }
650 if (column.equals(COLUMN_TOTAL_SIZE_BYTES)) {
651 return getUnderlyingLong(Downloads.COLUMN_TOTAL_BYTES);
652 }
653 if (column.equals(COLUMN_STATUS)) {
654 return translateStatus((int) getUnderlyingLong(Downloads.COLUMN_STATUS));
655 }
656 if (column.equals(COLUMN_ERROR_CODE)) {
657 return translateErrorCode((int) getUnderlyingLong(Downloads.COLUMN_STATUS));
658 }
659 if (column.equals(COLUMN_BYTES_DOWNLOADED_SO_FAR)) {
660 return getUnderlyingLong(Downloads.COLUMN_CURRENT_BYTES);
661 }
Steve Howardadcb6972010-07-12 17:09:25 -0700662 assert column.equals(COLUMN_LAST_MODIFIED_TIMESTAMP);
663 return getUnderlyingLong(Downloads.COLUMN_LAST_MODIFICATION);
Steve Howarda2709362010-07-02 17:12:48 -0700664 }
665
666 private long translateErrorCode(int status) {
667 if (translateStatus(status) != STATUS_FAILED) {
668 return 0; // arbitrary value when status is not an error
669 }
670 if ((400 <= status && status < 490) || (500 <= status && status < 600)) {
671 // HTTP status code
672 return status;
673 }
674
675 switch (status) {
676 case Downloads.STATUS_FILE_ERROR:
677 return ERROR_FILE_ERROR;
678
679 case Downloads.STATUS_UNHANDLED_HTTP_CODE:
680 case Downloads.STATUS_UNHANDLED_REDIRECT:
681 return ERROR_UNHANDLED_HTTP_CODE;
682
683 case Downloads.STATUS_HTTP_DATA_ERROR:
684 return ERROR_HTTP_DATA_ERROR;
685
686 case Downloads.STATUS_TOO_MANY_REDIRECTS:
687 return ERROR_TOO_MANY_REDIRECTS;
688
689 case Downloads.STATUS_INSUFFICIENT_SPACE_ERROR:
690 return ERROR_INSUFFICIENT_SPACE;
691
692 case Downloads.STATUS_DEVICE_NOT_FOUND_ERROR:
693 return ERROR_DEVICE_NOT_FOUND;
694
695 default:
696 return ERROR_UNKNOWN;
697 }
698 }
699
700 private long getUnderlyingLong(String column) {
701 return super.getLong(super.getColumnIndex(column));
702 }
703
704 private String getUnderlyingString(String column) {
705 return super.getString(super.getColumnIndex(column));
706 }
707
708 private long translateStatus(int status) {
709 switch (status) {
710 case Downloads.STATUS_PENDING:
711 return STATUS_PENDING;
712
713 case Downloads.STATUS_RUNNING:
714 return STATUS_RUNNING;
715
716 case Downloads.STATUS_PENDING_PAUSED:
717 case Downloads.STATUS_RUNNING_PAUSED:
718 return STATUS_PAUSED;
719
720 case Downloads.STATUS_SUCCESS:
721 return STATUS_SUCCESSFUL;
722
723 default:
724 assert Downloads.isStatusError(status);
725 return STATUS_FAILED;
726 }
727 }
728 }
729}