DownloadInfo.java revision 5224c6fbf20b4803a580ef449ab87ebfbbfedb78
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 com.android.providers.downloads; 18 19import android.content.ContentResolver; 20import android.content.Context; 21import android.content.Intent; 22import android.database.Cursor; 23import android.net.Uri; 24import android.provider.Downloads; 25 26import java.util.Collections; 27import java.util.HashMap; 28import java.util.Map; 29 30/** 31 * Stores information about an individual download. 32 */ 33public class DownloadInfo { 34 public int mId; 35 public String mUri; 36 public boolean mNoIntegrity; 37 public String mHint; 38 public String mFileName; 39 public String mMimeType; 40 public int mDestination; 41 public int mVisibility; 42 public int mControl; 43 public int mStatus; 44 public int mNumFailed; 45 public int mRetryAfter; 46 public int mRedirectCount; 47 public long mLastMod; 48 public String mPackage; 49 public String mClass; 50 public String mExtras; 51 public String mCookies; 52 public String mUserAgent; 53 public String mReferer; 54 public int mTotalBytes; 55 public int mCurrentBytes; 56 public String mETag; 57 public boolean mMediaScanned; 58 59 public int mFuzz; 60 61 public volatile boolean mHasActiveThread; 62 private Map<String, String> mRequestHeaders = new HashMap<String, String>(); 63 64 public DownloadInfo(ContentResolver resolver, Cursor cursor) { 65 int retryRedirect = 66 cursor.getInt(cursor.getColumnIndexOrThrow(Constants.RETRY_AFTER_X_REDIRECT_COUNT)); 67 mId = cursor.getInt(cursor.getColumnIndexOrThrow(Downloads.Impl._ID)); 68 mUri = cursor.getString(cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_URI)); 69 mNoIntegrity = cursor.getInt(cursor.getColumnIndexOrThrow( 70 Downloads.Impl.COLUMN_NO_INTEGRITY)) == 1; 71 mHint = cursor.getString(cursor.getColumnIndexOrThrow( 72 Downloads.Impl.COLUMN_FILE_NAME_HINT)); 73 mFileName = cursor.getString(cursor.getColumnIndexOrThrow(Downloads.Impl._DATA)); 74 mMimeType = cursor.getString(cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_MIME_TYPE)); 75 mDestination = 76 cursor.getInt(cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_DESTINATION)); 77 mVisibility = cursor.getInt(cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_VISIBILITY)); 78 mControl = cursor.getInt(cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_CONTROL)); 79 mStatus = cursor.getInt(cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_STATUS)); 80 mNumFailed = cursor.getInt(cursor.getColumnIndexOrThrow(Constants.FAILED_CONNECTIONS)); 81 mRetryAfter = retryRedirect & 0xfffffff; 82 mRedirectCount = retryRedirect >> 28; 83 mLastMod = cursor.getLong(cursor.getColumnIndexOrThrow( 84 Downloads.Impl.COLUMN_LAST_MODIFICATION)); 85 mPackage = cursor.getString(cursor.getColumnIndexOrThrow( 86 Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE)); 87 mClass = cursor.getString(cursor.getColumnIndexOrThrow( 88 Downloads.Impl.COLUMN_NOTIFICATION_CLASS)); 89 mExtras = cursor.getString(cursor.getColumnIndexOrThrow( 90 Downloads.Impl.COLUMN_NOTIFICATION_EXTRAS)); 91 mCookies = 92 cursor.getString(cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_COOKIE_DATA)); 93 mUserAgent = 94 cursor.getString(cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_USER_AGENT)); 95 mReferer = cursor.getString(cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_REFERER)); 96 mTotalBytes = 97 cursor.getInt(cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_TOTAL_BYTES)); 98 mCurrentBytes = 99 cursor.getInt(cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_CURRENT_BYTES)); 100 mETag = cursor.getString(cursor.getColumnIndexOrThrow(Constants.ETAG)); 101 mMediaScanned = cursor.getInt(cursor.getColumnIndexOrThrow(Constants.MEDIA_SCANNED)) == 1; 102 mFuzz = Helpers.sRandom.nextInt(1001); 103 104 readRequestHeaders(resolver, mId); 105 } 106 107 private void readRequestHeaders(ContentResolver resolver, long downloadId) { 108 Uri headerUri = Downloads.Impl.CONTENT_URI.buildUpon() 109 .appendPath(Long.toString(downloadId)) 110 .appendPath(Downloads.Impl.RequestHeaders.URI_SEGMENT).build(); 111 Cursor cursor = resolver.query(headerUri, null, null, null, null); 112 try { 113 int headerIndex = 114 cursor.getColumnIndexOrThrow(Downloads.Impl.RequestHeaders.COLUMN_HEADER); 115 int valueIndex = 116 cursor.getColumnIndexOrThrow(Downloads.Impl.RequestHeaders.COLUMN_VALUE); 117 for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) { 118 mRequestHeaders.put(cursor.getString(headerIndex), cursor.getString(valueIndex)); 119 } 120 } finally { 121 cursor.close(); 122 } 123 124 if (mCookies != null) { 125 mRequestHeaders.put("Cookie", mCookies); 126 } 127 if (mReferer != null) { 128 mRequestHeaders.put("Referer", mReferer); 129 } 130 } 131 132 public Map<String, String> getHeaders() { 133 return Collections.unmodifiableMap(mRequestHeaders); 134 } 135 136 public void sendIntentIfRequested(Uri contentUri, Context context) { 137 if (mPackage != null && mClass != null) { 138 Intent intent = new Intent(Downloads.Impl.ACTION_DOWNLOAD_COMPLETED); 139 intent.setClassName(mPackage, mClass); 140 if (mExtras != null) { 141 intent.putExtra(Downloads.Impl.COLUMN_NOTIFICATION_EXTRAS, mExtras); 142 } 143 // We only send the content: URI, for security reasons. Otherwise, malicious 144 // applications would have an easier time spoofing download results by 145 // sending spoofed intents. 146 intent.setData(contentUri); 147 context.sendBroadcast(intent); 148 } 149 } 150 151 /** 152 * Returns the time when a download should be restarted. Must only 153 * be called when numFailed > 0. 154 */ 155 public long restartTime() { 156 if (mRetryAfter > 0) { 157 return mLastMod + mRetryAfter; 158 } 159 return mLastMod + 160 Constants.RETRY_FIRST_DELAY * 161 (1000 + mFuzz) * (1 << (mNumFailed - 1)); 162 } 163 164 /** 165 * Returns whether this download (which the download manager hasn't seen yet) 166 * should be started. 167 */ 168 public boolean isReadyToStart(long now) { 169 if (mControl == Downloads.Impl.CONTROL_PAUSED) { 170 // the download is paused, so it's not going to start 171 return false; 172 } 173 if (mStatus == 0) { 174 // status hasn't been initialized yet, this is a new download 175 return true; 176 } 177 if (mStatus == Downloads.Impl.STATUS_PENDING) { 178 // download is explicit marked as ready to start 179 return true; 180 } 181 if (mStatus == Downloads.Impl.STATUS_RUNNING) { 182 // download was interrupted (process killed, loss of power) while it was running, 183 // without a chance to update the database 184 return true; 185 } 186 if (mStatus == Downloads.Impl.STATUS_RUNNING_PAUSED) { 187 if (mNumFailed == 0) { 188 // download is waiting for network connectivity to return before it can resume 189 return true; 190 } 191 if (restartTime() < now) { 192 // download was waiting for a delayed restart, and the delay has expired 193 return true; 194 } 195 } 196 return false; 197 } 198 199 /** 200 * Returns whether this download (which the download manager has already seen 201 * and therefore potentially started) should be restarted. 202 * 203 * In a nutshell, this returns true if the download isn't already running 204 * but should be, and it can know whether the download is already running 205 * by checking the status. 206 */ 207 public boolean isReadyToRestart(long now) { 208 if (mControl == Downloads.Impl.CONTROL_PAUSED) { 209 // the download is paused, so it's not going to restart 210 return false; 211 } 212 if (mStatus == 0) { 213 // download hadn't been initialized yet 214 return true; 215 } 216 if (mStatus == Downloads.Impl.STATUS_PENDING) { 217 // download is explicit marked as ready to start 218 return true; 219 } 220 if (mStatus == Downloads.Impl.STATUS_RUNNING_PAUSED) { 221 if (mNumFailed == 0) { 222 // download is waiting for network connectivity to return before it can resume 223 return true; 224 } 225 if (restartTime() < now) { 226 // download was waiting for a delayed restart, and the delay has expired 227 return true; 228 } 229 } 230 return false; 231 } 232 233 /** 234 * Returns whether this download has a visible notification after 235 * completion. 236 */ 237 public boolean hasCompletionNotification() { 238 if (!Downloads.Impl.isStatusCompleted(mStatus)) { 239 return false; 240 } 241 if (mVisibility == Downloads.Impl.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) { 242 return true; 243 } 244 return false; 245 } 246 247 /** 248 * Returns whether this download is allowed to use the network. 249 */ 250 public boolean canUseNetwork(boolean available, boolean roaming) { 251 if (!available) { 252 return false; 253 } 254 if (mDestination == Downloads.Impl.DESTINATION_CACHE_PARTITION_NOROAMING) { 255 return !roaming; 256 } else { 257 return true; 258 } 259 } 260} 261