Downloads.java revision 47f46981cc50f773c445a120da06ae3af866c05f
1/* 2 * Copyright (C) 2008 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.ContentUris; 21import android.content.ContentValues; 22import android.content.Context; 23import android.database.Cursor; 24import android.net.Uri; 25import android.os.SystemClock; 26import android.provider.BaseColumns; 27import android.util.Log; 28 29import java.io.File; 30 31/** 32 * The Download Manager 33 * 34 * @pending 35 */ 36public final class Downloads { 37 38 public static final class ByUri { 39 40 /** @hide */ 41 private ByUri() {} 42 43 /** 44 * Initiate a download where the download will be tracked by its URI. 45 * @pending 46 */ 47 public static boolean startDownloadByUri( 48 Context context, 49 String url, 50 boolean showDownload, 51 boolean allowRoaming, 52 String title, 53 String notification_package, 54 String notification_class) { 55 ContentResolver cr = context.getContentResolver(); 56 57 // Tell download manager to start downloading update. 58 ContentValues values = new ContentValues(); 59 values.put(android.provider.Downloads.Impl.COLUMN_URI, url); 60 values.put(android.provider.Downloads.Impl.COLUMN_VISIBILITY, 61 showDownload ? android.provider.Downloads.Impl.VISIBILITY_VISIBLE 62 : android.provider.Downloads.Impl.VISIBILITY_HIDDEN); 63 if (title != null) { 64 values.put(android.provider.Downloads.Impl.COLUMN_TITLE, title); 65 } 66 values.put(android.provider.Downloads.Impl.COLUMN_APP_DATA, url); 67 values.put(android.provider.Downloads.Impl.COLUMN_DESTINATION, 68 allowRoaming ? android.provider.Downloads.Impl.DESTINATION_CACHE_PARTITION : 69 android.provider.Downloads.Impl.DESTINATION_CACHE_PARTITION_NOROAMING); 70 values.put(android.provider.Downloads.Impl.COLUMN_NO_INTEGRITY, true); // Don't check ETag 71 if (notification_package != null && notification_class != null) { 72 values.put(android.provider.Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE, notification_package); 73 values.put(android.provider.Downloads.Impl.COLUMN_NOTIFICATION_CLASS, notification_class); 74 } 75 76 if (cr.insert(android.provider.Downloads.Impl.CONTENT_URI, values) == null) { 77 return false; 78 } 79 return true; 80 } 81 82 public static final class StatusInfo { 83 public boolean completed = false; 84 /** The filename of the active download. */ 85 public String filename = null; 86 /** An opaque id for the download */ 87 public long id = -1; 88 /** An opaque status code for the download */ 89 public int statusCode = -1; 90 /** Approximate number of bytes downloaded so far, for debugging purposes. */ 91 public long bytesSoFar = -1; 92 } 93 94 /** @hide */ 95 private static final int STATUS_INVALID = 0; 96 /** @hide */ 97 private static final int STATUS_DOWNLOADING_UPDATE = 3; 98 /** @hide */ 99 private static final int STATUS_DOWNLOADED_UPDATE = 4; 100 101 /** 102 * Column projection for the query to the download manager. This must match 103 * with the constants DOWNLOADS_COLUMN_*. 104 * @hide 105 */ 106 private static final String[] DOWNLOADS_PROJECTION = { 107 BaseColumns._ID, 108 android.provider.Downloads.Impl.COLUMN_APP_DATA, 109 android.provider.Downloads.Impl.COLUMN_STATUS, 110 android.provider.Downloads.Impl._DATA, 111 android.provider.Downloads.Impl.COLUMN_LAST_MODIFICATION, 112 android.provider.Downloads.Impl.COLUMN_CURRENT_BYTES, 113 }; 114 115 /** 116 * The column index for the ID. 117 * @hide 118 */ 119 private static final int DOWNLOADS_COLUMN_ID = 0; 120 /** 121 * The column index for the URI. 122 * @hide 123 */ 124 private static final int DOWNLOADS_COLUMN_URI = 1; 125 /** 126 * The column index for the status code. 127 * @hide 128 */ 129 private static final int DOWNLOADS_COLUMN_STATUS = 2; 130 /** 131 * The column index for the filename. 132 * @hide 133 */ 134 private static final int DOWNLOADS_COLUMN_FILENAME = 3; 135 /** 136 * The column index for the last modification time. 137 * @hide 138 */ 139 private static final int DOWNLOADS_COLUMN_LAST_MODIFICATION = 4; 140 /** 141 * The column index for the number of bytes downloaded so far. 142 * @hide 143 */ 144 private static final int DOWNLOADS_COLUMN_CURRENT_BYTES = 5; 145 146 /** 147 * Gets the status of a download. 148 * 149 * @param c A Cursor pointing to a download. The URL column is assumed to be valid. 150 * @return The status of the download. 151 * @hide 152 */ 153 private static final int getStatusOfDownload( 154 Cursor c, 155 long redownload_threshold) { 156 int status = c.getInt(DOWNLOADS_COLUMN_STATUS); 157 long realtime = SystemClock.elapsedRealtime(); 158 159 // TODO(dougz): special handling of 503, 404? (eg, special 160 // explanatory messages to user) 161 162 if (!android.provider.Downloads.Impl.isStatusCompleted(status)) { 163 // Check if it's stuck 164 long modified = c.getLong(DOWNLOADS_COLUMN_LAST_MODIFICATION); 165 long now = System.currentTimeMillis(); 166 if (now < modified || now - modified > redownload_threshold) { 167 return STATUS_INVALID; 168 } 169 170 return STATUS_DOWNLOADING_UPDATE; 171 } 172 173 if (android.provider.Downloads.Impl.isStatusError(status)) { 174 return STATUS_INVALID; 175 } 176 177 String filename = c.getString(DOWNLOADS_COLUMN_FILENAME); 178 if (filename == null) { 179 return STATUS_INVALID; 180 } 181 182 return STATUS_DOWNLOADED_UPDATE; 183 } 184 185 /** 186 * Gets a Cursor pointing to the download(s) of the current system update. 187 * @hide 188 */ 189 private static final Cursor getCurrentOtaDownloads(Context context, String url) { 190 return context.getContentResolver().query( 191 android.provider.Downloads.Impl.CONTENT_URI, 192 DOWNLOADS_PROJECTION, 193 android.provider.Downloads.Impl.COLUMN_APP_DATA + "='" + url.replace("'", "''") + "'", 194 null, 195 null); 196 } 197 198 /** 199 * Returns a StatusInfo with the result of trying to download the 200 * given URL. Returns null if no attempts have been made. 201 * @pending 202 */ 203 public static final StatusInfo getStatus( 204 Context context, 205 String url, 206 long redownload_threshold) { 207 StatusInfo result = null; 208 boolean hasFailedDownload = false; 209 long failedDownloadModificationTime = 0; 210 Cursor c = getCurrentOtaDownloads(context, url); 211 try { 212 while (c != null && c.moveToNext()) { 213 if (result == null) { 214 result = new StatusInfo(); 215 } 216 int status = getStatusOfDownload(c, redownload_threshold); 217 if (status == STATUS_DOWNLOADING_UPDATE || 218 status == STATUS_DOWNLOADED_UPDATE) { 219 result.completed = (status == STATUS_DOWNLOADED_UPDATE); 220 result.filename = c.getString(DOWNLOADS_COLUMN_FILENAME); 221 result.id = c.getLong(DOWNLOADS_COLUMN_ID); 222 result.statusCode = c.getInt(DOWNLOADS_COLUMN_STATUS); 223 result.bytesSoFar = c.getLong(DOWNLOADS_COLUMN_CURRENT_BYTES); 224 return result; 225 } 226 227 long modTime = c.getLong(DOWNLOADS_COLUMN_LAST_MODIFICATION); 228 if (hasFailedDownload && 229 modTime < failedDownloadModificationTime) { 230 // older than the one already in result; skip it. 231 continue; 232 } 233 234 hasFailedDownload = true; 235 failedDownloadModificationTime = modTime; 236 result.statusCode = c.getInt(DOWNLOADS_COLUMN_STATUS); 237 result.bytesSoFar = c.getLong(DOWNLOADS_COLUMN_CURRENT_BYTES); 238 } 239 } finally { 240 c.close(); 241 } 242 return result; 243 } 244 245 /** 246 * Query where clause for general querying. 247 * @hide 248 */ 249 private static final String QUERY_WHERE_CLAUSE = 250 android.provider.Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE + "=? AND " + 251 android.provider.Downloads.Impl.COLUMN_NOTIFICATION_CLASS + "=?"; 252 253 /** 254 * Delete all the downloads for a package/class pair. 255 * @pending 256 */ 257 public static final void removeAllDownloadsByPackage( 258 Context context, 259 String notification_package, 260 String notification_class) { 261 context.getContentResolver().delete( 262 android.provider.Downloads.Impl.CONTENT_URI, 263 QUERY_WHERE_CLAUSE, 264 new String[] { notification_package, notification_class }); 265 } 266 267 /** 268 * @pending 269 */ 270 public static final int getProgressColumnId() { 271 return 0; 272 } 273 274 /** 275 * @pending 276 */ 277 public static final int getProgressColumnCurrentBytes() { 278 return 1; 279 } 280 281 /** 282 * @pending 283 */ 284 public static final int getProgressColumnTotalBytes() { 285 return 2; 286 } 287 288 /** @hide */ 289 private static final String[] PROJECTION = { 290 BaseColumns._ID, android.provider.Downloads.Impl.COLUMN_CURRENT_BYTES, android.provider.Downloads.Impl.COLUMN_TOTAL_BYTES 291 }; 292 293 /** 294 * @pending 295 */ 296 public static final Cursor getProgressCursor(Context context, long id) { 297 Uri downloadUri = Uri.withAppendedPath(android.provider.Downloads.Impl.CONTENT_URI, String.valueOf(id)); 298 return context.getContentResolver().query(downloadUri, PROJECTION, null, null, null); 299 } 300 } 301 302 /** 303 * @hide 304 */ 305 private Downloads() {} 306} 307