diff options
Diffstat (limited to 'src')
4 files changed, 121 insertions, 17 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 735946c9..fbd3b82c 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(); } |