summaryrefslogtreecommitdiffstats
path: root/core/java
diff options
context:
space:
mode:
authorJean-Baptiste Queru <jbq@google.com>2010-02-12 13:48:51 -0800
committerAndroid (Google) Code Review <android-gerrit@google.com>2010-02-12 13:48:51 -0800
commitd85639afb62d995856b434635e55523c34b628bb (patch)
treeecb2399da120dbc21e2692e6b3051ac2e631388b /core/java
parenta16e4ee840db3dc6ecdfe60d11a8036ed4b40e7a (diff)
parent47f46981cc50f773c445a120da06ae3af866c05f (diff)
downloadframeworks_base-d85639afb62d995856b434635e55523c34b628bb.zip
frameworks_base-d85639afb62d995856b434635e55523c34b628bb.tar.gz
frameworks_base-d85639afb62d995856b434635e55523c34b628bb.tar.bz2
Merge "Public Download Manager API"
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/net/Downloads.java306
1 files changed, 306 insertions, 0 deletions
diff --git a/core/java/android/net/Downloads.java b/core/java/android/net/Downloads.java
new file mode 100644
index 0000000..b86a0e2
--- /dev/null
+++ b/core/java/android/net/Downloads.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.SystemClock;
+import android.provider.BaseColumns;
+import android.util.Log;
+
+import java.io.File;
+
+/**
+ * The Download Manager
+ *
+ * @pending
+ */
+public final class Downloads {
+
+ public static final class ByUri {
+
+ /** @hide */
+ private ByUri() {}
+
+ /**
+ * Initiate a download where the download will be tracked by its URI.
+ * @pending
+ */
+ public static boolean startDownloadByUri(
+ Context context,
+ String url,
+ boolean showDownload,
+ boolean allowRoaming,
+ String title,
+ String notification_package,
+ String notification_class) {
+ ContentResolver cr = context.getContentResolver();
+
+ // Tell download manager to start downloading update.
+ ContentValues values = new ContentValues();
+ values.put(android.provider.Downloads.Impl.COLUMN_URI, url);
+ values.put(android.provider.Downloads.Impl.COLUMN_VISIBILITY,
+ showDownload ? android.provider.Downloads.Impl.VISIBILITY_VISIBLE
+ : android.provider.Downloads.Impl.VISIBILITY_HIDDEN);
+ if (title != null) {
+ values.put(android.provider.Downloads.Impl.COLUMN_TITLE, title);
+ }
+ values.put(android.provider.Downloads.Impl.COLUMN_APP_DATA, url);
+ values.put(android.provider.Downloads.Impl.COLUMN_DESTINATION,
+ allowRoaming ? android.provider.Downloads.Impl.DESTINATION_CACHE_PARTITION :
+ android.provider.Downloads.Impl.DESTINATION_CACHE_PARTITION_NOROAMING);
+ values.put(android.provider.Downloads.Impl.COLUMN_NO_INTEGRITY, true); // Don't check ETag
+ if (notification_package != null && notification_class != null) {
+ values.put(android.provider.Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE, notification_package);
+ values.put(android.provider.Downloads.Impl.COLUMN_NOTIFICATION_CLASS, notification_class);
+ }
+
+ if (cr.insert(android.provider.Downloads.Impl.CONTENT_URI, values) == null) {
+ return false;
+ }
+ return true;
+ }
+
+ public static final class StatusInfo {
+ public boolean completed = false;
+ /** The filename of the active download. */
+ public String filename = null;
+ /** An opaque id for the download */
+ public long id = -1;
+ /** An opaque status code for the download */
+ public int statusCode = -1;
+ /** Approximate number of bytes downloaded so far, for debugging purposes. */
+ public long bytesSoFar = -1;
+ }
+
+ /** @hide */
+ private static final int STATUS_INVALID = 0;
+ /** @hide */
+ private static final int STATUS_DOWNLOADING_UPDATE = 3;
+ /** @hide */
+ private static final int STATUS_DOWNLOADED_UPDATE = 4;
+
+ /**
+ * Column projection for the query to the download manager. This must match
+ * with the constants DOWNLOADS_COLUMN_*.
+ * @hide
+ */
+ private static final String[] DOWNLOADS_PROJECTION = {
+ BaseColumns._ID,
+ android.provider.Downloads.Impl.COLUMN_APP_DATA,
+ android.provider.Downloads.Impl.COLUMN_STATUS,
+ android.provider.Downloads.Impl._DATA,
+ android.provider.Downloads.Impl.COLUMN_LAST_MODIFICATION,
+ android.provider.Downloads.Impl.COLUMN_CURRENT_BYTES,
+ };
+
+ /**
+ * The column index for the ID.
+ * @hide
+ */
+ private static final int DOWNLOADS_COLUMN_ID = 0;
+ /**
+ * The column index for the URI.
+ * @hide
+ */
+ private static final int DOWNLOADS_COLUMN_URI = 1;
+ /**
+ * The column index for the status code.
+ * @hide
+ */
+ private static final int DOWNLOADS_COLUMN_STATUS = 2;
+ /**
+ * The column index for the filename.
+ * @hide
+ */
+ private static final int DOWNLOADS_COLUMN_FILENAME = 3;
+ /**
+ * The column index for the last modification time.
+ * @hide
+ */
+ private static final int DOWNLOADS_COLUMN_LAST_MODIFICATION = 4;
+ /**
+ * The column index for the number of bytes downloaded so far.
+ * @hide
+ */
+ private static final int DOWNLOADS_COLUMN_CURRENT_BYTES = 5;
+
+ /**
+ * Gets the status of a download.
+ *
+ * @param c A Cursor pointing to a download. The URL column is assumed to be valid.
+ * @return The status of the download.
+ * @hide
+ */
+ private static final int getStatusOfDownload(
+ Cursor c,
+ long redownload_threshold) {
+ int status = c.getInt(DOWNLOADS_COLUMN_STATUS);
+ long realtime = SystemClock.elapsedRealtime();
+
+ // TODO(dougz): special handling of 503, 404? (eg, special
+ // explanatory messages to user)
+
+ if (!android.provider.Downloads.Impl.isStatusCompleted(status)) {
+ // Check if it's stuck
+ long modified = c.getLong(DOWNLOADS_COLUMN_LAST_MODIFICATION);
+ long now = System.currentTimeMillis();
+ if (now < modified || now - modified > redownload_threshold) {
+ return STATUS_INVALID;
+ }
+
+ return STATUS_DOWNLOADING_UPDATE;
+ }
+
+ if (android.provider.Downloads.Impl.isStatusError(status)) {
+ return STATUS_INVALID;
+ }
+
+ String filename = c.getString(DOWNLOADS_COLUMN_FILENAME);
+ if (filename == null) {
+ return STATUS_INVALID;
+ }
+
+ return STATUS_DOWNLOADED_UPDATE;
+ }
+
+ /**
+ * Gets a Cursor pointing to the download(s) of the current system update.
+ * @hide
+ */
+ private static final Cursor getCurrentOtaDownloads(Context context, String url) {
+ return context.getContentResolver().query(
+ android.provider.Downloads.Impl.CONTENT_URI,
+ DOWNLOADS_PROJECTION,
+ android.provider.Downloads.Impl.COLUMN_APP_DATA + "='" + url.replace("'", "''") + "'",
+ null,
+ null);
+ }
+
+ /**
+ * Returns a StatusInfo with the result of trying to download the
+ * given URL. Returns null if no attempts have been made.
+ * @pending
+ */
+ public static final StatusInfo getStatus(
+ Context context,
+ String url,
+ long redownload_threshold) {
+ StatusInfo result = null;
+ boolean hasFailedDownload = false;
+ long failedDownloadModificationTime = 0;
+ Cursor c = getCurrentOtaDownloads(context, url);
+ try {
+ while (c != null && c.moveToNext()) {
+ if (result == null) {
+ result = new StatusInfo();
+ }
+ int status = getStatusOfDownload(c, redownload_threshold);
+ if (status == STATUS_DOWNLOADING_UPDATE ||
+ status == STATUS_DOWNLOADED_UPDATE) {
+ result.completed = (status == STATUS_DOWNLOADED_UPDATE);
+ result.filename = c.getString(DOWNLOADS_COLUMN_FILENAME);
+ result.id = c.getLong(DOWNLOADS_COLUMN_ID);
+ result.statusCode = c.getInt(DOWNLOADS_COLUMN_STATUS);
+ result.bytesSoFar = c.getLong(DOWNLOADS_COLUMN_CURRENT_BYTES);
+ return result;
+ }
+
+ long modTime = c.getLong(DOWNLOADS_COLUMN_LAST_MODIFICATION);
+ if (hasFailedDownload &&
+ modTime < failedDownloadModificationTime) {
+ // older than the one already in result; skip it.
+ continue;
+ }
+
+ hasFailedDownload = true;
+ failedDownloadModificationTime = modTime;
+ result.statusCode = c.getInt(DOWNLOADS_COLUMN_STATUS);
+ result.bytesSoFar = c.getLong(DOWNLOADS_COLUMN_CURRENT_BYTES);
+ }
+ } finally {
+ c.close();
+ }
+ return result;
+ }
+
+ /**
+ * Query where clause for general querying.
+ * @hide
+ */
+ private static final String QUERY_WHERE_CLAUSE =
+ android.provider.Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE + "=? AND " +
+ android.provider.Downloads.Impl.COLUMN_NOTIFICATION_CLASS + "=?";
+
+ /**
+ * Delete all the downloads for a package/class pair.
+ * @pending
+ */
+ public static final void removeAllDownloadsByPackage(
+ Context context,
+ String notification_package,
+ String notification_class) {
+ context.getContentResolver().delete(
+ android.provider.Downloads.Impl.CONTENT_URI,
+ QUERY_WHERE_CLAUSE,
+ new String[] { notification_package, notification_class });
+ }
+
+ /**
+ * @pending
+ */
+ public static final int getProgressColumnId() {
+ return 0;
+ }
+
+ /**
+ * @pending
+ */
+ public static final int getProgressColumnCurrentBytes() {
+ return 1;
+ }
+
+ /**
+ * @pending
+ */
+ public static final int getProgressColumnTotalBytes() {
+ return 2;
+ }
+
+ /** @hide */
+ private static final String[] PROJECTION = {
+ BaseColumns._ID, android.provider.Downloads.Impl.COLUMN_CURRENT_BYTES, android.provider.Downloads.Impl.COLUMN_TOTAL_BYTES
+ };
+
+ /**
+ * @pending
+ */
+ public static final Cursor getProgressCursor(Context context, long id) {
+ Uri downloadUri = Uri.withAppendedPath(android.provider.Downloads.Impl.CONTENT_URI, String.valueOf(id));
+ return context.getContentResolver().query(downloadUri, PROJECTION, null, null, null);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ private Downloads() {}
+}