am 49663f1f: am f20af912: Revert "Merge "bug:3341145 if ignore_size_limits flag set...
[android/platform/packages/providers/DownloadProvider.git] / src / com / android / providers / downloads / DownloadInfo.java
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
17 package com.android.providers.downloads;
18
19 import android.app.DownloadManager;
20 import android.content.ContentResolver;
21 import android.content.ContentUris;
22 import android.content.ContentValues;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.database.Cursor;
26 import android.drm.mobile1.DrmRawContent;
27 import android.net.ConnectivityManager;
28 import android.net.Uri;
29 import android.provider.Downloads;
30 import android.provider.Downloads.Impl;
31 import android.text.TextUtils;
32 import android.util.Log;
33 import android.util.Pair;
34
35 import java.util.ArrayList;
36 import java.util.Collection;
37 import java.util.Collections;
38 import java.util.List;
39
40 /**
41  * Stores information about an individual download.
42  */
43 public class DownloadInfo {
44     public static class Reader {
45         private ContentResolver mResolver;
46         private Cursor mCursor;
47
48         public Reader(ContentResolver resolver, Cursor cursor) {
49             mResolver = resolver;
50             mCursor = cursor;
51         }
52
53         public DownloadInfo newDownloadInfo(Context context, SystemFacade systemFacade) {
54             DownloadInfo info = new DownloadInfo(context, systemFacade);
55             updateFromDatabase(info);
56             readRequestHeaders(info);
57             return info;
58         }
59
60         public void updateFromDatabase(DownloadInfo info) {
61             info.mId = getLong(Downloads.Impl._ID);
62             info.mUri = getString(Downloads.Impl.COLUMN_URI);
63             info.mNoIntegrity = getInt(Downloads.Impl.COLUMN_NO_INTEGRITY) == 1;
64             info.mHint = getString(Downloads.Impl.COLUMN_FILE_NAME_HINT);
65             info.mFileName = getString(Downloads.Impl._DATA);
66             info.mMimeType = getString(Downloads.Impl.COLUMN_MIME_TYPE);
67             info.mDestination = getInt(Downloads.Impl.COLUMN_DESTINATION);
68             info.mVisibility = getInt(Downloads.Impl.COLUMN_VISIBILITY);
69             info.mStatus = getInt(Downloads.Impl.COLUMN_STATUS);
70             info.mNumFailed = getInt(Constants.FAILED_CONNECTIONS);
71             int retryRedirect = getInt(Constants.RETRY_AFTER_X_REDIRECT_COUNT);
72             info.mRetryAfter = retryRedirect & 0xfffffff;
73             info.mLastMod = getLong(Downloads.Impl.COLUMN_LAST_MODIFICATION);
74             info.mPackage = getString(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE);
75             info.mClass = getString(Downloads.Impl.COLUMN_NOTIFICATION_CLASS);
76             info.mExtras = getString(Downloads.Impl.COLUMN_NOTIFICATION_EXTRAS);
77             info.mCookies = getString(Downloads.Impl.COLUMN_COOKIE_DATA);
78             info.mUserAgent = getString(Downloads.Impl.COLUMN_USER_AGENT);
79             info.mReferer = getString(Downloads.Impl.COLUMN_REFERER);
80             info.mTotalBytes = getLong(Downloads.Impl.COLUMN_TOTAL_BYTES);
81             info.mCurrentBytes = getLong(Downloads.Impl.COLUMN_CURRENT_BYTES);
82             info.mETag = getString(Constants.ETAG);
83             info.mMediaScanned = getInt(Constants.MEDIA_SCANNED);
84             info.mDeleted = getInt(Downloads.Impl.COLUMN_DELETED) == 1;
85             info.mMediaProviderUri = getString(Downloads.Impl.COLUMN_MEDIAPROVIDER_URI);
86             info.mIsPublicApi = getInt(Downloads.Impl.COLUMN_IS_PUBLIC_API) != 0;
87             info.mAllowedNetworkTypes = getInt(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES);
88             info.mAllowRoaming = getInt(Downloads.Impl.COLUMN_ALLOW_ROAMING) != 0;
89             info.mTitle = getString(Downloads.Impl.COLUMN_TITLE);
90             info.mDescription = getString(Downloads.Impl.COLUMN_DESCRIPTION);
91             info.mBypassRecommendedSizeLimit =
92                     getInt(Downloads.Impl.COLUMN_BYPASS_RECOMMENDED_SIZE_LIMIT);
93             info.mOtaUpdate = getInt(Constants.OTA_UPDATE) == 1;
94
95             synchronized (this) {
96                 info.mControl = getInt(Downloads.Impl.COLUMN_CONTROL);
97             }
98         }
99
100         private void readRequestHeaders(DownloadInfo info) {
101             info.mRequestHeaders.clear();
102             Uri headerUri = Uri.withAppendedPath(
103                     info.getAllDownloadsUri(), Downloads.Impl.RequestHeaders.URI_SEGMENT);
104             Cursor cursor = mResolver.query(headerUri, null, null, null, null);
105             try {
106                 int headerIndex =
107                         cursor.getColumnIndexOrThrow(Downloads.Impl.RequestHeaders.COLUMN_HEADER);
108                 int valueIndex =
109                         cursor.getColumnIndexOrThrow(Downloads.Impl.RequestHeaders.COLUMN_VALUE);
110                 for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
111                     addHeader(info, cursor.getString(headerIndex), cursor.getString(valueIndex));
112                 }
113             } finally {
114                 cursor.close();
115             }
116
117             if (info.mCookies != null) {
118                 addHeader(info, "Cookie", info.mCookies);
119             }
120             if (info.mReferer != null) {
121                 addHeader(info, "Referer", info.mReferer);
122             }
123         }
124
125         private void addHeader(DownloadInfo info, String header, String value) {
126             info.mRequestHeaders.add(Pair.create(header, value));
127         }
128
129         private String getString(String column) {
130             int index = mCursor.getColumnIndexOrThrow(column);
131             String s = mCursor.getString(index);
132             return (TextUtils.isEmpty(s)) ? null : s;
133         }
134
135         private Integer getInt(String column) {
136             return mCursor.getInt(mCursor.getColumnIndexOrThrow(column));
137         }
138
139         private Long getLong(String column) {
140             return mCursor.getLong(mCursor.getColumnIndexOrThrow(column));
141         }
142     }
143
144     // the following NETWORK_* constants are used to indicates specfic reasons for disallowing a
145     // download from using a network, since specific causes can require special handling
146
147     /**
148      * The network is usable for the given download.
149      */
150     public static final int NETWORK_OK = 1;
151
152     /**
153      * There is no network connectivity.
154      */
155     public static final int NETWORK_NO_CONNECTION = 2;
156
157     /**
158      * The download exceeds the maximum size for this network.
159      */
160     public static final int NETWORK_UNUSABLE_DUE_TO_SIZE = 3;
161
162     /**
163      * The download exceeds the recommended maximum size for this network, the user must confirm for
164      * this download to proceed without WiFi.
165      */
166     public static final int NETWORK_RECOMMENDED_UNUSABLE_DUE_TO_SIZE = 4;
167
168     /**
169      * The current connection is roaming, and the download can't proceed over a roaming connection.
170      */
171     public static final int NETWORK_CANNOT_USE_ROAMING = 5;
172
173     /**
174      * The app requesting the download specific that it can't use the current network connection.
175      */
176     public static final int NETWORK_TYPE_DISALLOWED_BY_REQUESTOR = 6;
177
178     /**
179      * For intents used to notify the user that a download exceeds a size threshold, if this extra
180      * is true, WiFi is required for this download size; otherwise, it is only recommended.
181      */
182     public static final String EXTRA_IS_WIFI_REQUIRED = "isWifiRequired";
183
184
185     public long mId;
186     public String mUri;
187     public boolean mNoIntegrity;
188     public String mHint;
189     public String mFileName;
190     public String mMimeType;
191     public int mDestination;
192     public int mVisibility;
193     public int mControl;
194     public int mStatus;
195     public int mNumFailed;
196     public int mRetryAfter;
197     public long mLastMod;
198     public String mPackage;
199     public String mClass;
200     public String mExtras;
201     public String mCookies;
202     public String mUserAgent;
203     public String mReferer;
204     public long mTotalBytes;
205     public long mCurrentBytes;
206     public String mETag;
207     public int mMediaScanned;
208     public boolean mDeleted;
209     public String mMediaProviderUri;
210     public boolean mIsPublicApi;
211     public int mAllowedNetworkTypes;
212     public boolean mAllowRoaming;
213     public String mTitle;
214     public String mDescription;
215     public int mBypassRecommendedSizeLimit;
216     public boolean mOtaUpdate;
217
218     public int mFuzz;
219
220     public volatile boolean mHasActiveThread;
221
222     private List<Pair<String, String>> mRequestHeaders = new ArrayList<Pair<String, String>>();
223     private SystemFacade mSystemFacade;
224     private Context mContext;
225
226     private DownloadInfo(Context context, SystemFacade systemFacade) {
227         mContext = context;
228         mSystemFacade = systemFacade;
229         mFuzz = Helpers.sRandom.nextInt(1001);
230     }
231
232     public Collection<Pair<String, String>> getHeaders() {
233         return Collections.unmodifiableList(mRequestHeaders);
234     }
235
236     public void sendIntentIfRequested() {
237         if (mPackage == null) {
238             return;
239         }
240
241         Intent intent;
242         if (mIsPublicApi) {
243             intent = new Intent(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
244             intent.setPackage(mPackage);
245             intent.putExtra(DownloadManager.EXTRA_DOWNLOAD_ID, mId);
246         } else { // legacy behavior
247             if (mClass == null) {
248                 return;
249             }
250             intent = new Intent(Downloads.Impl.ACTION_DOWNLOAD_COMPLETED);
251             intent.setClassName(mPackage, mClass);
252             if (mExtras != null) {
253                 intent.putExtra(Downloads.Impl.COLUMN_NOTIFICATION_EXTRAS, mExtras);
254             }
255             // We only send the content: URI, for security reasons. Otherwise, malicious
256             //     applications would have an easier time spoofing download results by
257             //     sending spoofed intents.
258             intent.setData(getMyDownloadsUri());
259         }
260         mSystemFacade.sendBroadcast(intent);
261     }
262
263     /**
264      * Returns the time when a download should be restarted.
265      */
266     public long restartTime(long now) {
267         if (mNumFailed == 0) {
268             return now;
269         }
270         if (mRetryAfter > 0) {
271             return mLastMod + mRetryAfter;
272         }
273         return mLastMod +
274                 Constants.RETRY_FIRST_DELAY *
275                     (1000 + mFuzz) * (1 << (mNumFailed - 1));
276     }
277
278     /**
279      * Returns whether this download (which the download manager hasn't seen yet)
280      * should be started.
281      */
282     private boolean isReadyToStart(long now) {
283         if (mHasActiveThread) {
284             // already running
285             return false;
286         }
287         if (mControl == Downloads.Impl.CONTROL_PAUSED) {
288             // the download is paused, so it's not going to start
289             return false;
290         }
291         switch (mStatus) {
292             case 0: // status hasn't been initialized yet, this is a new download
293             case Downloads.Impl.STATUS_PENDING: // download is explicit marked as ready to start
294             case Downloads.Impl.STATUS_RUNNING: // download interrupted (process killed etc) while
295                                                 // running, without a chance to update the database
296                 return true;
297
298             case Downloads.Impl.STATUS_WAITING_FOR_NETWORK:
299             case Downloads.Impl.STATUS_QUEUED_FOR_WIFI:
300                 return checkCanUseNetwork() == NETWORK_OK;
301
302             case Downloads.Impl.STATUS_WAITING_TO_RETRY:
303                 // download was waiting for a delayed restart
304                 return restartTime(now) <= now;
305         }
306         return false;
307     }
308
309     /**
310      * Returns whether this download has a visible notification after
311      * completion.
312      */
313     public boolean hasCompletionNotification() {
314         if (!Downloads.Impl.isStatusCompleted(mStatus)) {
315             return false;
316         }
317         if (mVisibility == Downloads.Impl.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) {
318             return true;
319         }
320         return false;
321     }
322
323     /**
324      * Returns whether this download is allowed to use the network.
325      * @return one of the NETWORK_* constants
326      */
327     public int checkCanUseNetwork() {
328         Integer networkType = mSystemFacade.getActiveNetworkType();
329         if (networkType == null) {
330             return NETWORK_NO_CONNECTION;
331         }
332         if (!isRoamingAllowed() && mSystemFacade.isNetworkRoaming()) {
333             return NETWORK_CANNOT_USE_ROAMING;
334         }
335         return checkIsNetworkTypeAllowed(networkType);
336     }
337
338     private boolean isRoamingAllowed() {
339         if (mIsPublicApi) {
340             return mAllowRoaming;
341         } else { // legacy behavior
342             return mDestination != Downloads.Impl.DESTINATION_CACHE_PARTITION_NOROAMING;
343         }
344     }
345
346     /**
347      * @return a non-localized string appropriate for logging corresponding to one of the
348      * NETWORK_* constants.
349      */
350     public String getLogMessageForNetworkError(int networkError) {
351         switch (networkError) {
352             case NETWORK_RECOMMENDED_UNUSABLE_DUE_TO_SIZE:
353                 return "download size exceeds recommended limit for mobile network";
354
355             case NETWORK_UNUSABLE_DUE_TO_SIZE:
356                 return "download size exceeds limit for mobile network";
357
358             case NETWORK_NO_CONNECTION:
359                 return "no network connection available";
360
361             case NETWORK_CANNOT_USE_ROAMING:
362                 return "download cannot use the current network connection because it is roaming";
363
364             case NETWORK_TYPE_DISALLOWED_BY_REQUESTOR:
365                 return "download was requested to not use the current network type";
366
367             default:
368                 return "unknown error with network connectivity";
369         }
370     }
371
372     /**
373      * Check if this download can proceed over the given network type.
374      * @param networkType a constant from ConnectivityManager.TYPE_*.
375      * @return one of the NETWORK_* constants
376      */
377     private int checkIsNetworkTypeAllowed(int networkType) {
378         if (mIsPublicApi) {
379             int flag = translateNetworkTypeToApiFlag(networkType);
380             if ((flag & mAllowedNetworkTypes) == 0) {
381                 return NETWORK_TYPE_DISALLOWED_BY_REQUESTOR;
382             }
383         }
384         return checkSizeAllowedForNetwork(networkType);
385     }
386
387     /**
388      * Translate a ConnectivityManager.TYPE_* constant to the corresponding
389      * DownloadManager.Request.NETWORK_* bit flag.
390      */
391     private int translateNetworkTypeToApiFlag(int networkType) {
392         switch (networkType) {
393             case ConnectivityManager.TYPE_MOBILE:
394                 return DownloadManager.Request.NETWORK_MOBILE;
395
396             case ConnectivityManager.TYPE_WIFI:
397                 return DownloadManager.Request.NETWORK_WIFI;
398
399             default:
400                 return 0;
401         }
402     }
403
404     /**
405      * Check if the download's size prohibits it from running over the current network.
406      * @return one of the NETWORK_* constants
407      */
408     private int checkSizeAllowedForNetwork(int networkType) {
409         if (mTotalBytes <= 0) {
410             return NETWORK_OK; // we don't know the size yet
411         }
412         if (networkType == ConnectivityManager.TYPE_WIFI) {
413             return NETWORK_OK; // anything goes over wifi
414         }
415         if (mOtaUpdate) {
416             // OTA update download can use mobile network with no size limits
417             return NETWORK_OK;
418         }
419         Long maxBytesOverMobile = mSystemFacade.getMaxBytesOverMobile();
420         if (maxBytesOverMobile != null && mTotalBytes > maxBytesOverMobile) {
421             return NETWORK_UNUSABLE_DUE_TO_SIZE;
422         }
423         if (mBypassRecommendedSizeLimit == 0) {
424             Long recommendedMaxBytesOverMobile = mSystemFacade.getRecommendedMaxBytesOverMobile();
425             if (recommendedMaxBytesOverMobile != null
426                     && mTotalBytes > recommendedMaxBytesOverMobile) {
427                 return NETWORK_RECOMMENDED_UNUSABLE_DUE_TO_SIZE;
428             }
429         }
430         return NETWORK_OK;
431     }
432
433     void startIfReady(long now, StorageManager storageManager) {
434         if (!isReadyToStart(now)) {
435             return;
436         }
437
438         if (Constants.LOGV) {
439             Log.v(Constants.TAG, "Service spawning thread to handle download " + mId);
440         }
441         if (mHasActiveThread) {
442             throw new IllegalStateException("Multiple threads on same download");
443         }
444         if (mStatus != Impl.STATUS_RUNNING) {
445             mStatus = Impl.STATUS_RUNNING;
446             ContentValues values = new ContentValues();
447             values.put(Impl.COLUMN_STATUS, mStatus);
448             mContext.getContentResolver().update(getAllDownloadsUri(), values, null, null);
449         }
450         DownloadThread downloader = new DownloadThread(mContext, mSystemFacade, this,
451                 storageManager);
452         mHasActiveThread = true;
453         mSystemFacade.startThread(downloader);
454     }
455
456     public boolean isOnCache() {
457         return (mDestination == Downloads.Impl.DESTINATION_CACHE_PARTITION
458                 || mDestination == Downloads.Impl.DESTINATION_SYSTEMCACHE_PARTITION
459                 || mDestination == Downloads.Impl.DESTINATION_CACHE_PARTITION_NOROAMING
460                 || mDestination == Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE);
461     }
462
463     public Uri getMyDownloadsUri() {
464         return ContentUris.withAppendedId(Downloads.Impl.CONTENT_URI, mId);
465     }
466
467     public Uri getAllDownloadsUri() {
468         return ContentUris.withAppendedId(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, mId);
469     }
470
471
472     public void logVerboseInfo() {
473         Log.v(Constants.TAG, "Service adding new entry");
474         Log.v(Constants.TAG, "ID      : " + mId);
475         Log.v(Constants.TAG, "URI     : " + ((mUri != null) ? "yes" : "no"));
476         Log.v(Constants.TAG, "NO_INTEG: " + mNoIntegrity);
477         Log.v(Constants.TAG, "HINT    : " + mHint);
478         Log.v(Constants.TAG, "FILENAME: " + mFileName);
479         Log.v(Constants.TAG, "MIMETYPE: " + mMimeType);
480         Log.v(Constants.TAG, "DESTINAT: " + mDestination);
481         Log.v(Constants.TAG, "VISIBILI: " + mVisibility);
482         Log.v(Constants.TAG, "CONTROL : " + mControl);
483         Log.v(Constants.TAG, "STATUS  : " + mStatus);
484         Log.v(Constants.TAG, "FAILED_C: " + mNumFailed);
485         Log.v(Constants.TAG, "RETRY_AF: " + mRetryAfter);
486         Log.v(Constants.TAG, "LAST_MOD: " + mLastMod);
487         Log.v(Constants.TAG, "PACKAGE : " + mPackage);
488         Log.v(Constants.TAG, "CLASS   : " + mClass);
489         Log.v(Constants.TAG, "COOKIES : " + ((mCookies != null) ? "yes" : "no"));
490         Log.v(Constants.TAG, "AGENT   : " + mUserAgent);
491         Log.v(Constants.TAG, "REFERER : " + ((mReferer != null) ? "yes" : "no"));
492         Log.v(Constants.TAG, "TOTAL   : " + mTotalBytes);
493         Log.v(Constants.TAG, "CURRENT : " + mCurrentBytes);
494         Log.v(Constants.TAG, "ETAG    : " + mETag);
495         Log.v(Constants.TAG, "SCANNED : " + mMediaScanned);
496         Log.v(Constants.TAG, "DELETED : " + mDeleted);
497         Log.v(Constants.TAG, "MEDIAPROVIDER_URI : " + mMediaProviderUri);
498     }
499
500     /**
501      * Returns the amount of time (as measured from the "now" parameter)
502      * at which a download will be active.
503      * 0 = immediately - service should stick around to handle this download.
504      * -1 = never - service can go away without ever waking up.
505      * positive value - service must wake up in the future, as specified in ms from "now"
506      */
507     long nextAction(long now) {
508         if (Downloads.Impl.isStatusCompleted(mStatus)) {
509             return -1;
510         }
511         if (mStatus != Downloads.Impl.STATUS_WAITING_TO_RETRY) {
512             return 0;
513         }
514         long when = restartTime(now);
515         if (when <= now) {
516             return 0;
517         }
518         return when - now;
519     }
520
521     /**
522      * Returns whether a file should be scanned
523      */
524     boolean shouldScanFile() {
525         return (mMediaScanned == 0)
526                 && (mDestination == Downloads.Impl.DESTINATION_EXTERNAL ||
527                         mDestination == Downloads.Impl.DESTINATION_FILE_URI ||
528                         mDestination == Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD)
529                 && Downloads.Impl.isStatusSuccess(mStatus)
530                 && !DrmRawContent.DRM_MIMETYPE_MESSAGE_STRING.equalsIgnoreCase(mMimeType);
531     }
532
533     void notifyPauseDueToSize(boolean isWifiRequired) {
534         Intent intent = new Intent(Intent.ACTION_VIEW);
535         intent.setData(getAllDownloadsUri());
536         intent.setClassName(SizeLimitActivity.class.getPackage().getName(),
537                 SizeLimitActivity.class.getName());
538         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
539         intent.putExtra(EXTRA_IS_WIFI_REQUIRED, isWifiRequired);
540         mContext.startActivity(intent);
541     }
542 }