From 071bd7acb3185f4f1e807855605c5e6018e9742f Mon Sep 17 00:00:00 2001 From: Steve Howard Date: Thu, 15 Jul 2010 19:59:40 -0700 Subject: Support for max download size that may go over mobile This change introduces support for a maximum download size that may go over a mobile connection. Downloads above this limit will wait for a wifi connection. To accomplish this, I moved a lot of the logic for checking connectivity info into DownloadInfo itself. I then moved the code to call these checks from DownloadService, where it would call the checks before spawning a DownloadThread, into DownloadThread itself. This makes it simpler to check connectivity after we get Content-Length info. It also eliminates the risk of a race condition where connectivity changes between the check and the actual request execution. I realize this change reduces efficiency, because we now call into ConnectivityManager/TelephonyManager twice per DownloadThread, rather than once per DownloadService "tick". I feel that it's OK since its a small amount of computation running relatively infrequently. If we feel that it's a serious concern, and that the efficiency issues outweigh the race problem, I can go easily back to the old approach. I've left out the code to actually fetch the limit. I think this will come from system settings, but I want to double-check, so I'll put it in a separate change. Other changes: * simplify SystemFacade's interface to get connectivity info - rather than returning all connected types, just return the active type, since that should be all we care about * adding @LargeTest to PublicApiFunctionalTest Change-Id: Id1faa2c45bf2dade9fe779440721a1d42cbdfcd1 --- .../android/providers/downloads/DownloadInfo.java | 79 ++++++++++++++++++---- 1 file changed, 66 insertions(+), 13 deletions(-) (limited to 'src/com/android/providers/downloads/DownloadInfo.java') diff --git a/src/com/android/providers/downloads/DownloadInfo.java b/src/com/android/providers/downloads/DownloadInfo.java index 1ae10ce1..5cd50c92 100644 --- a/src/com/android/providers/downloads/DownloadInfo.java +++ b/src/com/android/providers/downloads/DownloadInfo.java @@ -16,12 +16,16 @@ package com.android.providers.downloads; -import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.database.Cursor; +import android.net.ConnectivityManager; import android.net.Uri; import android.provider.Downloads; +import android.provider.Downloads.Impl; +import android.util.Log; import java.util.Collections; import java.util.HashMap; @@ -59,9 +63,15 @@ public class DownloadInfo { public int mFuzz; public volatile boolean mHasActiveThread; + private Map mRequestHeaders = new HashMap(); + private SystemFacade mSystemFacade; + private Context mContext; + + public DownloadInfo(Context context, SystemFacade systemFacade, Cursor cursor) { + mContext = context; + mSystemFacade = systemFacade; - public DownloadInfo(ContentResolver resolver, Cursor cursor) { int retryRedirect = cursor.getInt(cursor.getColumnIndexOrThrow(Constants.RETRY_AFTER_X_REDIRECT_COUNT)); mId = cursor.getInt(cursor.getColumnIndexOrThrow(Downloads.Impl._ID)); @@ -101,14 +111,14 @@ public class DownloadInfo { mMediaScanned = cursor.getInt(cursor.getColumnIndexOrThrow(Constants.MEDIA_SCANNED)) == 1; mFuzz = Helpers.sRandom.nextInt(1001); - readRequestHeaders(resolver, mId); + readRequestHeaders(mId); } - private void readRequestHeaders(ContentResolver resolver, long downloadId) { + private void readRequestHeaders(long downloadId) { Uri headerUri = Downloads.Impl.CONTENT_URI.buildUpon() .appendPath(Long.toString(downloadId)) .appendPath(Downloads.Impl.RequestHeaders.URI_SEGMENT).build(); - Cursor cursor = resolver.query(headerUri, null, null, null, null); + Cursor cursor = mContext.getContentResolver().query(headerUri, null, null, null, null); try { int headerIndex = cursor.getColumnIndexOrThrow(Downloads.Impl.RequestHeaders.COLUMN_HEADER); @@ -133,7 +143,7 @@ public class DownloadInfo { return Collections.unmodifiableMap(mRequestHeaders); } - public void sendIntentIfRequested(Uri contentUri, Context context) { + public void sendIntentIfRequested(Uri contentUri) { if (mPackage != null && mClass != null) { Intent intent = new Intent(Downloads.Impl.ACTION_DOWNLOAD_COMPLETED); intent.setClassName(mPackage, mClass); @@ -144,7 +154,7 @@ public class DownloadInfo { // applications would have an easier time spoofing download results by // sending spoofed intents. intent.setData(contentUri); - context.sendBroadcast(intent); + mContext.sendBroadcast(intent); } } @@ -247,14 +257,57 @@ public class DownloadInfo { /** * Returns whether this download is allowed to use the network. */ - public boolean canUseNetwork(boolean available, boolean roaming) { - if (!available) { + public boolean canUseNetwork() { + Integer networkType = mSystemFacade.getActiveNetworkType(); + if (networkType == null) { return false; } - if (mDestination == Downloads.Impl.DESTINATION_CACHE_PARTITION_NOROAMING) { - return !roaming; - } else { - return true; + if (!isSizeAllowedForNetwork(networkType)) { + return false; + } + if (mDestination == Downloads.Impl.DESTINATION_CACHE_PARTITION_NOROAMING + && mSystemFacade.isNetworkRoaming()) { + return false; + } + return true; + } + + /** + * Check if the download's size prohibits it from running over the current network. + */ + private boolean isSizeAllowedForNetwork(int networkType) { + if (mTotalBytes <= 0) { + return true; // we don't know the size yet + } + if (networkType == ConnectivityManager.TYPE_WIFI) { + return true; // anything goes over wifi + } + Integer maxBytesOverMobile = mSystemFacade.getMaxBytesOverMobile(); + if (maxBytesOverMobile == null) { + return true; // no limit + } + return mTotalBytes <= maxBytesOverMobile; + } + + void startIfReady(long now) { + if (isReadyToStart(now)) { + if (Constants.LOGV) { + Log.v(Constants.TAG, "Service spawning thread to handle download " + mId); + } + if (mHasActiveThread) { + throw new IllegalStateException("Multiple threads on same download"); + } + if (mStatus != Impl.STATUS_RUNNING) { + mStatus = Impl.STATUS_RUNNING; + ContentValues values = new ContentValues(); + values.put(Impl.COLUMN_STATUS, mStatus); + mContext.getContentResolver().update( + ContentUris.withAppendedId(Impl.CONTENT_URI, mId), + values, null, null); + } + DownloadThread downloader = new DownloadThread(mContext, mSystemFacade, this); + mHasActiveThread = true; + downloader.start(); } } } -- cgit v1.2.3