diff options
author | Vasu Nori <vnori@google.com> | 2011-03-10 11:57:56 -0800 |
---|---|---|
committer | Vasu Nori <vnori@google.com> | 2011-03-10 15:20:25 -0800 |
commit | 6baceb7073ed7858d3424ccf774e5f6a946bec59 (patch) | |
tree | c2b3466f2dee3b0ec9a01dbe649206f0ec48932b | |
parent | 34cebab410fe28edb9eb1bba6df96ceaa0224739 (diff) | |
download | android_packages_providers_DownloadProvider-6baceb7073ed7858d3424ccf774e5f6a946bec59.tar.gz android_packages_providers_DownloadProvider-6baceb7073ed7858d3424ccf774e5f6a946bec59.tar.bz2 android_packages_providers_DownloadProvider-6baceb7073ed7858d3424ccf774e5f6a946bec59.zip |
(master) bug:3204324 allow no more than certain number of concurrent downloads
Change-Id: Ibbce0782fcf7649209d6f56be240209cebd9045b
6 files changed, 137 insertions, 19 deletions
diff --git a/src/com/android/providers/downloads/DownloadHandler.java b/src/com/android/providers/downloads/DownloadHandler.java new file mode 100644 index 00000000..29d34700 --- /dev/null +++ b/src/com/android/providers/downloads/DownloadHandler.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.providers.downloads; + +import android.content.res.Resources; +import android.util.Log; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; + +public class DownloadHandler { + + private static final String TAG = "DownloadHandler"; + private final LinkedHashMap<Long, DownloadInfo> mDownloadsQueue = + new LinkedHashMap<Long, DownloadInfo>(); + private final HashMap<Long, DownloadInfo> mDownloadsInProgress = + new HashMap<Long, DownloadInfo>(); + private static final DownloadHandler mDownloadHandler = new DownloadHandler(); + private final int mMaxConcurrentDownloadsAllowed = Resources.getSystem().getInteger( + com.android.internal.R.integer.config_MaxConcurrentDownloadsAllowed); + + static DownloadHandler getInstance() { + return mDownloadHandler; + } + + synchronized void enqueueDownload(DownloadInfo info) { + if (!mDownloadsQueue.containsKey(info.mId)) { + if (Constants.LOGV) { + Log.i(TAG, "enqueued download. id: " + info.mId + ", uri: " + info.mUri); + } + mDownloadsQueue.put(info.mId, info); + startDownloadThread(); + } + } + + private synchronized void startDownloadThread() { + Iterator<Long> keys = mDownloadsQueue.keySet().iterator(); + ArrayList<Long> ids = new ArrayList<Long>(); + while (mDownloadsInProgress.size() < mMaxConcurrentDownloadsAllowed && keys.hasNext()) { + Long id = keys.next(); + DownloadInfo info = mDownloadsQueue.get(id); + info.startDownloadThread(); + ids.add(id); + mDownloadsInProgress.put(id, mDownloadsQueue.get(id)); + if (Constants.LOGV) { + Log.i(TAG, "started download for : " + id); + } + } + for (Long id : ids) { + mDownloadsQueue.remove(id); + } + } + + synchronized boolean hasDownloadInQueue(long id) { + return mDownloadsQueue.containsKey(id) || mDownloadsInProgress.containsKey(id); + } + + synchronized void dequeueDownload(long mId) { + mDownloadsInProgress.remove(mId); + startDownloadThread(); + if (mDownloadsInProgress.size() == 0 && mDownloadsQueue.size() == 0) { + notifyAll(); + } + } + + // right now this is only used by tests. but there is no reason why it can't be used + // by any module using DownloadManager (TODO add API to DownloadManager.java) + public synchronized void WaitUntilDownloadsTerminate() throws InterruptedException { + if (mDownloadsInProgress.size() == 0 && mDownloadsQueue.size() == 0) { + if (Constants.LOGVV) { + Log.i(TAG, "nothing to wait on"); + } + return; + } + if (Constants.LOGVV) { + for (DownloadInfo info : mDownloadsInProgress.values()) { + Log.i(TAG, "** progress: " + info.mId + ", " + info.mUri); + } + for (DownloadInfo info : mDownloadsQueue.values()) { + Log.i(TAG, "** in Q: " + info.mId + ", " + info.mUri); + } + } + if (Constants.LOGVV) { + Log.i(TAG, "waiting for 5 sec"); + } + // wait upto 5 sec + wait(5 * 1000); + } +} diff --git a/src/com/android/providers/downloads/DownloadInfo.java b/src/com/android/providers/downloads/DownloadInfo.java index 2973937e..bd8df864 100644 --- a/src/com/android/providers/downloads/DownloadInfo.java +++ b/src/com/android/providers/downloads/DownloadInfo.java @@ -216,8 +216,6 @@ public class DownloadInfo { public int mFuzz; - public volatile boolean mHasActiveThread; - private List<Pair<String, String>> mRequestHeaders = new ArrayList<Pair<String, String>>(); private SystemFacade mSystemFacade; private Context mContext; @@ -279,7 +277,7 @@ public class DownloadInfo { * should be started. */ private boolean isReadyToStart(long now) { - if (mHasActiveThread) { + if (DownloadHandler.getInstance().hasDownloadInQueue(mId)) { // already running return false; } @@ -442,19 +440,13 @@ public class DownloadInfo { 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(getAllDownloadsUri(), values, null, null); } - DownloadThread downloader = new DownloadThread(mContext, mSystemFacade, this, - storageManager); - mHasActiveThread = true; - mSystemFacade.startThread(downloader); + DownloadHandler.getInstance().enqueueDownload(this); } public boolean isOnCache() { @@ -543,4 +535,10 @@ public class DownloadInfo { intent.putExtra(EXTRA_IS_WIFI_REQUIRED, isWifiRequired); mContext.startActivity(intent); } + + void startDownloadThread() { + DownloadThread downloader = new DownloadThread(mContext, mSystemFacade, this, + StorageManager.getInstance(mContext)); + mSystemFacade.startThread(downloader); + } } diff --git a/src/com/android/providers/downloads/DownloadService.java b/src/com/android/providers/downloads/DownloadService.java index d4e50f23..035eaff4 100644 --- a/src/com/android/providers/downloads/DownloadService.java +++ b/src/com/android/providers/downloads/DownloadService.java @@ -23,7 +23,6 @@ import android.app.AlarmManager; import android.app.PendingIntent; import android.app.Service; import android.content.ComponentName; -import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.Intent; diff --git a/src/com/android/providers/downloads/DownloadThread.java b/src/com/android/providers/downloads/DownloadThread.java index 100ebcab..77f7fa1f 100644 --- a/src/com/android/providers/downloads/DownloadThread.java +++ b/src/com/android/providers/downloads/DownloadThread.java @@ -171,16 +171,18 @@ public class DownloadThread extends Thread { finalStatus = Downloads.Impl.STATUS_SUCCESS; } catch (StopRequestException error) { // remove the cause before printing, in case it contains PII - errorMsg = "Aborting request for download " + mInfo.mId + ": " + error.getMessage(); - Log.w(Constants.TAG, errorMsg); + errorMsg = error.getMessage(); + String msg = "Aborting request for download " + mInfo.mId + ": " + errorMsg; + Log.w(Constants.TAG, msg); if (Constants.LOGV) { - Log.w(Constants.TAG, errorMsg, error); + Log.w(Constants.TAG, msg, error); } finalStatus = error.mFinalStatus; // fall through to finally block } catch (Throwable ex) { //sometimes the socket code throws unchecked exceptions - errorMsg = "Exception for id " + mInfo.mId + ": " + ex.getMessage(); - Log.w(Constants.TAG, errorMsg, ex); + errorMsg = ex.getMessage(); + String msg = "Exception for id " + mInfo.mId + ": " + errorMsg; + Log.w(Constants.TAG, msg, ex); finalStatus = Downloads.Impl.STATUS_UNKNOWN_ERROR; // falls through to the code that reports an error } finally { @@ -196,7 +198,7 @@ public class DownloadThread extends Thread { notifyDownloadCompleted(finalStatus, state.mCountRetry, state.mRetryAfter, state.mGotData, state.mFilename, state.mNewUri, state.mMimeType, errorMsg); - mInfo.mHasActiveThread = false; + DownloadHandler.getInstance().dequeueDownload(mInfo.mId); } mStorageManager.incrementNumDownloadsSoFar(); } diff --git a/tests/src/com/android/providers/downloads/AbstractDownloadManagerFunctionalTest.java b/tests/src/com/android/providers/downloads/AbstractDownloadManagerFunctionalTest.java index 5283d425..d2ecf3e6 100644 --- a/tests/src/com/android/providers/downloads/AbstractDownloadManagerFunctionalTest.java +++ b/tests/src/com/android/providers/downloads/AbstractDownloadManagerFunctionalTest.java @@ -155,13 +155,12 @@ public abstract class AbstractDownloadManagerFunctionalTest extends Context realContext = getContext(); mTestContext = new TestContext(realContext); setupProviderAndResolver(); - assert isDatabaseEmpty(); // ensure we're not messing with real data mTestContext.setResolver(mResolver); setContext(mTestContext); setupService(); getService().mSystemFacade = mSystemFacade; - + assertTrue(isDatabaseEmpty()); // ensure we're not messing with real data mServer = new MockWebServer(); mServer.play(); } diff --git a/tests/src/com/android/providers/downloads/DownloadManagerFunctionalTest.java b/tests/src/com/android/providers/downloads/DownloadManagerFunctionalTest.java index c3ac8904..e01b617f 100644 --- a/tests/src/com/android/providers/downloads/DownloadManagerFunctionalTest.java +++ b/tests/src/com/android/providers/downloads/DownloadManagerFunctionalTest.java @@ -23,6 +23,8 @@ import android.net.Uri; import android.os.Environment; import android.provider.Downloads; import android.test.suitebuilder.annotation.LargeTest; +import android.util.Log; + import tests.http.MockWebServer; import tests.http.RecordedRequest; @@ -37,6 +39,8 @@ import java.net.MalformedURLException; */ @LargeTest public class DownloadManagerFunctionalTest extends AbstractDownloadManagerFunctionalTest { + private static final String TAG = "DownloadManagerFunctionalTest"; + public DownloadManagerFunctionalTest() { super(new FakeSystemFacade()); } @@ -104,6 +108,17 @@ public class DownloadManagerFunctionalTest extends AbstractDownloadManagerFuncti private void runUntilStatus(Uri downloadUri, int status) throws Exception { runService(); + boolean done = false; + while (!done) { + int rslt = getDownloadStatus(downloadUri); + if (rslt == Downloads.Impl.STATUS_RUNNING || rslt == Downloads.Impl.STATUS_PENDING) { + Log.i(TAG, "status is: " + rslt + ", for: " + downloadUri); + DownloadHandler.getInstance().WaitUntilDownloadsTerminate(); + Thread.sleep(100); + } else { + done = true; + } + } assertEquals(status, getDownloadStatus(downloadUri)); } |