• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 static com.android.providers.downloads.Constants.TAG;
20 
21 import android.app.DownloadManager;
22 import android.content.ContentResolver;
23 import android.content.ContentUris;
24 import android.content.ContentValues;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.database.Cursor;
28 import android.net.ConnectivityManager;
29 import android.net.NetworkInfo;
30 import android.net.NetworkInfo.DetailedState;
31 import android.net.Uri;
32 import android.os.Environment;
33 import android.provider.Downloads;
34 import android.provider.Downloads.Impl;
35 import android.text.TextUtils;
36 import android.util.Log;
37 import android.util.Pair;
38 
39 import com.android.internal.annotations.GuardedBy;
40 import com.android.internal.util.IndentingPrintWriter;
41 
42 import java.io.CharArrayWriter;
43 import java.io.File;
44 import java.util.ArrayList;
45 import java.util.Collection;
46 import java.util.Collections;
47 import java.util.List;
48 import java.util.concurrent.Executor;
49 import java.util.concurrent.ExecutorService;
50 import java.util.concurrent.Future;
51 
52 /**
53  * Details about a specific download. Fields should only be mutated by updating
54  * from database query.
55  */
56 public class DownloadInfo {
57     // TODO: move towards these in-memory objects being sources of truth, and
58     // periodically pushing to provider.
59 
60     public static class Reader {
61         private ContentResolver mResolver;
62         private Cursor mCursor;
63 
Reader(ContentResolver resolver, Cursor cursor)64         public Reader(ContentResolver resolver, Cursor cursor) {
65             mResolver = resolver;
66             mCursor = cursor;
67         }
68 
newDownloadInfo( Context context, SystemFacade systemFacade, DownloadNotifier notifier)69         public DownloadInfo newDownloadInfo(
70                 Context context, SystemFacade systemFacade, DownloadNotifier notifier) {
71             final DownloadInfo info = new DownloadInfo(context, systemFacade, notifier);
72             updateFromDatabase(info);
73             readRequestHeaders(info);
74             return info;
75         }
76 
updateFromDatabase(DownloadInfo info)77         public void updateFromDatabase(DownloadInfo info) {
78             info.mId = getLong(Downloads.Impl._ID);
79             info.mUri = getString(Downloads.Impl.COLUMN_URI);
80             info.mNoIntegrity = getInt(Downloads.Impl.COLUMN_NO_INTEGRITY) == 1;
81             info.mHint = getString(Downloads.Impl.COLUMN_FILE_NAME_HINT);
82             info.mFileName = getString(Downloads.Impl._DATA);
83             info.mMimeType = Intent.normalizeMimeType(getString(Downloads.Impl.COLUMN_MIME_TYPE));
84             info.mDestination = getInt(Downloads.Impl.COLUMN_DESTINATION);
85             info.mVisibility = getInt(Downloads.Impl.COLUMN_VISIBILITY);
86             info.mStatus = getInt(Downloads.Impl.COLUMN_STATUS);
87             info.mNumFailed = getInt(Downloads.Impl.COLUMN_FAILED_CONNECTIONS);
88             int retryRedirect = getInt(Constants.RETRY_AFTER_X_REDIRECT_COUNT);
89             info.mRetryAfter = retryRedirect & 0xfffffff;
90             info.mLastMod = getLong(Downloads.Impl.COLUMN_LAST_MODIFICATION);
91             info.mPackage = getString(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE);
92             info.mClass = getString(Downloads.Impl.COLUMN_NOTIFICATION_CLASS);
93             info.mExtras = getString(Downloads.Impl.COLUMN_NOTIFICATION_EXTRAS);
94             info.mCookies = getString(Downloads.Impl.COLUMN_COOKIE_DATA);
95             info.mUserAgent = getString(Downloads.Impl.COLUMN_USER_AGENT);
96             info.mReferer = getString(Downloads.Impl.COLUMN_REFERER);
97             info.mTotalBytes = getLong(Downloads.Impl.COLUMN_TOTAL_BYTES);
98             info.mCurrentBytes = getLong(Downloads.Impl.COLUMN_CURRENT_BYTES);
99             info.mETag = getString(Constants.ETAG);
100             info.mUid = getInt(Constants.UID);
101             info.mMediaScanned = getInt(Downloads.Impl.COLUMN_MEDIA_SCANNED);
102             info.mDeleted = getInt(Downloads.Impl.COLUMN_DELETED) == 1;
103             info.mMediaProviderUri = getString(Downloads.Impl.COLUMN_MEDIAPROVIDER_URI);
104             info.mIsPublicApi = getInt(Downloads.Impl.COLUMN_IS_PUBLIC_API) != 0;
105             info.mAllowedNetworkTypes = getInt(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES);
106             info.mAllowRoaming = getInt(Downloads.Impl.COLUMN_ALLOW_ROAMING) != 0;
107             info.mAllowMetered = getInt(Downloads.Impl.COLUMN_ALLOW_METERED) != 0;
108             info.mTitle = getString(Downloads.Impl.COLUMN_TITLE);
109             info.mDescription = getString(Downloads.Impl.COLUMN_DESCRIPTION);
110             info.mBypassRecommendedSizeLimit =
111                     getInt(Downloads.Impl.COLUMN_BYPASS_RECOMMENDED_SIZE_LIMIT);
112 
113             synchronized (this) {
114                 info.mControl = getInt(Downloads.Impl.COLUMN_CONTROL);
115             }
116         }
117 
readRequestHeaders(DownloadInfo info)118         private void readRequestHeaders(DownloadInfo info) {
119             info.mRequestHeaders.clear();
120             Uri headerUri = Uri.withAppendedPath(
121                     info.getAllDownloadsUri(), Downloads.Impl.RequestHeaders.URI_SEGMENT);
122             Cursor cursor = mResolver.query(headerUri, null, null, null, null);
123             try {
124                 int headerIndex =
125                         cursor.getColumnIndexOrThrow(Downloads.Impl.RequestHeaders.COLUMN_HEADER);
126                 int valueIndex =
127                         cursor.getColumnIndexOrThrow(Downloads.Impl.RequestHeaders.COLUMN_VALUE);
128                 for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
129                     addHeader(info, cursor.getString(headerIndex), cursor.getString(valueIndex));
130                 }
131             } finally {
132                 cursor.close();
133             }
134 
135             if (info.mCookies != null) {
136                 addHeader(info, "Cookie", info.mCookies);
137             }
138             if (info.mReferer != null) {
139                 addHeader(info, "Referer", info.mReferer);
140             }
141         }
142 
addHeader(DownloadInfo info, String header, String value)143         private void addHeader(DownloadInfo info, String header, String value) {
144             info.mRequestHeaders.add(Pair.create(header, value));
145         }
146 
getString(String column)147         private String getString(String column) {
148             int index = mCursor.getColumnIndexOrThrow(column);
149             String s = mCursor.getString(index);
150             return (TextUtils.isEmpty(s)) ? null : s;
151         }
152 
getInt(String column)153         private Integer getInt(String column) {
154             return mCursor.getInt(mCursor.getColumnIndexOrThrow(column));
155         }
156 
getLong(String column)157         private Long getLong(String column) {
158             return mCursor.getLong(mCursor.getColumnIndexOrThrow(column));
159         }
160     }
161 
162     /**
163      * Constants used to indicate network state for a specific download, after
164      * applying any requested constraints.
165      */
166     public enum NetworkState {
167         /**
168          * The network is usable for the given download.
169          */
170         OK,
171 
172         /**
173          * There is no network connectivity.
174          */
175         NO_CONNECTION,
176 
177         /**
178          * The download exceeds the maximum size for this network.
179          */
180         UNUSABLE_DUE_TO_SIZE,
181 
182         /**
183          * The download exceeds the recommended maximum size for this network,
184          * the user must confirm for this download to proceed without WiFi.
185          */
186         RECOMMENDED_UNUSABLE_DUE_TO_SIZE,
187 
188         /**
189          * The current connection is roaming, and the download can't proceed
190          * over a roaming connection.
191          */
192         CANNOT_USE_ROAMING,
193 
194         /**
195          * The app requesting the download specific that it can't use the
196          * current network connection.
197          */
198         TYPE_DISALLOWED_BY_REQUESTOR,
199 
200         /**
201          * Current network is blocked for requesting application.
202          */
203         BLOCKED;
204     }
205 
206     /**
207      * For intents used to notify the user that a download exceeds a size threshold, if this extra
208      * is true, WiFi is required for this download size; otherwise, it is only recommended.
209      */
210     public static final String EXTRA_IS_WIFI_REQUIRED = "isWifiRequired";
211 
212     public long mId;
213     public String mUri;
214     @Deprecated
215     public boolean mNoIntegrity;
216     public String mHint;
217     public String mFileName;
218     public String mMimeType;
219     public int mDestination;
220     public int mVisibility;
221     public int mControl;
222     public int mStatus;
223     public int mNumFailed;
224     public int mRetryAfter;
225     public long mLastMod;
226     public String mPackage;
227     public String mClass;
228     public String mExtras;
229     public String mCookies;
230     public String mUserAgent;
231     public String mReferer;
232     public long mTotalBytes;
233     public long mCurrentBytes;
234     public String mETag;
235     public int mUid;
236     public int mMediaScanned;
237     public boolean mDeleted;
238     public String mMediaProviderUri;
239     public boolean mIsPublicApi;
240     public int mAllowedNetworkTypes;
241     public boolean mAllowRoaming;
242     public boolean mAllowMetered;
243     public String mTitle;
244     public String mDescription;
245     public int mBypassRecommendedSizeLimit;
246 
247     public int mFuzz;
248 
249     private List<Pair<String, String>> mRequestHeaders = new ArrayList<Pair<String, String>>();
250 
251     /**
252      * Result of last {@link DownloadThread} started by
253      * {@link #startDownloadIfReady(ExecutorService)}.
254      */
255     @GuardedBy("this")
256     private Future<?> mSubmittedTask;
257 
258     @GuardedBy("this")
259     private DownloadThread mTask;
260 
261     private final Context mContext;
262     private final SystemFacade mSystemFacade;
263     private final DownloadNotifier mNotifier;
264 
DownloadInfo(Context context, SystemFacade systemFacade, DownloadNotifier notifier)265     private DownloadInfo(Context context, SystemFacade systemFacade, DownloadNotifier notifier) {
266         mContext = context;
267         mSystemFacade = systemFacade;
268         mNotifier = notifier;
269         mFuzz = Helpers.sRandom.nextInt(1001);
270     }
271 
getHeaders()272     public Collection<Pair<String, String>> getHeaders() {
273         return Collections.unmodifiableList(mRequestHeaders);
274     }
275 
getUserAgent()276     public String getUserAgent() {
277         if (mUserAgent != null) {
278             return mUserAgent;
279         } else {
280             return Constants.DEFAULT_USER_AGENT;
281         }
282     }
283 
sendIntentIfRequested()284     public void sendIntentIfRequested() {
285         if (mPackage == null) {
286             return;
287         }
288 
289         Intent intent;
290         if (mIsPublicApi) {
291             intent = new Intent(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
292             intent.setPackage(mPackage);
293             intent.putExtra(DownloadManager.EXTRA_DOWNLOAD_ID, mId);
294         } else { // legacy behavior
295             if (mClass == null) {
296                 return;
297             }
298             intent = new Intent(Downloads.Impl.ACTION_DOWNLOAD_COMPLETED);
299             intent.setClassName(mPackage, mClass);
300             if (mExtras != null) {
301                 intent.putExtra(Downloads.Impl.COLUMN_NOTIFICATION_EXTRAS, mExtras);
302             }
303             // We only send the content: URI, for security reasons. Otherwise, malicious
304             //     applications would have an easier time spoofing download results by
305             //     sending spoofed intents.
306             intent.setData(getMyDownloadsUri());
307         }
308         mSystemFacade.sendBroadcast(intent);
309     }
310 
311     /**
312      * Returns the time when a download should be restarted.
313      */
restartTime(long now)314     public long restartTime(long now) {
315         if (mNumFailed == 0) {
316             return now;
317         }
318         if (mRetryAfter > 0) {
319             return mLastMod + mRetryAfter;
320         }
321         return mLastMod +
322                 Constants.RETRY_FIRST_DELAY *
323                     (1000 + mFuzz) * (1 << (mNumFailed - 1));
324     }
325 
326     /**
327      * Returns whether this download should be enqueued.
328      */
isReadyToDownload()329     private boolean isReadyToDownload() {
330         if (mControl == Downloads.Impl.CONTROL_PAUSED) {
331             // the download is paused, so it's not going to start
332             return false;
333         }
334         switch (mStatus) {
335             case 0: // status hasn't been initialized yet, this is a new download
336             case Downloads.Impl.STATUS_PENDING: // download is explicit marked as ready to start
337             case Downloads.Impl.STATUS_RUNNING: // download interrupted (process killed etc) while
338                                                 // running, without a chance to update the database
339                 return true;
340 
341             case Downloads.Impl.STATUS_WAITING_FOR_NETWORK:
342             case Downloads.Impl.STATUS_QUEUED_FOR_WIFI:
343                 return checkCanUseNetwork(mTotalBytes) == NetworkState.OK;
344 
345             case Downloads.Impl.STATUS_WAITING_TO_RETRY:
346                 // download was waiting for a delayed restart
347                 final long now = mSystemFacade.currentTimeMillis();
348                 return restartTime(now) <= now;
349             case Downloads.Impl.STATUS_DEVICE_NOT_FOUND_ERROR:
350                 // is the media mounted?
351                 final Uri uri = Uri.parse(mUri);
352                 if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
353                     final File file = new File(uri.getPath());
354                     return Environment.MEDIA_MOUNTED
355                             .equals(Environment.getExternalStorageState(file));
356                 } else {
357                     Log.w(TAG, "Expected file URI on external storage: " + mUri);
358                     return false;
359                 }
360             case Downloads.Impl.STATUS_INSUFFICIENT_SPACE_ERROR:
361                 // avoids repetition of retrying download
362                 return false;
363         }
364         return false;
365     }
366 
367     /**
368      * Returns whether this download has a visible notification after
369      * completion.
370      */
hasCompletionNotification()371     public boolean hasCompletionNotification() {
372         if (!Downloads.Impl.isStatusCompleted(mStatus)) {
373             return false;
374         }
375         if (mVisibility == Downloads.Impl.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) {
376             return true;
377         }
378         return false;
379     }
380 
381     /**
382      * Returns whether this download is allowed to use the network.
383      */
checkCanUseNetwork(long totalBytes)384     public NetworkState checkCanUseNetwork(long totalBytes) {
385         final NetworkInfo info = mSystemFacade.getActiveNetworkInfo(mUid);
386         if (info == null || !info.isConnected()) {
387             return NetworkState.NO_CONNECTION;
388         }
389         if (DetailedState.BLOCKED.equals(info.getDetailedState())) {
390             return NetworkState.BLOCKED;
391         }
392         if (mSystemFacade.isNetworkRoaming() && !isRoamingAllowed()) {
393             return NetworkState.CANNOT_USE_ROAMING;
394         }
395         if (mSystemFacade.isActiveNetworkMetered() && !mAllowMetered) {
396             return NetworkState.TYPE_DISALLOWED_BY_REQUESTOR;
397         }
398         return checkIsNetworkTypeAllowed(info.getType(), totalBytes);
399     }
400 
isRoamingAllowed()401     private boolean isRoamingAllowed() {
402         if (mIsPublicApi) {
403             return mAllowRoaming;
404         } else { // legacy behavior
405             return mDestination != Downloads.Impl.DESTINATION_CACHE_PARTITION_NOROAMING;
406         }
407     }
408 
409     /**
410      * Check if this download can proceed over the given network type.
411      * @param networkType a constant from ConnectivityManager.TYPE_*.
412      * @return one of the NETWORK_* constants
413      */
checkIsNetworkTypeAllowed(int networkType, long totalBytes)414     private NetworkState checkIsNetworkTypeAllowed(int networkType, long totalBytes) {
415         if (mIsPublicApi) {
416             final int flag = translateNetworkTypeToApiFlag(networkType);
417             final boolean allowAllNetworkTypes = mAllowedNetworkTypes == ~0;
418             if (!allowAllNetworkTypes && (flag & mAllowedNetworkTypes) == 0) {
419                 return NetworkState.TYPE_DISALLOWED_BY_REQUESTOR;
420             }
421         }
422         return checkSizeAllowedForNetwork(networkType, totalBytes);
423     }
424 
425     /**
426      * Translate a ConnectivityManager.TYPE_* constant to the corresponding
427      * DownloadManager.Request.NETWORK_* bit flag.
428      */
translateNetworkTypeToApiFlag(int networkType)429     private int translateNetworkTypeToApiFlag(int networkType) {
430         switch (networkType) {
431             case ConnectivityManager.TYPE_MOBILE:
432                 return DownloadManager.Request.NETWORK_MOBILE;
433 
434             case ConnectivityManager.TYPE_WIFI:
435                 return DownloadManager.Request.NETWORK_WIFI;
436 
437             case ConnectivityManager.TYPE_BLUETOOTH:
438                 return DownloadManager.Request.NETWORK_BLUETOOTH;
439 
440             default:
441                 return 0;
442         }
443     }
444 
445     /**
446      * Check if the download's size prohibits it from running over the current network.
447      * @return one of the NETWORK_* constants
448      */
checkSizeAllowedForNetwork(int networkType, long totalBytes)449     private NetworkState checkSizeAllowedForNetwork(int networkType, long totalBytes) {
450         if (totalBytes <= 0) {
451             // we don't know the size yet
452             return NetworkState.OK;
453         }
454 
455         if (ConnectivityManager.isNetworkTypeMobile(networkType)) {
456             Long maxBytesOverMobile = mSystemFacade.getMaxBytesOverMobile();
457             if (maxBytesOverMobile != null && totalBytes > maxBytesOverMobile) {
458                 return NetworkState.UNUSABLE_DUE_TO_SIZE;
459             }
460             if (mBypassRecommendedSizeLimit == 0) {
461                 Long recommendedMaxBytesOverMobile = mSystemFacade
462                         .getRecommendedMaxBytesOverMobile();
463                 if (recommendedMaxBytesOverMobile != null
464                         && totalBytes > recommendedMaxBytesOverMobile) {
465                     return NetworkState.RECOMMENDED_UNUSABLE_DUE_TO_SIZE;
466                 }
467             }
468         }
469 
470         return NetworkState.OK;
471     }
472 
473     /**
474      * If download is ready to start, and isn't already pending or executing,
475      * create a {@link DownloadThread} and enqueue it into given
476      * {@link Executor}.
477      *
478      * @return If actively downloading.
479      */
startDownloadIfReady(ExecutorService executor)480     public boolean startDownloadIfReady(ExecutorService executor) {
481         synchronized (this) {
482             final boolean isReady = isReadyToDownload();
483             final boolean isActive = mSubmittedTask != null && !mSubmittedTask.isDone();
484             if (isReady && !isActive) {
485                 if (mStatus != Impl.STATUS_RUNNING) {
486                     mStatus = Impl.STATUS_RUNNING;
487                     ContentValues values = new ContentValues();
488                     values.put(Impl.COLUMN_STATUS, mStatus);
489                     mContext.getContentResolver().update(getAllDownloadsUri(), values, null, null);
490                 }
491 
492                 mTask = new DownloadThread(mContext, mSystemFacade, mNotifier, this);
493                 mSubmittedTask = executor.submit(mTask);
494             }
495             return isReady;
496         }
497     }
498 
499     /**
500      * If download is ready to be scanned, enqueue it into the given
501      * {@link DownloadScanner}.
502      *
503      * @return If actively scanning.
504      */
startScanIfReady(DownloadScanner scanner)505     public boolean startScanIfReady(DownloadScanner scanner) {
506         synchronized (this) {
507             final boolean isReady = shouldScanFile();
508             if (isReady) {
509                 scanner.requestScan(this);
510             }
511             return isReady;
512         }
513     }
514 
isOnCache()515     public boolean isOnCache() {
516         return (mDestination == Downloads.Impl.DESTINATION_CACHE_PARTITION
517                 || mDestination == Downloads.Impl.DESTINATION_SYSTEMCACHE_PARTITION
518                 || mDestination == Downloads.Impl.DESTINATION_CACHE_PARTITION_NOROAMING
519                 || mDestination == Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE);
520     }
521 
getMyDownloadsUri()522     public Uri getMyDownloadsUri() {
523         return ContentUris.withAppendedId(Downloads.Impl.CONTENT_URI, mId);
524     }
525 
getAllDownloadsUri()526     public Uri getAllDownloadsUri() {
527         return ContentUris.withAppendedId(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, mId);
528     }
529 
530     @Override
toString()531     public String toString() {
532         final CharArrayWriter writer = new CharArrayWriter();
533         dump(new IndentingPrintWriter(writer, "  "));
534         return writer.toString();
535     }
536 
dump(IndentingPrintWriter pw)537     public void dump(IndentingPrintWriter pw) {
538         pw.println("DownloadInfo:");
539         pw.increaseIndent();
540 
541         pw.printPair("mId", mId);
542         pw.printPair("mLastMod", mLastMod);
543         pw.printPair("mPackage", mPackage);
544         pw.printPair("mUid", mUid);
545         pw.println();
546 
547         pw.printPair("mUri", mUri);
548         pw.println();
549 
550         pw.printPair("mMimeType", mMimeType);
551         pw.printPair("mCookies", (mCookies != null) ? "yes" : "no");
552         pw.printPair("mReferer", (mReferer != null) ? "yes" : "no");
553         pw.printPair("mUserAgent", mUserAgent);
554         pw.println();
555 
556         pw.printPair("mFileName", mFileName);
557         pw.printPair("mDestination", mDestination);
558         pw.println();
559 
560         pw.printPair("mStatus", Downloads.Impl.statusToString(mStatus));
561         pw.printPair("mCurrentBytes", mCurrentBytes);
562         pw.printPair("mTotalBytes", mTotalBytes);
563         pw.println();
564 
565         pw.printPair("mNumFailed", mNumFailed);
566         pw.printPair("mRetryAfter", mRetryAfter);
567         pw.printPair("mETag", mETag);
568         pw.printPair("mIsPublicApi", mIsPublicApi);
569         pw.println();
570 
571         pw.printPair("mAllowedNetworkTypes", mAllowedNetworkTypes);
572         pw.printPair("mAllowRoaming", mAllowRoaming);
573         pw.printPair("mAllowMetered", mAllowMetered);
574         pw.println();
575 
576         pw.decreaseIndent();
577     }
578 
579     /**
580      * Return time when this download will be ready for its next action, in
581      * milliseconds after given time.
582      *
583      * @return If {@code 0}, download is ready to proceed immediately. If
584      *         {@link Long#MAX_VALUE}, then download has no future actions.
585      */
nextActionMillis(long now)586     public long nextActionMillis(long now) {
587         if (Downloads.Impl.isStatusCompleted(mStatus)) {
588             return Long.MAX_VALUE;
589         }
590         if (mStatus != Downloads.Impl.STATUS_WAITING_TO_RETRY) {
591             return 0;
592         }
593         long when = restartTime(now);
594         if (when <= now) {
595             return 0;
596         }
597         return when - now;
598     }
599 
600     /**
601      * Returns whether a file should be scanned
602      */
shouldScanFile()603     public boolean shouldScanFile() {
604         return (mMediaScanned == 0)
605                 && (mDestination == Downloads.Impl.DESTINATION_EXTERNAL ||
606                         mDestination == Downloads.Impl.DESTINATION_FILE_URI ||
607                         mDestination == Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD)
608                 && Downloads.Impl.isStatusSuccess(mStatus);
609     }
610 
notifyPauseDueToSize(boolean isWifiRequired)611     void notifyPauseDueToSize(boolean isWifiRequired) {
612         Intent intent = new Intent(Intent.ACTION_VIEW);
613         intent.setData(getAllDownloadsUri());
614         intent.setClassName(SizeLimitActivity.class.getPackage().getName(),
615                 SizeLimitActivity.class.getName());
616         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
617         intent.putExtra(EXTRA_IS_WIFI_REQUIRED, isWifiRequired);
618         mContext.startActivity(intent);
619     }
620 
621     /**
622      * Query and return status of requested download.
623      */
queryDownloadStatus(ContentResolver resolver, long id)624     public static int queryDownloadStatus(ContentResolver resolver, long id) {
625         final Cursor cursor = resolver.query(
626                 ContentUris.withAppendedId(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, id),
627                 new String[] { Downloads.Impl.COLUMN_STATUS }, null, null, null);
628         try {
629             if (cursor.moveToFirst()) {
630                 return cursor.getInt(0);
631             } else {
632                 // TODO: increase strictness of value returned for unknown
633                 // downloads; this is safe default for now.
634                 return Downloads.Impl.STATUS_PENDING;
635             }
636         } finally {
637             cursor.close();
638         }
639     }
640 }
641