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