blob: 02b6210da88d344bcd4b960344b31b62b005055e [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()) {
353 // TODO request headers support
354 throw new UnsupportedOperationException();
355 }
356
357 putIfNonNull(values, Downloads.COLUMN_TITLE, mTitle);
358 putIfNonNull(values, Downloads.COLUMN_DESCRIPTION, mDescription);
359 putIfNonNull(values, Downloads.COLUMN_MIME_TYPE, mMediaType);
360
361 int visibility = Downloads.VISIBILITY_HIDDEN;
362 if ((mNotificationFlags & NOTIFICATION_WHEN_RUNNING) != 0) {
363 visibility = Downloads.VISIBILITY_VISIBLE;
364 }
365 values.put(Downloads.COLUMN_VISIBILITY, visibility);
366
367 return values;
368 }
369
370 private void putIfNonNull(ContentValues contentValues, String key, String value) {
371 if (value != null) {
372 contentValues.put(key, value);
373 }
374 }
375 }
376
377 /**
378 * This class may be used to filter download manager queries.
379 */
380 public static class Query {
381 private Long mId;
382 private Integer mStatusFlags = null;
383
384 /**
385 * Include only the download with the given ID.
386 * @return this object
387 */
388 public Query setFilterById(long id) {
389 mId = id;
390 return this;
391 }
392
393 /**
394 * Include only downloads with status matching any the given status flags.
395 * @param flags any combination of the STATUS_* bit flags
396 * @return this object
397 */
398 public Query setFilterByStatus(int flags) {
399 mStatusFlags = flags;
400 return this;
401 }
402
403 /**
404 * Run this query using the given ContentResolver.
405 * @param projection the projection to pass to ContentResolver.query()
406 * @return the Cursor returned by ContentResolver.query()
407 */
408 Cursor runQuery(ContentResolver resolver, String[] projection) {
409 Uri uri = Downloads.CONTENT_URI;
410 String selection = null;
411
412 if (mId != null) {
413 uri = Uri.withAppendedPath(uri, mId.toString());
414 }
415
416 if (mStatusFlags != null) {
417 List<String> parts = new ArrayList<String>();
418 if ((mStatusFlags & STATUS_PENDING) != 0) {
419 parts.add(statusClause("=", Downloads.STATUS_PENDING));
420 }
421 if ((mStatusFlags & STATUS_RUNNING) != 0) {
422 parts.add(statusClause("=", Downloads.STATUS_RUNNING));
423 }
424 if ((mStatusFlags & STATUS_PAUSED) != 0) {
425 parts.add(statusClause("=", Downloads.STATUS_PENDING_PAUSED));
426 parts.add(statusClause("=", Downloads.STATUS_RUNNING_PAUSED));
427 }
428 if ((mStatusFlags & STATUS_SUCCESSFUL) != 0) {
429 parts.add(statusClause("=", Downloads.STATUS_SUCCESS));
430 }
431 if ((mStatusFlags & STATUS_FAILED) != 0) {
432 parts.add("(" + statusClause(">=", 400)
433 + " AND " + statusClause("<", 600) + ")");
434 }
435 selection = joinStrings(" OR ", parts);
436 Log.w("DownloadManagerPublic", selection);
437 }
Steve Howardadcb6972010-07-12 17:09:25 -0700438 String orderBy = Downloads.COLUMN_LAST_MODIFICATION + " DESC";
439 return resolver.query(uri, projection, selection, null, orderBy);
Steve Howarda2709362010-07-02 17:12:48 -0700440 }
441
442 private String joinStrings(String joiner, Iterable<String> parts) {
443 StringBuilder builder = new StringBuilder();
444 boolean first = true;
445 for (String part : parts) {
446 if (!first) {
447 builder.append(joiner);
448 }
449 builder.append(part);
450 first = false;
451 }
452 return builder.toString();
453 }
454
455 private String statusClause(String operator, int value) {
456 return Downloads.COLUMN_STATUS + operator + "'" + value + "'";
457 }
458 }
459
460 private ContentResolver mResolver;
461
462 /**
463 * @hide
464 */
465 public DownloadManager(ContentResolver resolver) {
466 mResolver = resolver;
467 }
468
469 /**
470 * Enqueue a new download. The download will start automatically once the download manager is
471 * ready to execute it and connectivity is available.
472 *
473 * @param request the parameters specifying this download
474 * @return an ID for the download, unique across the system. This ID is used to make future
475 * calls related to this download.
476 */
477 public long enqueue(Request request) {
478 ContentValues values = request.toContentValues();
479 Uri downloadUri = mResolver.insert(Downloads.CONTENT_URI, values);
480 long id = Long.parseLong(downloadUri.getLastPathSegment());
481 return id;
482 }
483
484 /**
485 * Cancel a download and remove it from the download manager. The download will be stopped if
486 * it was running, and it will no longer be accessible through the download manager. If a file
487 * was already downloaded, it will not be deleted.
488 *
489 * @param id the ID of the download
490 */
491 public void remove(long id) {
492 int numDeleted = mResolver.delete(getDownloadUri(id), null, null);
493 if (numDeleted == 0) {
494 throw new IllegalArgumentException("Download " + id + " does not exist");
495 }
496 }
497
498 /**
499 * Query the download manager about downloads that have been requested.
500 * @param query parameters specifying filters for this query
501 * @return a Cursor over the result set of downloads, with columns consisting of all the
502 * COLUMN_* constants.
503 */
504 public Cursor query(Query query) {
505 Cursor underlyingCursor = query.runQuery(mResolver, UNDERLYING_COLUMNS);
506 return new CursorTranslator(underlyingCursor);
507 }
508
509 /**
510 * Open a downloaded file for reading. The download must have completed.
511 * @param id the ID of the download
512 * @return a read-only {@link ParcelFileDescriptor}
513 * @throws FileNotFoundException if the destination file does not already exist
514 */
515 public ParcelFileDescriptor openDownloadedFile(long id) throws FileNotFoundException {
516 return mResolver.openFileDescriptor(getDownloadUri(id), "r");
517 }
518
519 /**
520 * Get the DownloadProvider URI for the download with the given ID.
521 */
522 private Uri getDownloadUri(long id) {
523 Uri downloadUri = Uri.withAppendedPath(Downloads.CONTENT_URI, Long.toString(id));
524 return downloadUri;
525 }
526
527 /**
528 * This class wraps a cursor returned by DownloadProvider -- the "underlying cursor" -- and
529 * presents a different set of columns, those defined in the DownloadManager.COLUMN_* constants.
530 * Some columns correspond directly to underlying values while others are computed from
531 * underlying data.
532 */
533 private static class CursorTranslator extends CursorWrapper {
534 public CursorTranslator(Cursor cursor) {
535 super(cursor);
536 }
537
538 @Override
539 public int getColumnIndex(String columnName) {
540 return Arrays.asList(COLUMNS).indexOf(columnName);
541 }
542
543 @Override
544 public int getColumnIndexOrThrow(String columnName) throws IllegalArgumentException {
545 int index = getColumnIndex(columnName);
546 if (index == -1) {
547 throw new IllegalArgumentException();
548 }
549 return index;
550 }
551
552 @Override
553 public String getColumnName(int columnIndex) {
554 int numColumns = COLUMNS.length;
555 if (columnIndex < 0 || columnIndex >= numColumns) {
556 throw new IllegalArgumentException("Invalid column index " + columnIndex + ", "
557 + numColumns + " columns exist");
558 }
559 return COLUMNS[columnIndex];
560 }
561
562 @Override
563 public String[] getColumnNames() {
564 String[] returnColumns = new String[COLUMNS.length];
565 System.arraycopy(COLUMNS, 0, returnColumns, 0, COLUMNS.length);
566 return returnColumns;
567 }
568
569 @Override
570 public int getColumnCount() {
571 return COLUMNS.length;
572 }
573
574 @Override
575 public byte[] getBlob(int columnIndex) {
576 throw new UnsupportedOperationException();
577 }
578
579 @Override
580 public double getDouble(int columnIndex) {
581 return getLong(columnIndex);
582 }
583
584 private boolean isLongColumn(String column) {
585 return LONG_COLUMNS.contains(column);
586 }
587
588 @Override
589 public float getFloat(int columnIndex) {
590 return (float) getDouble(columnIndex);
591 }
592
593 @Override
594 public int getInt(int columnIndex) {
595 return (int) getLong(columnIndex);
596 }
597
598 @Override
599 public long getLong(int columnIndex) {
600 return translateLong(getColumnName(columnIndex));
601 }
602
603 @Override
604 public short getShort(int columnIndex) {
605 return (short) getLong(columnIndex);
606 }
607
608 @Override
609 public String getString(int columnIndex) {
610 return translateString(getColumnName(columnIndex));
611 }
612
613 private String translateString(String column) {
614 if (isLongColumn(column)) {
615 return Long.toString(translateLong(column));
616 }
617 if (column.equals(COLUMN_TITLE)) {
618 return getUnderlyingString(Downloads.COLUMN_TITLE);
619 }
620 if (column.equals(COLUMN_DESCRIPTION)) {
621 return getUnderlyingString(Downloads.COLUMN_DESCRIPTION);
622 }
623 if (column.equals(COLUMN_URI)) {
624 return getUnderlyingString(Downloads.COLUMN_URI);
625 }
626 if (column.equals(COLUMN_MEDIA_TYPE)) {
627 return getUnderlyingString(Downloads.COLUMN_MIME_TYPE);
628 }
629 assert column.equals(COLUMN_LOCAL_URI);
Steve Howardadcb6972010-07-12 17:09:25 -0700630 return Uri.fromFile(new File(getUnderlyingString(Downloads._DATA))).toString();
Steve Howarda2709362010-07-02 17:12:48 -0700631 }
632
633 private long translateLong(String column) {
634 if (!isLongColumn(column)) {
635 // mimic behavior of underlying cursor -- most likely, throw NumberFormatException
636 return Long.valueOf(translateString(column));
637 }
638
639 if (column.equals(COLUMN_ID)) {
640 return getUnderlyingLong(Downloads.Impl._ID);
641 }
642 if (column.equals(COLUMN_TOTAL_SIZE_BYTES)) {
643 return getUnderlyingLong(Downloads.COLUMN_TOTAL_BYTES);
644 }
645 if (column.equals(COLUMN_STATUS)) {
646 return translateStatus((int) getUnderlyingLong(Downloads.COLUMN_STATUS));
647 }
648 if (column.equals(COLUMN_ERROR_CODE)) {
649 return translateErrorCode((int) getUnderlyingLong(Downloads.COLUMN_STATUS));
650 }
651 if (column.equals(COLUMN_BYTES_DOWNLOADED_SO_FAR)) {
652 return getUnderlyingLong(Downloads.COLUMN_CURRENT_BYTES);
653 }
Steve Howardadcb6972010-07-12 17:09:25 -0700654 assert column.equals(COLUMN_LAST_MODIFIED_TIMESTAMP);
655 return getUnderlyingLong(Downloads.COLUMN_LAST_MODIFICATION);
Steve Howarda2709362010-07-02 17:12:48 -0700656 }
657
658 private long translateErrorCode(int status) {
659 if (translateStatus(status) != STATUS_FAILED) {
660 return 0; // arbitrary value when status is not an error
661 }
662 if ((400 <= status && status < 490) || (500 <= status && status < 600)) {
663 // HTTP status code
664 return status;
665 }
666
667 switch (status) {
668 case Downloads.STATUS_FILE_ERROR:
669 return ERROR_FILE_ERROR;
670
671 case Downloads.STATUS_UNHANDLED_HTTP_CODE:
672 case Downloads.STATUS_UNHANDLED_REDIRECT:
673 return ERROR_UNHANDLED_HTTP_CODE;
674
675 case Downloads.STATUS_HTTP_DATA_ERROR:
676 return ERROR_HTTP_DATA_ERROR;
677
678 case Downloads.STATUS_TOO_MANY_REDIRECTS:
679 return ERROR_TOO_MANY_REDIRECTS;
680
681 case Downloads.STATUS_INSUFFICIENT_SPACE_ERROR:
682 return ERROR_INSUFFICIENT_SPACE;
683
684 case Downloads.STATUS_DEVICE_NOT_FOUND_ERROR:
685 return ERROR_DEVICE_NOT_FOUND;
686
687 default:
688 return ERROR_UNKNOWN;
689 }
690 }
691
692 private long getUnderlyingLong(String column) {
693 return super.getLong(super.getColumnIndex(column));
694 }
695
696 private String getUnderlyingString(String column) {
697 return super.getString(super.getColumnIndex(column));
698 }
699
700 private long translateStatus(int status) {
701 switch (status) {
702 case Downloads.STATUS_PENDING:
703 return STATUS_PENDING;
704
705 case Downloads.STATUS_RUNNING:
706 return STATUS_RUNNING;
707
708 case Downloads.STATUS_PENDING_PAUSED:
709 case Downloads.STATUS_RUNNING_PAUSED:
710 return STATUS_PAUSED;
711
712 case Downloads.STATUS_SUCCESS:
713 return STATUS_SUCCESSFUL;
714
715 default:
716 assert Downloads.isStatusError(status);
717 return STATUS_FAILED;
718 }
719 }
720 }
721}